Summary
Add code completion, find-all-references, and formatting integration to the Winn LSP server. This is the final phase that makes the IDE experience production-grade.
Prerequisites
- LSP Phase 1 (diagnostics) — must be complete
- LSP Phase 2 (navigation) — must be complete (symbol table required)
LSP Methods to Implement
textDocument/completion
Trigger on typing. Provide contextual completions:
| Context |
Candidates |
Source |
After Mod. |
Functions in that module |
Symbol table + stdlib registry |
After |> |
All visible functions |
Current module + imports |
| Start of expression |
Local functions + variables in scope |
Symbol table + scope analysis |
After import |
Known module names |
Stdlib list + project modules |
After def |
Nothing (new function name) |
— |
| Inside function args |
Variables in scope |
Scope tracking |
Completion Item Structure
{
"label": "puts",
"kind": 3, // Function
"detail": "IO.puts/1",
"documentation": "Print a string to stdout",
"insertText": "puts(${1:value})",
"insertTextFormat": 2 // Snippet
}
Stdlib Completion Registry
Build a static registry of all stdlib functions for completion:
stdlib_completions() -> #{
'IO' => [
#{name => puts, arity => 1, doc => <<"Print to stdout">>},
#{name => gets, arity => 0, doc => <<"Read line from stdin">>},
#{name => inspect, arity => 1, doc => <<"Debug print">>}
],
'String' => [
#{name => upcase, arity => 1, doc => <<"Uppercase a string">>},
#{name => downcase, arity => 1, doc => <<"Lowercase a string">>},
...
],
...
}.
Source this from winn_runtime.erl exported functions + doc comments.
textDocument/references
Find all uses of a symbol across the workspace.
Strategy:
- Identify the symbol at cursor position (function name, module name, variable)
- For functions: search all files' ASTs for
{call, _, Name, _} or {dot_call, _, Mod, Name, _} matching
- For modules: search for
{dot_call, _, Mod, _, _}, {import_directive, _, Mod}, {alias_directive, _, Mod, _}
- For variables: search within current function scope only
Workspace indexing:
- On startup: parse all
.winn files in src/ and subdirectories
- On file change: re-index that file only
- Store cross-reference map in ETS:
{SymbolKey, [{File, Line, Col}]}
textDocument/formatting
Integrate the existing winn_formatter with LSP:
handle_request(<<"textDocument/formatting">>, Params) ->
Uri = maps:get(<<"textDocument">>, Params),
Source = get_document(Uri),
case winn_formatter:format_string(Source) of
{ok, Formatted} ->
Edits = compute_full_text_edit(Source, Formatted),
{ok, Edits};
{error, _} ->
{ok, []} %% Don't format if parse fails
end.
This replaces the need for a separate format-on-save extension. Users get format-on-save via VS Code's built-in editor.formatOnSave setting.
textDocument/rangeFormatting
Format only a selection. Use winn_formatter:format_string/1 on the selected range — may need to extract the containing function/module for context.
textDocument/rename
Rename a symbol across the workspace:
- Find all references (reuse
textDocument/references)
- Generate
TextEdit for each occurrence
- Return
WorkspaceEdit with edits grouped by file
Scope:
- Variable rename: within current function only
- Function rename: across all files that call it
- Module rename: across all files + rename source file
textDocument/signatureHelp
Show function signature while typing arguments:
IO.puts( | )
─────────
IO.puts(value) — Print a string to stdout
Trigger on ( and ,. Look up the function being called in the symbol table, return parameter names and doc.
New Files
| File |
Purpose |
~Lines |
apps/winn/src/winn_lsp_completion.erl |
Completion provider + stdlib registry |
250 |
apps/winn/src/winn_lsp_references.erl |
Find-all-references engine |
200 |
apps/winn/src/winn_lsp_formatting.erl |
Formatter integration |
60 |
apps/winn/src/winn_lsp_rename.erl |
Rename refactoring |
150 |
apps/winn/src/winn_lsp_workspace.erl |
Workspace-wide indexing + file watching |
200 |
apps/winn/test/winn_lsp_completion_tests.erl |
Completion tests |
150 |
apps/winn/test/winn_lsp_references_tests.erl |
References tests |
100 |
Modified Files
| File |
Change |
winn_lsp_handler.erl |
Add handlers for completion, references, formatting, rename, signatureHelp |
winn_lsp.erl |
Register new capabilities in initialize response |
winn_lsp_documents.erl |
Workspace-wide document tracking |
Scope Tracking Enhancement
For completion and rename to work well, need basic scope tracking:
%% At cursor position, what variables are in scope?
%% Walk function body up to cursor line, collect assignments
scope_at(AST, Line) ->
%% Returns #{VarName => {DefinedAtLine, Type}}
This doesn't need full type inference — just track what names are bound and where.
Acceptance Criteria
Part of v0.9.0 — Developer Tooling
Summary
Add code completion, find-all-references, and formatting integration to the Winn LSP server. This is the final phase that makes the IDE experience production-grade.
Prerequisites
LSP Methods to Implement
textDocument/completionTrigger on typing. Provide contextual completions:
Mod.|>importdefCompletion Item Structure
{ "label": "puts", "kind": 3, // Function "detail": "IO.puts/1", "documentation": "Print a string to stdout", "insertText": "puts(${1:value})", "insertTextFormat": 2 // Snippet }Stdlib Completion Registry
Build a static registry of all stdlib functions for completion:
Source this from
winn_runtime.erlexported functions + doc comments.textDocument/referencesFind all uses of a symbol across the workspace.
Strategy:
{call, _, Name, _}or{dot_call, _, Mod, Name, _}matching{dot_call, _, Mod, _, _},{import_directive, _, Mod},{alias_directive, _, Mod, _}Workspace indexing:
.winnfiles insrc/and subdirectories{SymbolKey, [{File, Line, Col}]}textDocument/formattingIntegrate the existing
winn_formatterwith LSP:This replaces the need for a separate format-on-save extension. Users get format-on-save via VS Code's built-in
editor.formatOnSavesetting.textDocument/rangeFormattingFormat only a selection. Use
winn_formatter:format_string/1on the selected range — may need to extract the containing function/module for context.textDocument/renameRename a symbol across the workspace:
textDocument/references)TextEditfor each occurrenceWorkspaceEditwith edits grouped by fileScope:
textDocument/signatureHelpShow function signature while typing arguments:
Trigger on
(and,. Look up the function being called in the symbol table, return parameter names and doc.New Files
apps/winn/src/winn_lsp_completion.erlapps/winn/src/winn_lsp_references.erlapps/winn/src/winn_lsp_formatting.erlapps/winn/src/winn_lsp_rename.erlapps/winn/src/winn_lsp_workspace.erlapps/winn/test/winn_lsp_completion_tests.erlapps/winn/test/winn_lsp_references_tests.erlModified Files
winn_lsp_handler.erlwinn_lsp.erlinitializeresponsewinn_lsp_documents.erlScope Tracking Enhancement
For completion and rename to work well, need basic scope tracking:
This doesn't need full type inference — just track what names are bound and where.
Acceptance Criteria
IO.shows completion list of IO functionswinn fmtunder the hoodfunc(Part of v0.9.0 — Developer Tooling