Goal
Add LSP completion for Expressif function names so that users can discover and insert available functions directly while writing expressions.
This is the first genuinely interactive feature in the initial LSP roadmap. It should provide immediate value without requiring full type inference or a complete semantic model.
Expected behavior
When the cursor is at a position where Expressif syntax can accept a function/expression, the server should offer matching known functions.
For example:
@foo | text-to-
↓
text-to-lower
text-to-upper
text-to-title
...
The completion list should be driven by Expressif's known public functions rather than by a hard-coded list in the handler.
Scope
Implement an LSP completion handler that:
- determines whether the cursor is in a syntactic context where a function/expression may be entered;
- extracts the currently typed function-name prefix when applicable;
- returns known Expressif functions that match that prefix;
- exposes the canonical function name as the completion label;
- uses an appropriate LSP completion kind for functions;
- inserts a useful function representation without trying to guess parameter values;
- behaves sensibly both after a pipeline operator and when starting a standalone expression;
- does not return function completion in clearly unrelated contexts such as inside a literal value.
This first version should favor predictable syntax-aware completion over sophisticated ranking.
Shared function metadata and catalog source
Completion should consume a small reusable server-side abstraction for function metadata rather than embedding function knowledge directly in CompletionHandler.
Expressif itself must remain the authoritative source of the available functions and their signatures. The language server should obtain this information through Expressif's existing introspection/metadata capabilities rather than maintaining its own list or independently rediscovering functions through reflection.
This means Expressif.LanguageServer should add a direct dependency on the Expressif assembly/package itself, in addition to its dependency on Expressif.Syntax. Expressif.Syntax remains responsible for parsing and identifying the syntactic context at the cursor, while Expressif provides the runtime function catalog used by completion and, later, other semantic LSP features.
Conceptually:
Expressif.Syntax
│
└── parsed syntax / node at position
Expressif
│
└── introspection / function metadata
│
▼
LanguageServer FunctionCatalog
│
├── names
├── parameters
├── descriptions
└── categories
│
▼
CompletionHandler
If the current Expressif introspection implementation mixes discovery with presentation/formatting, the reusable metadata/discovery part should be exposed cleanly enough for the language server to consume it without depending on presentation concerns. Introspection and the language server should ideally consume the same underlying catalog rather than the language server depending on formatted introspection output.
For this issue, completion primarily needs the function names. However, the catalog should be designed so the same source of truth can later support hover and signature help without creating parallel metadata models.
Catalog lifecycle
The catalog should be loaded once during language-server initialization and retained in memory for the lifetime of the server process. Completion requests should query this in-memory catalog rather than invoking Expressif introspection or scanning assemblies for every request.
A normal coding session therefore follows this lifecycle:
server startup
↓
load function metadata from Expressif
↓
build FunctionCatalog
↓
retain catalog in memory
↓
completion / hover / signature-help requests reuse it
↓
server shutdown
Dynamic catalog refresh is not required for this first version. A changed Expressif version is picked up when the language server process is restarted with the updated dependency.
Completion context
The handler should at least cover these cases:
Pipeline position
The cursor is after a pipeline operator and the grammar expects the next transformation/expression.
Beginning of an expression
Completion should also work when the expression starts directly with a function.
Empty function position
The server may offer all available functions when there is no prefix yet.
Non-function context
@foo | suffix("text-to-")
The server should not offer function-name completion merely because the characters resemble a function prefix inside a literal.
The implementation does not need perfect semantic knowledge for every grammar production in the first version, but context detection should be based on parsed syntax / parser information rather than broad string matching over the document.
Matching and ordering
Initial matching can be simple:
- prefix matching is sufficient;
- matching should follow Expressif's normal function-name casing rules;
- deterministic ordering is required;
- canonical names should be preferred over aliases unless there is a deliberate reason to expose aliases too.
If aliases are included, the behavior should be documented and covered by tests.
Insert behavior
The completion item should make it easy to continue writing the function.
At minimum, selecting a completion should insert the function name. If the implementation can reliably distinguish parameterless and parameterized functions from the shared metadata, it may also insert parentheses/snippets, but snippet-style argument placeholders are not required for this first feature.
Signature help should remain responsible for guiding the user through parameters later.
Acceptance criteria
Out of scope
- Type-aware filtering of functions based on the pipeline input type.
- Ranking based on usage frequency or semantic relevance.
- Parameter-value completion.
- Signature help.
- Hover documentation.
- Completion for variables, fields, literals, or other Expressif syntax categories.
- Dynamic catalog refresh while the server is running.
- VS Code-specific completion implementation.
Goal
Add LSP completion for Expressif function names so that users can discover and insert available functions directly while writing expressions.
This is the first genuinely interactive feature in the initial LSP roadmap. It should provide immediate value without requiring full type inference or a complete semantic model.
Expected behavior
When the cursor is at a position where Expressif syntax can accept a function/expression, the server should offer matching known functions.
For example:
The completion list should be driven by Expressif's known public functions rather than by a hard-coded list in the handler.
Scope
Implement an LSP completion handler that:
This first version should favor predictable syntax-aware completion over sophisticated ranking.
Shared function metadata and catalog source
Completion should consume a small reusable server-side abstraction for function metadata rather than embedding function knowledge directly in
CompletionHandler.Expressif itself must remain the authoritative source of the available functions and their signatures. The language server should obtain this information through Expressif's existing introspection/metadata capabilities rather than maintaining its own list or independently rediscovering functions through reflection.
This means
Expressif.LanguageServershould add a direct dependency on the Expressif assembly/package itself, in addition to its dependency onExpressif.Syntax.Expressif.Syntaxremains responsible for parsing and identifying the syntactic context at the cursor, while Expressif provides the runtime function catalog used by completion and, later, other semantic LSP features.Conceptually:
If the current Expressif introspection implementation mixes discovery with presentation/formatting, the reusable metadata/discovery part should be exposed cleanly enough for the language server to consume it without depending on presentation concerns. Introspection and the language server should ideally consume the same underlying catalog rather than the language server depending on formatted introspection output.
For this issue, completion primarily needs the function names. However, the catalog should be designed so the same source of truth can later support hover and signature help without creating parallel metadata models.
Catalog lifecycle
The catalog should be loaded once during language-server initialization and retained in memory for the lifetime of the server process. Completion requests should query this in-memory catalog rather than invoking Expressif introspection or scanning assemblies for every request.
A normal coding session therefore follows this lifecycle:
Dynamic catalog refresh is not required for this first version. A changed Expressif version is picked up when the language server process is restarted with the updated dependency.
Completion context
The handler should at least cover these cases:
Pipeline position
The cursor is after a pipeline operator and the grammar expects the next transformation/expression.
Beginning of an expression
Completion should also work when the expression starts directly with a function.
Empty function position
The server may offer all available functions when there is no prefix yet.
Non-function context
The server should not offer function-name completion merely because the characters resemble a function prefix inside a literal.
The implementation does not need perfect semantic knowledge for every grammar production in the first version, but context detection should be based on parsed syntax / parser information rather than broad string matching over the document.
Matching and ordering
Initial matching can be simple:
If aliases are included, the behavior should be documented and covered by tests.
Insert behavior
The completion item should make it easy to continue writing the function.
At minimum, selecting a completion should insert the function name. If the implementation can reliably distinguish parameterless and parameterized functions from the shared metadata, it may also insert parentheses/snippets, but snippet-style argument placeholders are not required for this first feature.
Signature help should remain responsible for guiding the user through parameters later.
Acceptance criteria
Expressif.LanguageServerhas a direct dependency on Expressif itself for authoritative function metadata, in addition toExpressif.Syntaxfor parsing.Out of scope