Skip to content

Initialize result slices as empty arrays so JSON output is [] not null#12

Open
delphinus wants to merge 2 commits into
netmute:mainfrom
delphinus:fix/empty-arrays
Open

Initialize result slices as empty arrays so JSON output is [] not null#12
delphinus wants to merge 2 commits into
netmute:mainfrom
delphinus:fix/empty-arrays

Conversation

@delphinus

@delphinus delphinus commented Apr 24, 2026

Copy link
Copy Markdown

Three handlers declare their result slices with var ... []T, which leaves
them as nil if the loop body never appends. When marshalled to JSON, a nil
Go slice becomes null, not [].

For textDocument/completion, this is a spec violation. CompletionList.items is typed as CompletionItem[], non-nullable. Strict LSP clients can choke on {"isIncomplete": false, "items": null}. I hit this in blink.cmp.

For workspace/symbol and textDocument/documentSymbol, top-level null is technically valid LSP, but [] is the more common convention. The same one-line change keeps the three handlers consistent.

Diff

- var items   []CompletionItem
+ items   := []CompletionItem{}

- var symbols []SymbolInformation   // workspace/symbol
+ symbols := []SymbolInformation{}

- var symbols []SymbolInformation   // documentSymbol
+ symbols := []SymbolInformation{}

No behavior change in Go (len, range, append all behave identically;
no nil comparisons in the codebase). Tiny extra heap allocation per
request (~24 bytes for a slice header). Existing tests pass.

delphinus and others added 2 commits April 24, 2026 17:34
…null`

Three handlers declare their result slices with `var ... []T`, which
leaves them as nil if the loop body never appends. When marshalled to
JSON, a nil Go slice becomes `null`, not `[]`.

For textDocument/completion, this is a spec violation:
CompletionList.items is typed as CompletionItem[], non-nullable. Strict
LSP clients can choke on `{"isIncomplete": false, "items": null}`.

For workspace/symbol and textDocument/documentSymbol, top-level null is
technically valid LSP, but `[]` is the more common convention. The same
one-line change keeps the three handlers consistent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Asserts that the JSON-encoded textDocument/completion response contains
`"items":[]` (and never `"items":null`) when the tag table has no
matches for the current word, so the spec violation cannot regress.

The test enters the main path of handleCompletion (cursor inside a
word) rather than the early-return branch that already used an empty
literal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
delphinus added a commit to delphinus/dotfiles that referenced this pull request May 4, 2026
ctags-lsp returns `{ items = vim.NIL }` (JSON null) when there are no
candidates instead of the spec-conformant `{ items = {} }`. blink.cmp's
LSP source treats vim.NIL as truthy and then `ipairs(vim.NIL)` errors,
which propagates through async.task.all and discards completion
responses from ALL attached LSP clients (so a `vim.` query in Lua would
silently lose lua_ls output too).

Wrap client.request via on_init so the broken response is normalized
before reaching blink.cmp. Drop this workaround after
netmute/ctags-lsp#12 is merged and rolled out.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant