Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}