Silk Language Server (LSP)
This document specifies the current Language Server Protocol (LSP) implementation for Silk.
The goal of the language server is to provide editor and IDE integrations (diagnostics, hover, go-to-definition, references, rename, completion, signature help, semantic tokens, inlay hints, document symbols, and future tooling extensions) while remaining a thin, spec-driven wrapper around the existing compiler front-end.
Overview#
The Silk language server:
- is implemented in Zig and shipped as a separate executable (
silk-lsp), - speaks the Language Server Protocol over stdin/stdout using JSON-RPC 2.0 and
Content-Lengthframing, - reuses the existing lexer, parser, and type checker for semantics,
- builds a workspace cache from open documents, nearest-package
silk.tomlroots, dependency package graphs, manifest definition files, and standard library modules when enabled, - maintains a lightweight native C symbol index for manifest-owned
.c/.h/.msources soextdeclarations can resolve to local native definitions when those sources are available, - does not change the language surface or ABI; it is a tooling layer on top of the existing compiler.
No language features or CLI options are introduced by the LSP itself. Any future extensions that affect language semantics or user-facing flags must still be documented in the appropriate docs/language/ or cli silk files first.
Running the Server#
The silk-lsp binary is built and installed alongside the silk CLI:
zig build install(or the project’s preferred build wrapper) will install bothsilkandsilk-lspinto the configured prefix.- Editor and IDE integrations should launch
silk-lspas a stdio-based LSP server, without extra arguments, and then speak JSON-RPC 2.0 over its stdin/stdout. - The server writes protocol messages to stdout and may emit diagnostic logs to stderr; LSP clients must not treat stderr as protocol traffic.
- Optional flags:
--std-root <path>overrides the stdlib root used for resolvingimport std::...;.--std <path>is an accepted alias of--std-root <path>.--nostddisables stdlib auto-loading entirely.-h/--helpprints the current usage text.
Typical client configurations (e.g., Vim/Neovim LSP, VS Code, or other LSP frontends) should:
- set the command to
["silk-lsp"], - enable standard LSP text document synchronization,
- rely on the capabilities advertised in
initialize; the current request/notification surface includes diagnostics, hover, definition, references, rename, completion, formatting, signature help, semantic tokens, inlay hints, document symbols, shutdown, and exit.
Transport and Protocol#
The language server:
- reads requests from standard input using the LSP message framing (
Content-Length: <n>\r\n\r\n<json>), - writes responses and server-initiated notifications to standard output using the same framing,
- implements JSON-RPC 2.0 semantics (
jsonrpc: "2.0",id,method,params,result/error).
The server does not depend on any external networking libraries; it uses Zig standard library I/O and JSON support.
Position handling note:
- The lexer tracks byte offsets and byte-based columns.
- The LSP layer maps between byte columns and LSP
utf-16character positions. - Clients should treat returned
line/charactervalues as LSP positions in UTF-16. - If a request uses a character position beyond the end of a line, the server clamps it to the line end when locating tokens.
Initialization#
The server supports the standard LSP initialization sequence:
initialize(request):- Advertised capabilities:
positionEncoding:"utf-16"(the server currently operates in UTF-16 positions for maximum client compatibility).textDocumentSync:openClose: true,change: 1(Full document sync),save: { includeText: false }.hoverProvider: true(semantic hover on literals, declarations, imports, and nativeexttargets as described below).definitionProvider: true(definition lookups backed by the module-set symbol index; see below).referencesProvider: true(reference queries across the current cached module set; see below).renameProvider: true(identifier renames across the current cached module set; see below).documentFormattingProvider: true(whole-document formatting via the canonicalsilk formatformatter; see below).documentSymbolProvider: true(hierarchical document symbols for declarations and nested bodies as described below).semanticTokensProvider:- legend-driven
fullsemantic token responses, - token types include namespaces, types, enums, interfaces, structs, parameters, variables, properties, enum members, functions, methods, keywords, comments, strings, numbers, and operators.
inlayHintProvider: true(type inlay hints for currently supported local bindings; see below).completionProvider:- does not support resolve,
- advertises trigger characters
.,:,{,,,",`, and/, - offers keyword, identifier, import-specifier, and symbol-aware suggestions as described below.
signatureHelpProvider:- trigger characters
(and,, - provides function and method signatures for the current call.
- The server uses
rootUri(orrootPath) to help locate a stdlib root when no explicit--std-rootorSILK_STD_ROOTis set. initialized(notification):- Accepted but does not currently trigger additional behavior.
shutdown(request) andexit(notification) are honored as in the LSP spec.- Requests received after
shutdown(other thanexit) are treated as invalid and answered with an error response. $\/cancelRequestnotifications are accepted and ignored; the initial server does not track per-request cancellation state.
Any additional capabilities beyond this documented set must be documented here before being implemented.
Hover#
The server provides textDocument/hover for open documents using the cached
workspace module set.
- Hover requests are handled for the current contents of an open document (as tracked in the server’s in-memory document table).
- The server computes hover information lexically, based on the token at the given position:
- integer literals are reported as “int literal”,
- floating-point literals as “float literal”,
- boolean literals (
true/false) as “bool literal”, - string and character literals as “string literal” and “char literal”,
- the context-sensitive keyword forms
panic,await,await *,yield,yield *,sizeof, andas rawas Markdown usage help, including hovering either token in the two-token*andas rawforms, - identifiers are reported as
identifier 'name'. - Hover now includes lightweight semantic hints:
- function identifiers show their
fn name (...) -> resultsignature when available, letbindings show the declared (or literal-inferred) type when available,- struct / enum / interface / error identifiers report
struct Name/enum Name/interface Name/error Name, extdeclarations reportext name: <type>when available,- field and method accesses (
value.field,value.method) report the field type for known struct or error receivers, and report method signatures for known struct receivers, - chained field receivers (
box.value.field) are resolved by walking the known struct/error field path, including applied generic structs where direct field type parameters can be substituted before rendering the hover type, - imported names are resolved across the module set:
- package imports (
import ns::pkg;,import ns::pkg as alias;), - qualified symbol imports (
import ns::pkg::name;,import ns::pkg::name as alias;,import ::malloc;), - JS-style imports (
import { name } from "dep/path";,import { name as alias } from "dep/path";,import alias from ns::pkg;), - and module-scope
usingaliases for imported or local names, - when an
extdeclaration resolves to a locally indexed native C symbol, hover includes the native C declaration/prototype in an additionalccode block, - native C lookup is filtered by the
extshape (fn/c_fnexterns prefer C functions; non-function externs prefer C variables), so common C tag/function collisions likestruct statvsstat(...)do not override the callable symbol. - When the resolved declaration has a doc comment, hover renders it as Markdown:
- the first block is a
silkcode block containing the signature/header, - followed by the rendered doc comment body.
- The hover
rangereturned to the client corresponds to the token span (same token line/column/length used for diagnostics); when no suitable token is found at the requested position, the server returnsnullas the hover result.
The native C index is intentionally lexical and top-level only; it is not a
full C parser. It is used to surface nearby declarations/definitions in
manifest-owned C sources, not to typecheck arbitrary C. It tolerates leading
indentation before preprocessor directives so common headers with indented
#include / #define lines still index the following declarations correctly.
Go To Definition#
The server provides textDocument/definition for open documents.
- Definition requests are handled for the current contents of an open document.
- The server first consults the module-set symbol index (open docs + manifest package graphs + definition files + std modules when enabled) to resolve:
- exported or package-local
fn,let,ext,struct,enum,interface, anderrordeclarations, - methods declared in
implblocks when invoked asvalue.method(...), - qualified names such as
std::pkg::nameand namespace-qualified names likealias::nameoralias::child::namewhenaliasis introduced by: - a package import,
- a default/namespace import,
- or a module-scope
usingalias. - Package and import resolution covers:
- package imports (
import ns::pkg;,import ns::pkg as alias;), - qualified symbol imports (
import ns::pkg::name;,import ns::pkg::name as alias;,import ::name;), - JS-style imports from package specifiers and file specifiers,
- and
usingaliases for both value names and namespace aliases. - Local scopes are then consulted to resolve:
- function parameters,
- block-scoped
letbindings, - destructuring
letpattern binders, let ... elsebinders after the statement succeeds,if letandwhile letpattern binders, including chained&& letclauses within the active body,- C-style
forinitializer bindings within the loop, match-statement binders within the selected arm body.- Member access (
value.field/value.method) uses the heuristic receiver-type resolver: - receiver expressions may be direct locals/types or chained field paths such as
box.value.field, - applied generic struct receivers substitute direct type-parameter fields before resolving the next field in the path,
- when a struct method is found, the definition points at the
implmethod declaration, - when a struct or error field is found, the definition points at that field declaration,
- otherwise the definition falls back to the receiver struct or error declaration.
- Constructor calls (
new Type(...), including namespace/import-qualifiednew pkg::Type(...)) resolve to the public/exportedfn constructoroverload selected by the callsite argument count, using the same arity ranking as the compiler for exact matches, defaulted trailing parameters, and trailing varargs. Empty allocation literals (new Type{}) similarly resolve through the zero-argument constructor ranking. If the call shape is incomplete, unmatched, hidden by visibility, or still ambiguous, the server falls back to thestruct Typedeclaration. - When a resolved
extdeclaration has a matching native C symbol in a manifest-owned source/header file, go-to-definition prefers the C definition (or declaration if no definition is present locally) over the Silkextdeclaration, using the same function-vs-variable matching rule as hover. - If symbol resolution fails, the server falls back to a lexical scan of the current file for the first matching
let/fn/ext/struct/enum/interface/errordeclaration.
Known limitations in this initial support:
- local block scopes and shadowing are modeled for statement-level binders, but match-expression binders and full control-flow refinement are not yet a complete semantic scope tree,
- ambiguous names across multiple imports are not disambiguated; the first match wins,
- cross-file results are limited to declarations present in the current module set (open docs, manifest-discovered package files, and std modules when enabled).
Manifest-aware cache rebuild note:
- When a nearest-package graph cannot be loaded (for example because a
dependency is missing or a manifest hash/version check fails),
silk-lspemits a concise stderr warning describing the failed package root and the manifest diagnostic it received, rather than silently dropping package indexing.
Completion#
The server provides textDocument/completion using the same cached workspace
module set.
- Completion items are offered for:
- all language keywords defined in
src/token.zig(viakeywordTable()), tagged with LSP keyword kind, - guided keyword-help completions for
panic,await,await *,yield,yield *,sizeof,#embed, andas raw, with compact detail strings and Markdown documentation for the typed-error, Promise, Task, byte-size, compile-time file embedding, and raw-cast usage contracts, - all distinct identifiers lexed from the current document (names that are not recognized as keywords),
- symbol-aware suggestions from the current package and imported packages (functions, lets, ext, structs, enums, interfaces, errors),
- imported names from:
- package imports and qualified symbol imports,
- JS-style named/default imports from file or package specifiers,
- and module-scope
usingaliases, - unqualified local-package and named imported functions in
statement-position buffers that are temporarily incomplete while typing
(for example before
(or;has been inserted), - import specifier path completion inside
from "..."strings: - relative file specifiers (
"./..."and"../...") suggest.slkfiles and subdirectories, - std package specifiers (
"std/...") suggest stdlib package paths (omitting the.slkextension), - unquoted package-path completion inside import specifiers, including
package imports such as
import oro::logger;and JS-style imports such asimport logger from oro::logger;, - named-import member completion from unquoted package paths, such as
import { format_ } from std::fmt;, #embed("path", "...")encoding completion in the optional second argument, offering exactly"utf8","utf16","u8","u16", and"u32"; omitting the argument is equivalent to"utf8",- namespace completions after
::for known packages, package-import aliases, default/namespace imports, andusing-introduced namespace aliases, including nested alias chains such asrt::mem::, - member completions for struct and error fields after
.when the receiver type is known (including locals with type annotations, struct/error literals, casts, ornew Type(...)initializers); struct receivers also include instance methods, - bare member-trigger completions such as
manager.by parsing a transient completion probe in memory and returning member results instead of falling back to global lexical keywords, - static impl method completions after a type receiver such as
User., limited toimpl Userfunctions without an explicitselfreceiver while value receivers keep instance fields and methods, - receiver completions after local bindings initialized from static impl calls, such as
let user = User.create(options); user., by using the static impl function result type, - array element receiver completions such as
inputs[0].wheninputshas an array or slice type annotation, for item in items { item. }completions whenitemshas an array or slice type annotation,for item in manager.iter() { item. }completions when the iterator-returning call result, local type aliases, package/file imports, andnext() -> T?element type can be resolved through the cached symbol index or transient parse overlay,- local completion and receiver inference for statement-level language
binders, including destructuring
letpatterns,let ... else,if let/while letand chained&& letclauses,matchstatement arms, and C-styleforinitializers, - field/method completions for safe pattern payloads when the scrutinee type
is known, including optional
Some(value)payloads, genericResult(T, E)-styleOk(value)/Err(error)payloads, struct destructuring fields, and array destructuring elements, - chained field completions such as
box.value.by walking known struct/error receiver fields without invoking the full type checker, - applied generic receiver completions such as
b.value.whereb: Box(Inner)by substituting direct struct type parameters (T) into field types before resolving the next field, - struct- and error-literal field suggestions in
Type { ... }expressions, includingpanic Error { ... }payloads, when the cursor is in a field-name position (before the:); this contextual completion remains limited to record fields while the entry is incomplete, returns field-kind items withfield: Typedetail/documentation, inserts the canonicalfield:initializer prefix throughtextEdit, suppresses fields already initialized in the current literal, and suppliesfilterTextso editors can keep showing field choices even when the partial token does not prefix-match a field label. - Currently:
- returns completion items with
label,kind, anddetailpopulated when symbol data is available, - attaches Markdown documentation for functions, methods, structs, and documented
letbindings when doc comments are available, falling back to a compact signature/type preview, - filters results by the identifier prefix immediately preceding the cursor position,
- uses a heuristic symbol index built from the module set (open docs + imports + std modules).
- When the current buffer is syntactically incomplete, ordinary identifier
completion may parse a transient in-memory probe before falling back to
lexical identifiers. If a lexical identifier is later resolved to a symbol,
the server promotes that item with the resolved kind, detail, and
documentation. If the symbol overlay still cannot represent the current
file, lexical
fn namedeclarations are still promoted to function completion items so local functions remain visible. - Scope precision is still limited:
- receiver type inference is heuristic (it is not a full typechecker),
- match-expression binders and full control-flow refinements are not yet represented in completion,
- cross-file results are limited to declarations present in the current module set (open docs + manifest/package import closure + std modules when enabled).
As richer front-end support becomes available, completion may be extended to:
- filter suggestions by lexical/semantic scope,
- distinguish between functions, types, variables, and other symbol kinds,
- surface standard library symbols by consulting the resolver.
Signature Help#
The server provides a minimal implementation of textDocument/signatureHelp:
- Signature help is computed for the innermost call expression at the cursor.
- The server supports:
- direct calls to named functions (
foo(...)), - qualified calls (
std::pkg::foo(...)andalias::foo(...)for namespace imports), - method calls (
value.method(...)) when the receiver resolves to a known struct type. - constructor calls via heap allocation syntax (
new Type(...)), which resolves to theconstructoroverload set defined inimpl Type { ... }. - calls via named imports and default-imported default exports (
import { f as g } from "..."; g(...),import g from "./mod.slk"; g(...)) when resolvable. - When the current buffer is temporarily incomplete after typing
(or,, signature help parses a transient in-memory call probe and overlays the current document's symbols on the workspace cache, so local, named-imported, default-imported, and namespace-qualified function signatures remain available while the user is still typing the argument list. - Active parameter selection is based on delimiter-aware comma counting in the current call. Explicit generic-call arguments before
;are excluded from the value-argument count, and commas inside nested calls, array literals, and aggregate literals are ignored for the outer call. - Signature labels follow the Silk surface syntax (e.g.
fn foo (a: int, b: int) -> int). - When doc comments are available:
SignatureInformation.documentationis populated with rendered Markdown from the doc comment,SignatureParameter.documentationis populated from@paramentries when present.
For constructor overload sets, the server returns public/exported signatures and selects an active signature using delimiter-aware value-argument heuristics, with exact arity preferred over defaulted trailing parameters and varargs. The implicit receiver parameter (mut self: &Type) is not shown in the signature parameters for new Type(...).
Signature help is heuristic and will become richer as the front-end’s symbol tables evolve. Some clients may request signature help even before ( is typed; the server will attempt to resolve the identifier under the cursor as a callee in that case.
Document Symbols#
The server provides textDocument/documentSymbol for open documents.
- Document symbols are produced hierarchically from the parsed AST:
- top-level
fn,let,struct,enum,error,interface,ext,impl,test,using, and inline-module declarations are reported, - struct / enum / error / interface children include fields, variants, and methods where applicable,
impldeclarations nest their methods,- functions, tests, loops,
matcharms, and similar nested bodies surface supported local declarations and binders beneath their containing symbol. - Implementation details:
- ranges are token- or body-based spans derived from the parsed declaration/block structure,
selectionRangetracks the declaration identifier token,- nested coverage is intentionally selective: supported locals and binders are surfaced, but this is not yet a perfect semantic scope tree for every binder form.
- Symbol kinds:
- functions are reported using the LSP
Functionkind (numeric value12), letbindings are reported using theVariablekind (numeric value13),structdeclarations useStruct(numeric value23),enumdeclarations useEnum(numeric value10),errordeclarations useStruct(numeric value23),interfacedeclarations useInterface(numeric value11),extdeclarations useFunction(numeric value12),impldeclarations useNamespace(numeric value3).
References and Rename#
The server provides textDocument/references and textDocument/rename for
identifiers that resolve within the current cached module set.
- Reference lookups:
- resolve the identifier under the cursor to its declaration using the same module-set-aware navigation used by go-to-definition,
- scan the cached module set for matching identifier occurrences that resolve back to that same declaration,
- optionally include the declaration itself when the client sets
context.includeDeclaration. - Rename:
- uses the same declaration-resolution and reference collection path,
- returns a
WorkspaceEdit.documentChangespayload with per-document text edits, - rejects invalid Silk identifier spellings (
newNamemust be a valid identifier).
Current boundaries:
- results are limited to the cached module set (open docs, manifest/package/file-import closure, and std modules when enabled),
- rename is identifier-based and does not yet attempt broader semantic refactors outside that cached symbol graph.
Semantic Tokens#
The server provides textDocument/semanticTokens/full.
- The implementation combines:
- lexical token classification for keywords, comments, strings, numbers, and operators,
- AST-backed semantic marking for declaration and binder tokens such as namespaces, types, structs, enums, interfaces, functions, methods, parameters, variables, properties, and enum members.
- This gives editors a stronger semantic surface than plain TextMate tokenization while still reusing the existing parser/module cache.
Limitations:
- token classification is still declaration/binder-oriented rather than a full resolver/type-driven classification for every identifier use,
- token modifiers are not yet emitted.
Inlay Hints#
The server provides textDocument/inlayHint.
- The implementation emits type hints for:
- unannotated
letbindings when the initializer’s type is inferable from the current tooling subset, forbinders over inferable arrays, slices, and ranges,- unannotated C-style
forinitializer bindings with the same inferable-type rule. - Hints are range-aware: the server only returns hints whose insertion positions fall inside the requested LSP range.
Limitations:
- only a narrow type-hint subset is emitted today,
- parameter-name hints and broader expression/result hints are not yet implemented.
Formatting#
The server provides textDocument/formatting for open documents.
- Formatting uses the same canonical source formatter as
silk format. - The response is whole-document oriented:
- if the open document is already formatted, the server returns an empty edit list,
- otherwise it returns one full-range
TextEditthat replaces the document text with the formatted text. - Newline-based
if/else ifheaders use the same canonical layout as the CLI formatter: chained condition lines are indented one level deeper than the control keyword, and the following standalone{aligns back to the control keyword.
Limitations:
- LSP formatting currently uses the default formatter configuration; editor-specific
FormattingOptionsand.silk/format.tomldiscovery are not yet applied inside the server. - Range formatting is not advertised.
Text Document Lifetime and Diagnostics#
The server maintains an in-memory table of open documents, keyed by URI:
textDocument/didOpen:- stores the full text of the document,
- rebuilds a lightweight workspace cache (module set + symbol index + export table) used for hover/definition/completion/signature help,
- publishes diagnostics via
textDocument/publishDiagnosticsfor the opened URI by parsing the opened document and type-checking it against the cached module set (imports + std modules). textDocument/didChange(full sync):- replaces the stored text with the new full contents,
- increments only the changed document revision for already-open documents so hover/definition/completion/signature-help can continue using the existing workspace/import/std cache while typing,
- parses the changed document and type-checks it against the cached module set (imports + std modules),
- publishes updated diagnostics for the changed URI.
textDocument/didSave:- rebuilds the module set from all open documents,
- resolves imports across the module set (packages + file imports) and loads standard library modules when configured,
- type-checks the module set,
- publishes diagnostics via
textDocument/publishDiagnosticsfor any affected module URI (including imports). textDocument/didClose:- removes the document entry,
- publishes an empty diagnostics list for the closed URI,
- rebuilds the workspace cache for the remaining open documents.
For responsiveness, the server caches parsed modules (AST + lightweight module info) per open document revision and reuses them across hover/definition/completion/signatureHelp requests until the document changes.
Workspace Cache and Package Discovery#
The workspace cache is manifest-aware:
- for each open document with a filesystem path, the server walks upward to the
nearest owning
silk.toml, - it loads the full package graph rooted there, including dependency packages, using the same contextual package search roots as the CLI for nested pathless dependencies,
- it adds manifest-declared definition files to the indexed module set,
- it applies the manifest package name as the default package for source or
definition files that omit an explicit
package ...;/module ...;declaration, - and it collects manifest-owned
.c/.h/.msources from target inputs and shipped C headers for the native symbol index.
This keeps position-time queries on precomputed module/package/native symbol data rather than rescanning manifests or package graphs for each request.
File-backed URI handling note:
- open-document URIs and file-backed cache entries are normalized to the same
file://form before lookup, - normalization canonicalizes percent-encoding,
file://localhost/...versusfile:///..., Windows drive-letter URIs, and UNC host casing so path spelling differences do not fragment the cache.
Standard Library Integration#
By default, the language server will load standard library packages referenced by import std::... or quoted std path imports such as import { f } from "std/fmt"; when a stdlib root is available. The stdlib root is selected using the same rules as the compiler, with an additional workspace-root fallback:
--std-root <path>passed tosilk-lsp(highest priority),SILK_STD_ROOTwhen set,./stdwhen present (development default),- an executable-relative fallback (
../share/silk/std) when installed, - walk upward from the
silk-lspexecutable’s directory to findstd/(developer build fallback), - if none of the above are available and the LSP client provides
rootUri/rootPath, walk upward from that workspace root to find astd/directory.
You can disable stdlib integration entirely with --nostd, which is useful for sandboxed editor setups or custom stdlib forks.
Diagnostics Source and Current Limits#
Diagnostics are derived from the existing compiler front-end:
- Parsing uses
parser.Parserand the existing grammar in grammar. - Type checking uses
checker.checkModuleand the rules from types and related concept docs.
For responsiveness while typing:
didChangediagnostics are computed for the changed document by parsing it and type-checking it against the cached module set (imports + std modules). The cache is not rebuilt on every change.- A full module-set parse + resolve + type-check (including imports) is performed on
didSave, and diagnostics are published for all affected modules.
The current LSP diagnostic surface follows these rules:
- Parse errors:
- reported at the location of the unexpected token using the token’s line/column and length,
- message text describes the unexpected token and that parsing failed,
- publish
E0001and attach structureddata.helpspointing atsilk error E0001for clients that surface custom diagnostic data, #embed("path")expressions use the document URI’s filesystem path for source-relative lookup, and unreadable or invalid embedded files are reported as parse diagnostics on the path literal.- Resolve/type-check diagnostics:
- use the compiler’s existing structured failure data (stable code, message, source span, and any available detail),
- map that span/code directly onto the LSP diagnostic when the compiler reports one,
- include exact expected/found type detail for supported incorrect
await,await *,yield, andyield *task/async operand errors, - include a
silk error <code>lookup hint for stable compiler codes so editor users can open the same diagnostic reference available from the CLI, - flatten available note/help guidance inline into the LSP
messagefield and also attach structureddata.detail,data.notes, anddata.helpswhen the compiler provides them. - Conditional compilation (
if attr(...)): - the server evaluates
attr(...)query conditions using the host target (arch,os,target) and the enabled feature set (empty in Silk currently), - when an
if/else ifcondition is an attribute-query boolean expression that resolves to a constanttrue/false, the inactive branch body is published as aHintdiagnostic taggedUnnecessaryso editors may render it faded (similar to inactive#ifblocks in C/C++).
Current limits:
- parse and attribute-pass diagnostics are still built locally from the parser / attr pass because those subsystems do not yet surface the same reusable diagnostic struct as the later resolve/type-check stages,
- when an internal error path still lacks an exact span, the server falls back to the owning module with a coarse range rather than dropping the diagnostic entirely.
Non-Goals#
silk-lsp still intentionally does not provide:
- full semantic completions with perfect scope/type filtering across every binder/control-flow form,
- project-wide navigation or rename outside the current cached module set,
- fully semantic token classification for every identifier use,
- richer inlay-hint categories beyond the current local type hints,
- code actions.
These features are intended as future extensions and must be:
- designed and documented here (and in any relevant
docs/language/ordocs/std/docs), - backed by the underlying compiler front-end and/or standard library,
- covered by tests (Zig and, where appropriate, C) before being advertised as supported capabilities.
Relationship to Other Tooling#
The language server is part of the broader tooling story described in:
- architecture (compiler and tool layout),
- cli silk (CLI behavior for
silk), docs/usage/(editor integrations, including Vim and LSP-based workflows).
The tmp/zls/ directory in the Silk compiler repository contains a built-in copy of the Zig Language Server (ZLS) for inspiration and experimentation only:
- it is not part of the supported Silk toolchain,
- it must not be treated as authoritative for Silk semantics,
- ideas from it may inform the design and implementation of
silk-lsp, but Silk remains spec-driven from thedocs/tree.
Source repository · Edit this page · View Markdown