Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

triedemo.gno

3.95 Kb · 125 lines
  1// Package triedemo is a small gnoweb demo of the prefix tree provided by the
  2// [p/moul/x/daily/trie](/p/moul/x/daily/trie/v0) library: type a prefix in the
  3// URL and it lists every dictionary word that completes it.
  4//
  5// It contains no trie logic of its own — the tree, the ordering and the limit
  6// all come from the library. The dictionary is a fixed, sorted word list so the
  7// realm has no mutable state and Render is fully deterministic.
  8package triedemo
  9
 10import (
 11	"strconv"
 12	"strings"
 13
 14	"gno.land/p/moul/x/daily/trie/v0"
 15)
 16
 17// maxResults caps how many completions a single Render lists.
 18const maxResults = 25
 19
 20// dictionary is the demo corpus: gno/Go vocabulary, deliberately clustered on
 21// a few prefixes ("co", "gn", "re", "tr") so completions are fun to explore.
 22var dictionary = []string{
 23	"chain", "coin", "collection", "commit", "compile", "complete", "concurrent",
 24	"consensus", "constant", "contract", "counter", "crossing", "deploy", "gas",
 25	"genesis", "gno", "gnodev", "gnokey", "gnoland", "gnoweb", "goroutine",
 26	"grc20", "hash", "interface", "keeper", "ledger", "mempool", "merkle",
 27	"module", "namespace", "package", "panic", "pointer", "prefix", "prove",
 28	"realm", "receiver", "recover", "reflect", "render", "replay", "rollback",
 29	"slice", "stdlib", "struct", "transaction", "transfer", "traverse", "tree",
 30	"trie", "type", "validator", "vault", "vm", "wallet",
 31}
 32
 33// dict is built once at init; the library keeps it sorted internally.
 34var dict = trie.FromWords(dictionary)
 35
 36// Render renders the autocomplete for gnoweb.
 37//
 38//	Render("")          / Render("/") -> the whole dictionary + usage
 39//	Render("/<prefix>")               -> every word completing <prefix>
 40func Render(path string) string {
 41	prefix := parsePrefix(path)
 42
 43	var b strings.Builder
 44	b.WriteString("# Trie Autocomplete\n\n")
 45	b.WriteString("A prefix tree over a ")
 46	b.WriteString(strconv.Itoa(dict.Len()))
 47	b.WriteString("-word dictionary, demoing the ")
 48	b.WriteString("[`p/moul/x/daily/trie`](/p/moul/x/daily/trie/v0) library.\n\n")
 49
 50	if prefix == "" {
 51		b.WriteString("Append a prefix to the path to complete it.\n\n")
 52		b.WriteString("## Try one\n\n")
 53		// Listing all 55 words here would bury the page (and the example test
 54		// that pins it); the interesting part is the prefix walk, so show a few
 55		// live counts and let /<prefix> do the listing.
 56		for _, p := range []string{"co", "gno", "re", "tr"} {
 57			b.WriteString("- [`/")
 58			b.WriteString(p)
 59			b.WriteString("`](/r/moul/x/daily/triedemo/v0:")
 60			b.WriteString(p)
 61			b.WriteString(") — ")
 62			b.WriteString(strconv.Itoa(len(dict.Complete(p, 0))))
 63			b.WriteString(" words\n")
 64		}
 65		return b.String()
 66	}
 67
 68	b.WriteString("## Completions for `")
 69	b.WriteString(prefix)
 70	b.WriteString("`\n\n")
 71
 72	if !dict.HasPrefix(prefix) {
 73		b.WriteString("_No word starts with `")
 74		b.WriteString(prefix)
 75		b.WriteString("`._\n\n> Try `/co`, `/gno`, `/tr` or `/re`.\n")
 76		return b.String()
 77	}
 78
 79	words := dict.Complete(prefix, maxResults)
 80	b.WriteString("**")
 81	b.WriteString(strconv.Itoa(len(words)))
 82	b.WriteString("** match")
 83	if len(words) != 1 {
 84		b.WriteString("es")
 85	}
 86	if len(words) == maxResults {
 87		b.WriteString(" (capped at ")
 88		b.WriteString(strconv.Itoa(maxResults))
 89		b.WriteString(")")
 90	}
 91	b.WriteString("\n\n")
 92	b.WriteString(list(words))
 93
 94	if dict.Contains(prefix) {
 95		b.WriteString("\n> `")
 96		b.WriteString(prefix)
 97		b.WriteString("` is itself a word in the dictionary.\n")
 98	}
 99	return b.String()
100}
101
102// parsePrefix extracts the prefix from a Render path like "/gno", keeping only
103// the first segment. Returns "" for the empty/root path.
104func parsePrefix(path string) string {
105	s := strings.TrimSpace(path)
106	s = strings.TrimPrefix(s, "/")
107	if i := strings.IndexByte(s, '/'); i >= 0 {
108		s = s[:i]
109	}
110	return s
111}
112
113// list renders words as a Markdown bullet list.
114func list(words []string) string {
115	if len(words) == 0 {
116		return "_none_\n"
117	}
118	var b strings.Builder
119	for _, w := range words {
120		b.WriteString("- `")
121		b.WriteString(w)
122		b.WriteString("`\n")
123	}
124	return b.String()
125}