Skip to content

Add function-name completion #16

Description

@Seddryck

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

@foo | text-to-

The cursor is after a pipeline operator and the grammar expects the next transformation/expression.

Beginning of an expression

text-to-

Completion should also work when the expression starts directly with a function.

Empty function position

@foo | 

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

  • The language server advertises and handles LSP completion requests.
  • Completion works when a function is expected after a pipeline operator.
  • Completion works at the beginning of an expression.
  • An empty function position can return the available function names.
  • A typed prefix filters the returned functions.
  • Completion does not trigger solely from matching text inside literals or other clearly unrelated syntax positions.
  • Completion items use canonical Expressif function names and an appropriate function completion kind.
  • Results are deterministic.
  • Expressif.LanguageServer has a direct dependency on Expressif itself for authoritative function metadata, in addition to Expressif.Syntax for parsing.
  • Function metadata comes from Expressif introspection/shared metadata rather than from a handler-local hard-coded list or independent reflection scan in the language server.
  • The function catalog is built once during server initialization and reused from memory for subsequent requests.
  • The metadata abstraction can later be reused by hover and signature help.
  • Tests cover pipeline completion, standalone completion, prefix filtering, empty-prefix completion, and at least one non-function context.
  • The feature remains editor-agnostic and works through standard LSP clients, including the thin VS Code client from Add VS Code extension as thin LSP client #13.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    new-featureRequest for a new feature in the tool

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions