Documentation
¶
Overview ¶
Package lsp is a small client for Language Server Protocol servers, built for driving generation/self-correction loops: start one persistent server, sync in-memory documents, and query diagnostics, hover, symbols, and completion.
See the sections below for usage examples, common server configurations (gopls, LuaLS), integrating the client as ds4go.ToolHandler values via the lsptool subpackage, and important caveats about the push-based diagnostics model.
Usage ¶
Start a server, open an in-memory document, edit it, and read diagnostics:
ctx := context.Background()
c, err := lsp.New(ctx, lsp.ServerConfig{Command: "gopls", RootDir: "/abs/work"})
if err != nil { return err }
defer c.Shutdown(ctx)
uri := c.URI("main.go")
_ = c.Open(ctx, uri, "go", initialSrc)
_, _ = c.Update(ctx, uri, generatedSrc)
diags, _ := c.WaitForDiagnostics(ctx, uri, 5*time.Second) // empty == clean
Common servers ¶
gopls (Go):
lsp.ServerConfig{Command: "gopls", RootDir: moduleDir}
lua-language-server (LuaLS):
lsp.ServerConfig{
Command: "lua-language-server",
RootDir: projectDir,
Settings: map[string]any{"Lua": map[string]any{
"diagnostics": map[string]any{"globals": []string{"vim"}},
}},
}
Tool loop ¶
Wrap the client as ds4go tools via the lsptool subpackage and register them on a ds4go.ToolRegistry so a model can self-correct.
Caveats ¶
- Diagnostics are push-based. WaitForDiagnostics correlates on the document version when the server reports one; otherwise it falls back to a time-settle heuristic (FirstWait then SettleWait). A pathologically slow server may publish after the wait returns. This is best-effort, not a guarantee.
- Some servers only emit diagnostics when a workspace is declared and RootDir points at real files on disk. The client always declares a workspace folder from RootDir; for full semantic diagnostics (e.g. LuaLS resolving require()) point RootDir at the real project. Pure in-memory documents reliably yield syntactic diagnostics.
- v1 does not auto-restart a crashed server. Query methods return ErrServerDown; recreate the Client to recover.
Index ¶
- Constants
- Variables
- type Client
- func (c *Client) Close(ctx context.Context, uri string) error
- func (c *Client) Completion(ctx context.Context, uri string, line, col, limit int) (labels []string, truncated int, err error)
- func (c *Client) Diagnostics(uri string) []Diagnostic
- func (c *Client) Hover(ctx context.Context, uri string, line, col int) (string, error)
- func (c *Client) OnDiagnostics(cb func(uri string, version int32, diags []Diagnostic))
- func (c *Client) Open(ctx context.Context, uri, languageID, text string) error
- func (c *Client) Shutdown(ctx context.Context) error
- func (c *Client) Symbols(ctx context.Context, uri string) ([]Symbol, error)
- func (c *Client) URI(name string) string
- func (c *Client) Update(ctx context.Context, uri, text string) (int, error)
- func (c *Client) WaitForDiagnostics(ctx context.Context, uri string, timeout time.Duration) ([]Diagnostic, error)
- type Diagnostic
- type ServerConfig
- type Severity
- type Symbol
Constants ¶
const ( // DefaultFirstWait bounds how long WaitForDiagnostics waits for the first // publish before assuming a silent server is clean. This is a tradeoff: too // low and a cold server that has not yet published its first diagnostics // gets a freshly-opened broken file reported as clean; too high and a server // that simply stays silent on clean files makes every first wait block this // long. 5s clears the cold first-publish latency of common servers (gopls, // LuaLS) for a single small file. Servers with heavier cold starts should // raise ServerConfig.FirstWait. DefaultFirstWait = 5 * time.Second DefaultSettleWait = 300 * time.Millisecond )
Defaults for the diagnostics time-settle fallback.
const DefaultShutdownTimeout = 5 * time.Second
DefaultShutdownTimeout bounds the graceful shutdown handshake when ServerConfig.ShutdownTimeout is unset.
Variables ¶
var ( // ErrServerDown is returned by query methods when the language server // process is not running. v1 does not auto-restart; recreate the Client. ErrServerDown = errors.New("lsp: language server not running") // ErrDiagnosticsTimeout is returned by WaitForDiagnostics when the wait // elapses before diagnostics settle. The returned snapshot is best-effort. ErrDiagnosticsTimeout = errors.New("lsp: timed out waiting for diagnostics") )
Sentinel errors returned by Client.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client drives a single persistent language-server subprocess with in-memory documents. It is safe for concurrent use.
func New ¶
func New(ctx context.Context, cfg ServerConfig) (*Client, error)
New launches and initializes a language server per cfg, returning a ready Client. The server command must be on PATH.
func (*Client) Completion ¶
func (c *Client) Completion(ctx context.Context, uri string, line, col, limit int) (labels []string, truncated int, err error)
Completion returns up to limit completion labels at a 1-based (line, col). A non-positive limit means no cap.
func (*Client) Diagnostics ¶
func (c *Client) Diagnostics(uri string) []Diagnostic
Diagnostics returns the latest buffered diagnostics for uri.
func (*Client) Hover ¶
Hover returns hover markdown at a 1-based (line, col), or "" if none.
The client advertises contentFormat support during initialization, so compliant servers return MarkupContent. Servers that return plain strings or MarkedStrings will surface an error from the underlying RPC layer.
func (*Client) OnDiagnostics ¶
func (c *Client) OnDiagnostics(cb func(uri string, version int32, diags []Diagnostic))
OnDiagnostics registers a callback fired on every publishDiagnostics. Pass nil to clear.
func (*Client) Symbols ¶
Symbols returns the document outline for uri. The URI is converted to a filesystem path before sending to the server because powernap's RequestDocumentSymbols accepts a filepath; callers should still use c.URI(name) to construct a valid uri argument.
func (*Client) URI ¶
URI returns the file URI this Client uses for a document name (relative names are resolved under the configured RootDir).
func (*Client) Update ¶
Update replaces a document's text (whole-document change) and notifies the server. Returns the new document version.
func (*Client) WaitForDiagnostics ¶
func (c *Client) WaitForDiagnostics(ctx context.Context, uri string, timeout time.Duration) ([]Diagnostic, error)
WaitForDiagnostics waits for diagnostics for the current version of uri, returning a best-effort snapshot. On timeout it returns the snapshot plus ErrDiagnosticsTimeout. The document must be open.
type Diagnostic ¶
Diagnostic is a single problem reported for a document. Line and Col are 1-based (LSP wire positions are 0-based; we convert at the boundary).
type ServerConfig ¶
type ServerConfig struct {
Command string // executable, looked up on PATH
Args []string // process arguments
RootDir string // workspace root (absolute path); "" allowed
InitOptions map[string]any // LSP initializationOptions
Settings map[string]any // workspace/configuration settings
Environment map[string]string // extra environment variables
// Timeout is the per-request timeout passed to the underlying RPC client.
// Zero means no timeout.
Timeout time.Duration
// ShutdownTimeout bounds the graceful shutdown handshake before the server
// is force-killed. Zero falls back to DefaultShutdownTimeout. This is
// deliberately separate from Timeout (the per-request RPC timeout) so the
// shutdown grace period can be tuned without affecting request latency.
ShutdownTimeout time.Duration
// FirstWait and SettleWait tune the time-settle fallback used by
// WaitForDiagnostics when the server does not report a document version.
// FirstWait also bounds how long WaitForDiagnostics waits for the first
// publish before assuming a silent server is clean; once the server has
// published a versioned diagnostic, the version-correlated wait honors the
// full timeout instead. Set FirstWait above your server's cold first-
// publish latency to avoid a premature "clean" result. Zero values fall
// back to defaults (DefaultFirstWait/DefaultSettleWait).
FirstWait time.Duration
SettleWait time.Duration
}
ServerConfig describes how to launch and initialize a language server.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package lsptool adapts an *lsp.Client into ds4go.ToolHandler values so a model running in a ds4go.ToolLoop can query a language server.
|
Package lsptool adapts an *lsp.Client into ds4go.ToolHandler values so a model running in a ds4go.ToolLoop can query a language server. |