on main help sign in
feat: add configurable timeout for LSP initialization (#2075)

* feat: add configurable timeout for LSP initialization Add a timeout field to LSPConfig to allow users to customize the initialization timeout for LSP servers. This is particularly useful for slow-starting servers like kotlin-language-server that may require more than the default 30 seconds to initialize. Fixes #1865 * refactor: simplify timeout logic with cmp.Or Simplified the timeout handling by using Go's cmp.Or() function instead of manual conditional checks, reducing code from 5 lines to 1 line while maintaining the same functionality.

commitea8c39f4cf3baf8f816f82c4a6bdb796a8b3750c authorhuaiyuWangh <34158348+huaiyuWangh@users.noreply.github.com> dateTue 3 Feb 2026, 20:59 (8 months ago) parente6a4896
3 files changed+17−3
internal/app/lsp.go+7−3 internal/config/config.go+1 schema.json+9
internal/app/lsp.go+7−3
@@ -1,6 +1,7 @@
11package app
22
33import (
4+ "cmp"
45 "context"
56 "log/slog"
67 "os/exec"
@@ -69,7 +70,7 @@ func (app *App) initLSPClients(ctx context.Context) {
6970 wg.Go(func() {
7071 app.createAndStartLSPClient(
7172 ctx, name,
72− toOurConfig(server),
73+ toOurConfig(server, app.config.LSP[name]),
7374 slices.Contains(userConfiguredLSPs, name),
7475 )
7576 })
@@ -83,7 +84,9 @@ func (app *App) initLSPClients(ctx context.Context) {
8384 }
8485}
8586
86−func toOurConfig(in *powernapconfig.ServerConfig) config.LSPConfig {
87+// toOurConfig merges powernap default config with user config.
88+// If user config is zero value, it means no user override exists.
89+func toOurConfig(in *powernapconfig.ServerConfig, user config.LSPConfig) config.LSPConfig {
8790 return config.LSPConfig{
8891 Command: in.Command,
8992 Args: in.Args,
@@ -92,6 +95,7 @@ func toOurConfig(in *powernapconfig.ServerConfig) config.LSPConfig {
9295 RootMarkers: in.RootMarkers,
9396 InitOptions: in.InitOptions,
9497 Options: in.Settings,
98+ Timeout: user.Timeout,
9599 }
96100}
97101
@@ -126,7 +130,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, config
126130 lspClient.SetDiagnosticsCallback(updateLSPDiagnostics)
127131
128132 // Increase initialization timeout as some servers take more time to start.
129− initCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
133+ initCtx, cancel := context.WithTimeout(ctx, time.Duration(cmp.Or(config.Timeout, 30))*time.Second)
130134 defer cancel()
131135
132136 // Initialize LSP client.
internal/config/config.go+1
@@ -194,6 +194,7 @@ type LSPConfig struct {
194194 RootMarkers []string `json:"root_markers,omitempty" jsonschema:"description=Files or directories that indicate the project root,example=go.mod,example=package.json,example=Cargo.toml"`
195195 InitOptions map[string]any `json:"init_options,omitempty" jsonschema:"description=Initialization options passed to the LSP server during initialize request"`
196196 Options map[string]any `json:"options,omitempty" jsonschema:"description=LSP server-specific settings passed during initialization"`
197+ Timeout int `json:"timeout,omitempty" jsonschema:"description=Timeout in seconds for LSP server initialization,default=30,example=60,example=120"`
197198}
198199
199200type TUIOptions struct {
schema.json+9
@@ -156,6 +156,15 @@
156156 "options": {
157157 "type": "object",
158158 "description": "LSP server-specific settings passed during initialization"
159+ },
160+ "timeout": {
161+ "type": "integer",
162+ "description": "Timeout in seconds for LSP server initialization",
163+ "default": 30,
164+ "examples": [
165+ 60,
166+ 120
167+ ]
159168 }
160169 },
161170 "additionalProperties": false,