Commitf97e0b86Recorded17 Apr 2026Repositorysigil-docs

sigil-docs: remove stale local keyword->string shadows

Message

(sigil docs search) and (sigil docs index) each defined a local keyword->string that did (symbol->string kw) and stripped the trailing colon. That treated keywords as symbols ending in ':', but modern Sigil keywords are their own type -- (symbol->string kw) signals "symbol->string: expected symbol" immediately. The shadow shadowed the native (sigil core) keyword->string imported via the module's import list, so every search-docs call errored on the first key.

Because the iteration was inside a silent filter-map, errors were swallowed and callers just saw empty results -- hiding the bug for as long as the docs JSON files were empty (they always were, see the preceding compiler.c fix). Now that the JSON has real content and search-docs actually runs the filter-map body, the shadow surfaced.

Deleting both local definitions makes keyword->string resolve to the (sigil core) native, which handles Sigil keywords correctly.

After this: (search-docs "json-encode") returns 1 hit (json-encode in (sigil json)), unblocking hover/completion/ suggest-import in sigil-lsp and sigil-mcp.

Changed
 src/sigil/docs/index.sgl  |  8 --------
 src/sigil/docs/search.sgl | 49 ++++++++++++++++++++++++++++---------------------
 2 files changed, 28 insertions(+), 29 deletions(-)
Diff
src/sigil/docs/index.sglmodified
@@ -31,14 +31,6 @@
31
32
(begin
33
34
;; Convert keyword to string (remove trailing colon)
35
(define (keyword->string kw)
36
(let ((s (symbol->string kw)))
37
(if (and (> (string-length s) 0)
38
(char=? (string-ref s (- (string-length s) 1)) #\:))
39
(substring s 0 (- (string-length s) 1))
40
s)))
41
34
;; Extract first line of a string for summary
35
(define (first-line str)
36
(if (or (not str) (not (string? str)))
src/sigil/docs/search.sglmodified
@@ -114,25 +114,40 @@
114
(matches? word str)))
115
words)))
116
+117
;; Coerce a dict key to its string form. The symbol index is keyed by
+118
;; keywords, but we accept strings/symbols defensively too.
+119
(define (key->string k)
+120
(cond
+121
((keyword? k) (keyword->string k))
+122
((symbol? k) (symbol->string k))
+123
((string? k) k)
+124
(else #f)))
+125
126
;; Search symbols in the index
127
(define (search-symbols index query)
128
(let ((symbols (dict-ref index symbols: #{})))
129
(filter-map
121
(lambda (key)
122
(let* ((info (dict-ref symbols key #f))
123
(name (keyword->string key))
124
(module (dict-ref info module: ""))
+130
(lambda (key) (search-symbols-one symbols key query))
+131
(dict-keys symbols))))
+132
+133
(define (search-symbols-one symbols key query)
+134
(guard (e (else #f))
+135
(let ((info (dict-ref symbols key #f))
+136
(name (key->string key)))
+137
(cond
+138
((or (not name) (not info)) #f)
+139
(else
+140
(let ((module (dict-ref info module: ""))
141
(kind (dict-ref info kind: "unknown"))
142
(summary (dict-ref info summary: "")))
127
;; Match on name or summary
128
(if (or (any-word-matches? query name)
129
(any-word-matches? query summary))
130
(search-result name: name
131
module: module
132
kind: kind
133
summary: summary)
134
#f)))
135
(dict-keys symbols))))
+143
(if (or (any-word-matches? query name)
+144
(and (string? summary)
+145
(any-word-matches? query summary)))
+146
(search-result name: name
+147
module: (if (string? module) module "")
+148
kind: (if (string? kind) kind "unknown")
+149
summary: (if (string? summary) summary ""))
+150
#f)))))))
151
152
;; Filter results by module if specified
153
(define (filter-by-module results module-filter)
@@ -266,12 +281,4 @@
281
((pred (car lst)) #t)
282
(else (any pred (cdr lst)))))
283
269
;; Convert keyword to string (remove trailing colon)
270
(define (keyword->string kw)
271
(let ((s (symbol->string kw)))
272
(if (and (> (string-length s) 0)
273
(char=? (string-ref s (- (string-length s) 1)) #\:))
274
(substring s 0 (- (string-length s) 1))
275
s)))
276
284
))