feat(lsp): experimental in-process LSP server with hover support#21
Open
StanAngeloff wants to merge 12 commits intodevelopfrom
Open
feat(lsp): experimental in-process LSP server with hover support#21StanAngeloff wants to merge 12 commits intodevelopfrom
StanAngeloff wants to merge 12 commits intodevelopfrom
Conversation
Extend flemma.ast.Position with an optional end_col field and populate
it for both {{ expr }} and @./file expression segments in the parser.
This enables column-precise cursor-to-node detection for LSP hover and
gf navigation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generic cursor-to-node lookup that returns the segment and parent message at a 1-indexed (line, col) position. Uses column refinement for inline segments (expressions) and line-only matching for multi-line segments (thinking, tool_use, tool_result). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover column-precise position tracking for {{ }} and @./ expressions,
trailing punctuation exclusion, cursor-to-node resolution across
segment types (text, expression, thinking, tool_use), and same-line
disambiguation of adjacent expressions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add flemma.config.Experimental type with an lsp boolean (default false) to gate experimental features. This provides a clean namespace for in-development capabilities without polluting the main config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement an experimental in-process LSP server for .chat buffers using vim.lsp.start() with a Lua cmd function. The server handles textDocument/hover by resolving cursor position to an AST segment via find_segment_at_position and serializing the segment to a structured markdown hover card showing kind, role, content, and position. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Call lsp.setup() during plugin initialization when experimental.lsp is true. The setup registers a FileType autocmd that attaches the in-process LSP client to chat buffers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover client attachment/non-attachment based on config flag, and hover responses for expression, text, thinking, and tool_use segments. Uses request_sync with named buffers for reliable URI resolution in the headless test environment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Also includes formatter-applied line wrapping in parser.lua and quote normalization in test files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Log lifecycle events (setup, attach, server creation) at debug level and per-request details (method dispatch, hover hits/misses with position) at trace level for diagnosing issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reserve INFO for rare notable events (setup, exit). Shutdown and terminate are routine cleanup — DEBUG is the right level, matching the codebase convention where INFO appears only a handful of times. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… target Turn on experimental.lsp and set logging level to TRACE in the make develop config so new LSP instrumentation is immediately visible during manual testing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three-tier hover resolution ensures every position returns a result: 1. Segment-level (text, expression, thinking, tool_use, tool_result) 2. Message-level (role marker lines) — shows segment count/breakdown 3. Frontmatter — shows language, byte length, and code Restructured find_segment_at_position to return (nil, msg) when cursor is within a message range but no segment matched, enabling role marker detection without bypassing the AST. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an experimental in-process LSP server that attaches to
.chatbuffers and provides hover inspection of every AST node. This is the foundational infrastructure for future LSP features (goto-definition for includes, diagnostics, completions, etc.).Enable with:
Architecture
The LSP server runs in-process via
vim.lsp.start()with a Luacmdfunction — no external binary, no sockets. Neovim's built-in LSP client calls directly into our dispatch table. The server advertiseshoverProvidercapability and handlestextDocument/hoverrequests by:ast.find_segment_at_position(doc, lnum, col)to resolve cursor → nodeThree-tier hover resolution
Every buffer position returns a hover result — there are no dead zones:
@Role:marker line)text=2, tool_use=1)Cursor-to-node detection (
find_segment_at_position)This is the core query function added to
flemma.ast.query. It resolves a(line, col)pair to the innermost AST node:start_col/end_col(expressions, file references) — needed because multiple segments can share a line (e.g.Hello {{ a }} and {{ b }})(nil, msg)so the hover handler can show message-level infoParser changes
end_coltoflemma.ast.Positiontypeend_colfor expression segments ({{ expr }}) and file reference segments (@./path), enabling column-precise hover detection on shared linesNew files
lua/flemma/lsp.lua— In-process LSP server: dispatch table, hover handler, markdown serializers for each node typetests/flemma/lsp_spec.lua— 8 tests: attach/detach, hover for expression, text, thinking, tool_use, role marker, frontmatterModified files
lua/flemma/config.lua— Addedexperimental = { lsp = false }default andflemma.config.Experimentaltypelua/flemma/init.lua— Wireslsp.setup()behind theexperimental.lspflaglua/flemma/ast/nodes.lua— Addedend_col? integertoflemma.ast.Positionlua/flemma/ast/query.lua— Addedfind_segment_at_position()with column refinement and message-level fallbacklua/flemma/parser.lua— Computesend_colfor expression and file reference segmentstests/flemma/ast_spec.lua— 10 new tests:end_colpositions,find_segment_at_position(expression, text, out-of-range, thinking, tool_use, adjacent expressions, role marker)Makefile—make developnow enablesexperimental.lsp = trueandTRACElogging for manual testingLogging
The LSP module follows codebase logging conventions:
request/notifycall, full hover markdown responseslsp.setup()activation onlyTest plan
make qapasses (luacheck, LuaLS types, inline-requires, all tests).chatfile withexperimental.lsp = true, hover over text, expressions,@Role:markers, thinking blocks, tool blocks, frontmatter — each shows structured infoexperimental.lsp = false(default)🤖 Generated with Claude Code