feat(langservice): function-completion items insert with arg-placeholder snippets#45
Draft
joewiz wants to merge 5 commits into
Draft
feat(langservice): function-completion items insert with arg-placeholder snippets#45joewiz wants to merge 5 commits into
joewiz wants to merge 5 commits into
Conversation
…layer 1) When the cursor is at `prefix:` or `prefix:partial`, only that namespace's functions are returned (and the local-name is prefix-matched case-insensitively when present). Keywords are dropped from prefixed responses since `util:return`/`util:let` can't exist. Bare/empty cursors get the full set unchanged — current behaviour for that case. Cuts the typical wire payload from ~930 items to ~10–100 for any prefixed cursor (e.g. `util:` → 104, `fn:cou` → 1). User-declared symbols (functions + global variables) get the same scoping. Variables are never offered in prefixed mode. Cypress: 5 new tests covering prefixed scoping, local-name match, keyword suppression, full-set fallback, and multi-line trailing-token detection. Refs eXist-db#31
layer 2) Every completion item now carries three additional fields the LSP contract expects: - filterText — what the client matches typed input against. For functions, this is the local-name only, so bare `cou` matches `fn:count` (which would not match if the client filtered against the full label `fn:count#1`). - sortText — bucketed by namespace via a small table; `fn:*`, keywords, user-declared fns and vars all bucket to "0_…" so an unprefixed `cou` surfaces `fn:count` above `util:count*`, `range:count*`, etc. - insertTextFormat — LSP 1 (PlainText) on every existing item; the field is in place for layer 3 (snippets, format 2). In addition, bare-mode `fn:*` items now drop the `fn:` prefix from `insertText`, so accepting a completion for `cou` yields `count(...)` rather than `fn:count(...)` — preserves the user's unprefixed style. Prefixed-mode (`fn:cou`) keeps the prefix the user already typed. Other namespaces always keep their prefix in insertText since the function literally can't be called without it. Cypress: 5 new tests covering field presence, bare-fn prefix drop, prefixed-fn prefix retention, non-fn prefix retention, and sortText bucketing. Refs eXist-db#31
Adds 7 snippet items (FLWOR for/let, if/then/else, try/catch,
typeswitch, declare function, import module) that expand tab-stop
placeholders when accepted in clients that honor LSP
\`insertTextFormat: 2\`. Clients that don't fall back to plain-text
insertion per the LSP spec.
Snippets are only emitted in bare/empty cursor mode — never after
\`prefix:\` since \`util:for\` etc. can't exist. They bucket to
sortText "0_<trigger>" so typing "fo" or "tr" surfaces the snippet
alongside matching keywords and fn:* functions.
The \`for\` snippet body, for example, expands to:
for $x in expr
return $x
with the user's tab stops landing on \`x\` → \`expr\` → \`$x\` (the
return clause, defaulting to a back-reference).
Cypress: 2 new tests covering snippet emission in bare mode and
suppression in prefixed mode.
Refs eXist-db#31
) Documents what the server does for completions scoping, sortText biasing, insertText shaping, and snippets — and what client implementers are expected to do on top of it (cache per trigger session, filter against filterText not label, trust sortText, honor insertTextFormat: 2). Includes a practical sequencing section noting that scoping makes interactive use one round-trip per context change, not per keystroke. Refs eXist-db#31
…der snippets
Accepting a function completion now inserts a snippet body with one tab
stop per parameter, defaulting to the parameter's declared XQuery name:
cou → count(${1:\$items}) insertTextFormat: 2
fn:tr → fn:trace(${1:\$value}) insertTextFormat: 2
util:log → util:log(${1:\$priority}, ${2:\$message})
fn:tr → fn:true() insertTextFormat: 1 (zero-arity stays plain)
Matches the eXide F1 Function Documentation panel's "Template" line, but
delivered through the LSP-standard CompletionItem.insertText +
insertTextFormat mechanism so any LSP-aware client gets it. Clients
without snippet support fall back to plain-text insertion per the LSP
spec (the placeholders render as visible text, slightly noisy but
correct).
Implementation: extracted formatInsertText(prefix, sig) that walks
sig.getArgumentTypes(), downcasting each to FunctionParameterSequenceType
to get the declared param name (e.g. "items" for fn:count's $items).
Falls back to "arg1"/"arg2"/... if a parameter has no declared name.
Zero-arity functions get no placeholders and the existing plain
insertTextFormat (1). Build-time check: insertFormatFor(sig) picks the
right format per sig.
Cypress: existing prefix-drop / prefix-keep / non-fn tests updated to
assert the new snippet body; one new test pins zero-arity to plain
format.
Refs eXist-db#31's Layer 3 (snippets in keyword-trigger items); this is the
analogous treatment for function items themselves.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[This PR was co-authored with Claude Code. -Joe]
Draft — stacked on #42 (completions scoping + LSP-shaped output). The diff currently shows #42's commits too; once #42 merges into develop, this branch will be rebased and the diff will shrink to just the one commit added here. Please don't review until #42 is merged.
The third item from the oxygen-plugin team's recommendations on #44: when a user picks a function from completions, drop them into tab stops for each argument instead of a bare
name(). Matches the "Template:" line in eXide's F1 Function Documentation panel, delivered through the LSP-standardCompletionItem.insertText+insertTextFormatchannel so every LSP-aware client gets it.Behaviour
cou(bare)fn:count#1count(${1:\$items})fn:coufn:count#1fn:count(${1:\$items})util:lutil:log#2util:log(${1:\$priority}, ${2:\$message})fn:trfn:true#0fn:true()Tab stops default to the parameter's declared XQuery name (e.g.
$itemsforfn:count's parameter), so accepting the completion drops the user at the first$itemshighlighted as the default — they can type to replace, or Tab to move on. Multi-parameter functions cycle through each placeholder in order.LSP clients without snippet support fall back to inserting
insertTextverbatim per spec — the placeholders render as visible text (slightly noisy but correct).Implementation
formatInsertText(prefix, sig)walkssig.getArgumentTypes(), downcasting each toFunctionParameterSequenceTypeto read the declared name (XQuery built-ins register these viaFunctionDSL.param(…, "$name", …)). Falls back toarg1/arg2/… when no declared name is available. Zero-arity functions get no placeholders and the existing plain format —insertFormatFor(sig)returns SNIPPET (2) only when arity > 0.Same treatment for user-declared functions: their argument names come through the same
FunctionParameterSequenceTypeAPI on the user function's signature.Tests
bare-mode fn:* drops the prefix from insertText (snippet form)— updated to assert the new snippet body forfn:countprefixed-mode fn:* keeps the prefix the user typed (snippet form)— updated for prefixed shapenon-fn namespaces always keep their prefix in insertText— updated to check the snippet patternzero-arity functions stay plain (no snippet placeholders)— new; pinsfn:true#0to plain format=1All 22 langservice cypress tests pass; PMD clean.
Closes the oxygen-plugin recommendation list
This completes items 1–3 from the oxygen-plugin team's PR #44 feedback:
f94367e.