Documentation
¶
Overview ¶
Package lsp provides Language Server Protocol integration for evva.
The lsp_request tool (deferred) allows the agent to query LSP servers for semantic code intelligence: go-to-definition, find references, hover, and document symbols. Servers are started lazily on first use and managed via the daemon system.
See docs/roadmap/design/lsp.md for architecture and implementation plan.
Index ¶
- func DiscoverServers(workdir string) map[string]LspServerConfig
- func FormatDiagnosticsReminder(diags []PendingDiagnostic) string
- func IsNotFoundError(err error) bool
- func Names() []tools.ToolName
- func NewTool(mgr *Manager, workDir string) *lspTool
- func SuggestServerForExt(ext string) string
- type Client
- func (c *Client) Close() error
- func (c *Client) Notify(ctx context.Context, method string, params any) error
- func (c *Client) OnNotify(method string, handler NotificationHandler)
- func (c *Client) Process() *os.Process
- func (c *Client) Request(ctx context.Context, method string, params any) (json.RawMessage, error)
- type DiagnosticRegistry
- type LspConfig
- type LspServerConfig
- type Manager
- func (m *Manager) CloseFile(ctx context.Context, filePath string) error
- func (m *Manager) DiagnosticsForFile(ctx context.Context, filePath string, timeout time.Duration) []PendingDiagnostic
- func (m *Manager) DidChange(ctx context.Context, filePath, content string) error
- func (m *Manager) DocumentSymbols(ctx context.Context, path string) ([]Symbol, error)
- func (m *Manager) DrainDiagnostics() []PendingDiagnostic
- func (m *Manager) EnsureServerStarted(ctx context.Context, filePath string) (*Server, error)
- func (m *Manager) FirstExtensionFor(serverName string) string
- func (m *Manager) NotifyFileChanged(filePath string)
- func (m *Manager) OpenFile(ctx context.Context, filePath, content string) error
- func (m *Manager) ServerForFile(filePath string) (*Server, bool)
- func (m *Manager) Servers() []string
- func (m *Manager) SetDaemonState(ds *daemon.DaemonState)
- func (m *Manager) Shutdown(ctx context.Context) error
- func (m *Manager) WorkspaceSymbols(ctx context.Context, query string) ([]Symbol, error)
- type NotificationHandler
- type PendingDiagnostic
- type Server
- func (s *Server) Capabilities() protocol.ServerCapabilities
- func (s *Server) IsHealthy() bool
- func (s *Server) MaxRestarts() int
- func (s *Server) Notify(ctx context.Context, method string, params any) error
- func (s *Server) OnNotify(method string, handler NotificationHandler)
- func (s *Server) Request(ctx context.Context, method string, params any) (json.RawMessage, error)
- func (s *Server) RestartCount() int
- func (s *Server) SetDiagHandler(h NotificationHandler)
- func (s *Server) Start(ctx context.Context) error
- func (s *Server) State() ServerState
- func (s *Server) Stop(ctx context.Context) error
- type ServerState
- type Symbol
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DiscoverServers ¶
func DiscoverServers(workdir string) map[string]LspServerConfig
DiscoverServers scans the workdir for project markers and checks PATH for matching LSP servers. Returns configs for usable servers.
func FormatDiagnosticsReminder ¶
func FormatDiagnosticsReminder(diags []PendingDiagnostic) string
FormatDiagnosticsReminder renders diagnostics as a <system-reminder> block.
func IsNotFoundError ¶
IsNotFoundError reports whether err indicates the server binary was not found.
func SuggestServerForExt ¶
SuggestServerForExt returns the name of a known server that handles the given extension, or "" if none is known.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a JSON-RPC 2.0 client that speaks the LSP framing protocol (Content-Length header + JSON body) over stdin/stdout of a child process.
func Start ¶
func Start(ctx context.Context, command string, args []string, logger *slog.Logger) (*Client, error)
Start spawns the LSP server process and starts the reader goroutine. The caller owns the returned Client and must call Close to clean up.
func (*Client) Close ¶
Close sends a graceful shutdown sequence: shutdown request → exit notification → kill process. Callers should use Server.Stop instead of calling this directly — Server manages the handshake timeout.
func (*Client) OnNotify ¶
func (c *Client) OnNotify(method string, handler NotificationHandler)
OnNotify registers a handler for incoming notifications. Handlers are called synchronously from the reader goroutine — keep them fast.
type DiagnosticRegistry ¶
type DiagnosticRegistry struct {
// contains filtered or unexported fields
}
DiagnosticRegistry collects, deduplicates, and drains LSP diagnostics.
func NewDiagnosticRegistry ¶
func NewDiagnosticRegistry() *DiagnosticRegistry
NewDiagnosticRegistry creates a registry with default limits.
func (*DiagnosticRegistry) ClearFile ¶
func (r *DiagnosticRegistry) ClearFile(fileURI string)
ClearFile removes pending diagnostics for a file and marks their identity keys as delivered (so they don't re-appear if the server re-sends them).
func (*DiagnosticRegistry) Drain ¶
func (r *DiagnosticRegistry) Drain() []PendingDiagnostic
Drain returns all pending diagnostics and clears the queue.
func (*DiagnosticRegistry) Register ¶
func (r *DiagnosticRegistry) Register(serverName, fileURI string, diags []protocol.Diagnostic)
Register adds diagnostics for a file. Already-delivered diagnostics (matched by identity key) are dropped. Per-file and total caps are enforced.
func (*DiagnosticRegistry) TakeFile ¶ added in v1.11.0
func (r *DiagnosticRegistry) TakeFile(fileURI string) []PendingDiagnostic
TakeFile removes and returns pending diagnostics for fileURI, marking their identity keys delivered (so a later server re-publish or the passive DrainDiagnostics path never redelivers what this call already returned). Returns nil if nothing is pending for that file. Used by DiagnosticsForFile's bounded wait — the synchronous-tier counterpart to the passive Drain/ClearFile pair above.
type LspConfig ¶
type LspConfig struct {
Servers map[string]LspServerConfig `yaml:"servers"`
}
LspConfig is the top-level config file shape.
func LoadConfig ¶
LoadConfig reads project-level .evva/lsp_servers.yml with fallback to user-level ~/.evva/lsp_servers.yml. Project-level entries override user-level for the same server name. Returns a default empty config when neither file exists.
type LspServerConfig ¶
type LspServerConfig struct {
Command string `yaml:"command"`
Args []string `yaml:"args"`
Extensions map[string]string `yaml:"extensions"` // ".go" → "go", ".ts" → "typescript"
Env map[string]string `yaml:"env"`
StartupTimeout string `yaml:"startupTimeout"` // "30s"
MaxRestarts int `yaml:"maxRestarts"` // default 3
}
LspServerConfig describes one LSP server to manage.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager routes files to LSP servers based on extension and manages server lifecycles (lazy start, document sync, shutdown).
func NewManager ¶
NewManager creates a Manager from a list of server configs. Servers are registered but not started — they start lazily on first EnsureServerStarted.
func (*Manager) DiagnosticsForFile ¶ added in v1.11.0
func (m *Manager) DiagnosticsForFile(ctx context.Context, filePath string, timeout time.Duration) []PendingDiagnostic
DiagnosticsForFile blocks until a diagnostic for filePath arrives in the registry, ctx is done, or timeout elapses — whichever comes first — then returns and REMOVES just that file's pending entries (via DiagnosticRegistry.TakeFile) so the passive between-turns drain (DrainDiagnostics) never redelivers what this call already returned. Returns nil on timeout/cancellation; never blocks past timeout.
func (*Manager) DidChange ¶ added in v1.11.0
DidChange tells the server responsible for filePath that its content is now content (full-document sync — no incremental range math, see the PRD's design rationale in docs/roadmap/PRD/edit-diagnostics-sync.md §5.2). First touch lazy-opens the file (didOpen, v1) via OpenFile; subsequent calls bump a per-URI version and send textDocument/didChange. Stale diagnostics for the URI are cleared before the resync so the next publish is authoritative (never a ghost error against a line the edit already fixed).
Best-effort by contract: a missing/unhealthy server for this file's language is a silent no-op, not an error — callers (the fs edit/write tools, via the LSPSyncSink adapter) must never have a mutation fail or block because of this.
func (*Manager) DocumentSymbols ¶ added in v1.8.2
DocumentSymbols runs textDocument/documentSymbol for one file and returns the outline as neutral Symbols, flattening the hierarchical response (a tree of types → members) into a depth-first slice with Container set to the parent's name. Opens and closes the document around the request, exactly like the lsp_request document_symbols path (executeFileOp). Returns an error when the file is unreadable or no server handles its extension.
func (*Manager) DrainDiagnostics ¶
func (m *Manager) DrainDiagnostics() []PendingDiagnostic
DrainDiagnostics returns all pending diagnostics and clears the queue. Called by the agent loop at the start of each iteration.
func (*Manager) EnsureServerStarted ¶
EnsureServerStarted lazily starts the server responsible for filePath. Uses singleflight to prevent duplicate starts — all concurrent callers for the same server share one start attempt and result.
func (*Manager) FirstExtensionFor ¶
FirstExtensionFor returns the first file extension (including leading dot) mapped to the given server name, or "" if none is mapped.
func (*Manager) NotifyFileChanged ¶
NotifyFileChanged clears diagnostics for a file. Call this after external file modifications (write, edit, bash) to invalidate stale diagnostics.
func (*Manager) OpenFile ¶
OpenFile sends textDocument/didOpen to the server responsible for filePath and tracks the open state.
func (*Manager) ServerForFile ¶
ServerForFile returns the server responsible for the given file path and whether one was found.
func (*Manager) SetDaemonState ¶
func (m *Manager) SetDaemonState(ds *daemon.DaemonState)
SetDaemonState installs the daemon catalog for LSP lifecycle tracking. When set, LSP servers are registered as daemons on start so daemon_list and daemon_output can introspect their state.
func (*Manager) WorkspaceSymbols ¶ added in v1.8.2
WorkspaceSymbols runs a workspace/symbol query against the first configured server and returns the results as neutral Symbols. An empty query asks the server for everything it knows (gopls and most servers treat "" as a broad sweep), which is what the repo map wants. Returns an error when no server is configured or the request fails — the caller degrades to the glob fallback.
type NotificationHandler ¶
type NotificationHandler func(params json.RawMessage)
NotificationHandler receives incoming notifications from the server.
type PendingDiagnostic ¶
type PendingDiagnostic struct {
ServerName string
FileURI string
FilePath string
Diagnostics []protocol.Diagnostic
}
PendingDiagnostic holds diagnostics queued for delivery for a single file.
type Server ¶
type Server struct {
Name string
Config LspServerConfig
// contains filtered or unexported fields
}
Server wraps a single LSP server process with a state machine.
func NewServer ¶
NewServer creates a Server from the given config. The server is not started until Start is called.
func (*Server) Capabilities ¶
func (s *Server) Capabilities() protocol.ServerCapabilities
Capabilities returns the server's advertised capabilities.
func (*Server) MaxRestarts ¶
MaxRestarts returns the configured restart limit.
func (*Server) OnNotify ¶
func (s *Server) OnNotify(method string, handler NotificationHandler)
OnNotify registers a notification handler on the underlying client.
func (*Server) RestartCount ¶
RestartCount returns the number of restarts since the last successful start.
func (*Server) SetDiagHandler ¶
func (s *Server) SetDiagHandler(h NotificationHandler)
SetDiagHandler stores the publishDiagnostics handler for re-registration when the client is created in Start(). Called by Manager at construction.
type ServerState ¶
type ServerState int
ServerState is the lifecycle state of one LSP server instance.
const ( StateStopped ServerState = iota StateStarting StateRunning StateStopping StateError )
func (ServerState) String ¶
func (s ServerState) String() string
type Symbol ¶ added in v1.8.2
type Symbol struct {
Name string // declared name, e.g. "Manager" or "EnsureServerStarted"
Kind string // human-readable kind ("Struct", "Function", "Method", …)
Detail string // signature/detail when the server reports it (document symbols)
File string // absolute file path
Line int // 1-indexed declaration line
Container string // enclosing symbol (parent in a document-symbol tree); "" at top level
}
Symbol is a wire-type-free view of an LSP symbol — the neutral shape the repo-map builder consumes so it never imports pkg/tools/lsp/protocol. It flattens both the workspace/symbol (flat SymbolInformation) and the textDocument/documentSymbol (hierarchical DocumentSymbol) responses into one representation. Kept here, in pkg/tools/lsp, so *Manager satisfies the builder's Source interface without that interface depending on protocol.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package protocol holds hand-written LSP protocol types covering the subset of the LSP 3.17 spec needed by evva's Phase 1 operations (definition, references, hover, document symbols) plus the lifecycle handshake.
|
Package protocol holds hand-written LSP protocol types covering the subset of the LSP 3.17 spec needed by evva's Phase 1 operations (definition, references, hover, document symbols) plus the lifecycle handshake. |