lsp

package
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 24 Imported by: 0

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

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

func IsNotFoundError(err error) bool

IsNotFoundError reports whether err indicates the server binary was not found.

func Names

func Names() []tools.ToolName

Names lists every tool name this package contributes.

func NewTool

func NewTool(mgr *Manager, workDir string) *lspTool

NewTool creates an lspTool bound to the given manager.

func SuggestServerForExt

func SuggestServerForExt(ext string) string

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

func (c *Client) Close() error

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) Notify

func (c *Client) Notify(ctx context.Context, method string, params any) error

Notify sends a JSON-RPC notification (no id, no response expected).

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.

func (*Client) Process

func (c *Client) Process() *os.Process

Process returns the underlying os.Process for signal operations.

func (*Client) Request

func (c *Client) Request(ctx context.Context, method string, params any) (json.RawMessage, error)

Request sends a JSON-RPC request and blocks until the response arrives or ctx expires. Returns the raw JSON result field or an error.

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

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

func LoadConfig(projectDir, userHome string) (*LspConfig, error)

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

func NewManager(configs map[string]LspServerConfig, rootURI string, logger *slog.Logger) *Manager

NewManager creates a Manager from a list of server configs. Servers are registered but not started — they start lazily on first EnsureServerStarted.

func (*Manager) CloseFile

func (m *Manager) CloseFile(ctx context.Context, filePath string) error

CloseFile sends textDocument/didClose for a previously opened file.

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

func (m *Manager) DidChange(ctx context.Context, filePath, content string) error

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

func (m *Manager) DocumentSymbols(ctx context.Context, path string) ([]Symbol, error)

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

func (m *Manager) EnsureServerStarted(ctx context.Context, filePath string) (*Server, error)

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

func (m *Manager) FirstExtensionFor(serverName string) string

FirstExtensionFor returns the first file extension (including leading dot) mapped to the given server name, or "" if none is mapped.

func (*Manager) NotifyFileChanged

func (m *Manager) NotifyFileChanged(filePath string)

NotifyFileChanged clears diagnostics for a file. Call this after external file modifications (write, edit, bash) to invalidate stale diagnostics.

func (*Manager) OpenFile

func (m *Manager) OpenFile(ctx context.Context, filePath, content string) error

OpenFile sends textDocument/didOpen to the server responsible for filePath and tracks the open state.

func (*Manager) ServerForFile

func (m *Manager) ServerForFile(filePath string) (*Server, bool)

ServerForFile returns the server responsible for the given file path and whether one was found.

func (*Manager) Servers

func (m *Manager) Servers() []string

Servers returns all managed server names.

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) Shutdown

func (m *Manager) Shutdown(ctx context.Context) error

Shutdown stops all managed servers gracefully.

func (*Manager) WorkspaceSymbols added in v1.8.2

func (m *Manager) WorkspaceSymbols(ctx context.Context, query string) ([]Symbol, error)

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

func NewServer(name string, cfg LspServerConfig, rootURI string, logger *slog.Logger) *Server

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) IsHealthy

func (s *Server) IsHealthy() bool

IsHealthy returns true when the server is in StateRunning.

func (*Server) MaxRestarts

func (s *Server) MaxRestarts() int

MaxRestarts returns the configured restart limit.

func (*Server) Notify

func (s *Server) Notify(ctx context.Context, method string, params any) error

Notify sends a notification to the server.

func (*Server) OnNotify

func (s *Server) OnNotify(method string, handler NotificationHandler)

OnNotify registers a notification handler on the underlying client.

func (*Server) Request

func (s *Server) Request(ctx context.Context, method string, params any) (json.RawMessage, error)

Request sends a typed LSP request and returns the raw JSON result.

func (*Server) RestartCount

func (s *Server) RestartCount() int

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.

func (*Server) Start

func (s *Server) Start(ctx context.Context) error

Start spawns the LSP process, performs the initialize handshake, and transitions to StateRunning. Returns an error if the server fails to start. Thread-safe; concurrent callers must serialize via singleflight in Manager.EnsureServerStarted.

func (*Server) State

func (s *Server) State() ServerState

State returns the current server state.

func (*Server) Stop

func (s *Server) Stop(ctx context.Context) error

Stop sends shutdown → exit, then kills the process after a timeout. Idempotent — safe to call on an already-stopped server.

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.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL