Documentation
¶
Overview ¶
Package lsp is a from-scratch Language Server Protocol client — the way Kram gets real semantic navigation (diagnostics, go-to-definition, find-references) instead of only grep/glob's text matching. Like internal/mcp, this is hand-rolled JSON-RPC 2.0 rather than an SDK dependency, but the wire framing is different: LSP servers speak `Content-Length: N\r\n\r\n<json>` over stdio, not MCP's newline-delimited JSON, so it needs its own transport rather than reusing internal/mcp's.
This implements one vertical slice, not the whole protocol: initialize, textDocument/didOpen (+didClose, to reopen cleanly on every query), textDocument/publishDiagnostics, textDocument/definition and textDocument/references. No code actions, no rename, no call hierarchy, hover is not implemented.
Index ¶
- Constants
- type Client
- type Diagnostic
- type Location
- type Manager
- func (m *Manager) ClientFor(ctx context.Context, relPath string) (*Client, error)
- func (m *Manager) Close()
- func (m *Manager) Definition(ctx context.Context, relPath string, line, character int) ([]Location, error)
- func (m *Manager) Diagnostics(ctx context.Context, relPath string) ([]Diagnostic, error)
- func (m *Manager) DisplayPath(uri string) string
- func (m *Manager) References(ctx context.Context, relPath string, line, character int) ([]Location, error)
- type Position
- type Range
Constants ¶
const ( SeverityError = 1 SeverityWarning = 2 SeverityInformation = 3 SeverityHint = 4 )
Diagnostic severities, per the LSP spec (textDocument/publishDiagnostics).
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is one live connection to one language server process. Every exported method is safe to call from multiple goroutines.
type Diagnostic ¶
type Diagnostic struct {
Range Range `json:"range"`
Severity int `json:"severity,omitempty"`
Code any `json:"code,omitempty"`
Source string `json:"source,omitempty"`
Message string `json:"message"`
}
Diagnostic is one server-reported problem with a file.
func (Diagnostic) SeverityLabel ¶
func (d Diagnostic) SeverityLabel() string
SeverityLabel renders a diagnostic's severity as short lowercase text ("error", "warning", "info", "hint") for display in tool output.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns every language server connection for one workspace, one per language, started lazily on first use. Nothing here touches a filesystem or spawns a process at construction time — the extension table itself is built lazily on first ClientFor call — so building a Manager at daemon startup (as tools.NewRegistry does) costs nothing and starts nothing, satisfying the "no server starts before it's actually asked for" invariant.
func NewManager ¶
NewManager builds a manager scoped to workspace. Cheap and side-effect free: no config file is read and no process is started until the first ClientFor call.
func (*Manager) ClientFor ¶
ClientFor returns the language server connection that owns relPath's extension, starting it if this is the first request for that language in this workspace. Concurrent callers requesting the same language before it's finished starting all wait on the same in-flight attempt rather than racing to start it twice.
A missing extension mapping, a missing/unusable server binary, or a failed initialize handshake all come back as a plain error — this method never panics and never brings down the caller; it's the tool layer's job to turn that error into "LSP capability unavailable for language X: <reason>" text for the model, per Kram's rule that a third-party tool being broken costs only its own capability.
func (*Manager) Close ¶
func (m *Manager) Close()
Close shuts down every language server this manager started (including any still mid-startup — Close waits for those to finish before closing them, so nothing is left running as an orphan after the daemon exits).
func (*Manager) Definition ¶
func (m *Manager) Definition(ctx context.Context, relPath string, line, character int) ([]Location, error)
Definition resolves the symbol at a 0-indexed line/character in relPath.
func (*Manager) Diagnostics ¶
Diagnostics returns whatever the language server has published for relPath after (re)opening it fresh.
func (*Manager) DisplayPath ¶
DisplayPath converts a Location's URI to a path suitable for showing the model: workspace-relative when the location is inside the workspace (the common case), an absolute path when it isn't (e.g. a definition resolving into GOROOT or node_modules), and the raw URI as a last resort if it isn't even a file:// URI.
type Position ¶
Position is 0-indexed on both axes, the native LSP convention: line 0 is the file's first line, character 0 is the first column. Kram's LSP tools intentionally keep this convention rather than translating to 1-indexed, and document it in each tool's description instead — see internal/daemon/tools/lsp.go.