Skip to content

LSP: stdlib completion + hover generated from lib/ signature comments (fixes #590)#691

Merged
InauguralPhysicist merged 1 commit into
InauguralSystems:mainfrom
Nitjsefnie-OSC:lsp-stdlib-completion-590
Jul 22, 2026
Merged

LSP: stdlib completion + hover generated from lib/ signature comments (fixes #590)#691
InauguralPhysicist merged 1 commit into
InauguralSystems:mainfrom
Nitjsefnie-OSC:lsp-stdlib-completion-590

Conversation

@Nitjsefnie

Copy link
Copy Markdown
Contributor

Implements #590: the ~75-module lib/ layer becomes visible to LSP completion and hover, driven by a build-time generated table.

What

  • tools/gen_lsp_stdlib_index.sh (new, shell+awk, zero-dependency, conventions follow stdlib_index_check.sh): scrapes every lib/*.eigs for line-start public define name(params) definitions (leading-underscore = private, excluded) plus the rule-2 # name of args -> type signature comment from the comment block directly above. Emits src/lsp_stdlib_index.h (static const char *stdlib_docs[][4] — module, name, params, detail). Current tree: 815 public functions across 75 modules (the issue's ~808 was the 2026-07-13 audit count).
  • Build wiring: Makefile dependency rule (lib/*.eigs + the script → header) so the table regenerates before eigenlsp compiles; build.sh lsp runs the generator too. The header is generated, not committed — matches the repo's .gitignored-generated-artifacts precedent (build/, make lib + two-file amalgamation: ship the embeddable artifact #397).
  • src/eigenlsp.c (+61): completion offers an imported module's public functions as kind-3 Function items with the signature as detail (unimported modules stay invisible — reuses the existing SYM_IMPORT tracking); hover on mod.name resolves against that module, bare names against any imported module; missing signature comments fall back to the define name(params) as: shape (~70% of lib/ functions currently lack the rule-2 comment — deliberately NOT back-filled here, that's tracked separately in STDLIB.md).
  • Tests: +8 checks in tests/test_lsp.py (offer/detail/fallback/omit-unimported/empty-without-import/hover×3). The [88] section header said "(23 checks)" while the harness actually ran 72 — corrected to the new true count of 80 (the pass/fail gate counts dynamically; the header is cosmetic).

Verification

  • Release suite: RESULTS: 3138/3138 passed, 0 failed; [88] all 80 LSP checks pass; stdlib_index_check.sh green incl. selftest.
  • ASan+leaks suite: 3142/3142 passed, 0 sanitizer reports; the LSP-specific sanitizer gate (tests/test_lsp_asan.sh) passes 80/80.
  • Mutation-verified: with the generated table emptied, the 5 positive new checks fail — the tests actually bite.

Maintainer-facing choices (overrule freely)

  • Bare-name hover with two imported modules defining the same name (e.g. list.drop vs store.drop) serves the first match deterministically; dotted hover is exact. Kept simple since qualified calls are the house style.
  • No dedupe when a lib function shadows a builtin name in completion (e.g. map) — matches the handler's existing behavior for local defines shadowing builtins.
  • Signature comments are served verbatim (e.g. lib/audio.eigs writes => instead of ->); a comment-shape lint felt like its own issue, not part of this one.

Generated by Claude Fable 5 (brief, review), Kimi K3 (implementation)

…InauguralSystems#590)

handle_completion/handle_hover served builtin_docs[] and document-local
symbols, but the 75-module pure-EigenScript lib/ layer (815 public
functions) was invisible: typing `stats.` offered nothing, and hovering a
lib/ call showed nothing.

tools/gen_lsp_stdlib_index.sh (shell+awk, zero-dependency, in the style of
tools/stdlib_index_check.sh) scrapes lib/*.eigs for public
`define name(params)` definitions (leading-underscore names stay private)
plus their rule-2 `# name of args -> type` signature comment
(docs/STDLIB.md "Writing Library Functions"), emitting
src/lsp_stdlib_index.h: a static {module, name, params, detail} table.
Functions without a signature comment fall back to the
`define name(params) as:` shape — back-filling those comments is tracked
separately in STDLIB.md, not papered over here.

The header is a build artifact, not committed (the build/ amalgamation
precedent, InauguralSystems#397): .gitignore'd, and regenerated by the Makefile lsp
target and build.sh lsp before eigenlsp compiles, so install.sh and both
CI lsp legs pick it up automatically.

eigenlsp serves the table through the existing paths, scoped by the
document's own import-symbol tracking:
- completion: an imported module's public functions are offered as
  kind-3 Function items with the signature as detail; unimported modules
  stay invisible.
- hover: a dotted `mod.name` pins the module; a bare name matches any
  imported module's function. Either way the detail is the signature
  comment or the define-shape fallback.

tests/test_lsp.py gains 8 checks (imported offered w/ signature detail,
fallback detail, unimported absent, no-import empty, hover signature,
hover fallback, hover unimported -> null); the [88] header count moves
23 -> 80 (it had already drifted from the real 72).

Gates: release suite 3138/3138, asan+ubsan suite (detect_leaks=1)
3142/3142, test_lsp_asan.sh 80/80 with no sanitizer reports,
tools/stdlib_index_check.sh + selftest green.

Co-Authored-By: Kimi K3 <noreply@kimi.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants