summaryrefslogtreecommitdiffhomepage
path: root/internal/app/lsp.go
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-04-13 13:17:17 +0200
committerKujtim Hoxha <[email protected]>2025-04-21 13:41:25 +0200
commit3ad983db0f2c08826d56cb5de274d706c95b3353 (patch)
tree3151e7f361dc2b468429791d581eb5f3d658f84f /internal/app/lsp.go
parent5601466fe1610b777895682050b1b458f80c0ac8 (diff)
downloadopencode-3ad983db0f2c08826d56cb5de274d706c95b3353.tar.gz
opencode-3ad983db0f2c08826d56cb5de274d706c95b3353.zip
cleanup app, config and root
Diffstat (limited to 'internal/app/lsp.go')
-rw-r--r--internal/app/lsp.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/internal/app/lsp.go b/internal/app/lsp.go
new file mode 100644
index 000000000..4e0568f07
--- /dev/null
+++ b/internal/app/lsp.go
@@ -0,0 +1,108 @@
+package app
+
+import (
+ "context"
+ "time"
+
+ "github.com/kujtimiihoxha/termai/internal/config"
+ "github.com/kujtimiihoxha/termai/internal/logging"
+ "github.com/kujtimiihoxha/termai/internal/lsp"
+ "github.com/kujtimiihoxha/termai/internal/lsp/watcher"
+)
+
+func (app *App) initLSPClients(ctx context.Context) {
+ cfg := config.Get()
+
+ // Initialize LSP clients
+ for name, clientConfig := range cfg.LSP {
+ app.createAndStartLSPClient(ctx, name, clientConfig.Command, clientConfig.Args...)
+ }
+}
+
+// createAndStartLSPClient creates a new LSP client, initializes it, and starts its workspace watcher
+func (app *App) createAndStartLSPClient(ctx context.Context, name string, command string, args ...string) {
+ // Create a specific context for initialization with a timeout
+ initCtx, initCancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer initCancel()
+
+ // Create the LSP client
+ lspClient, err := lsp.NewClient(initCtx, command, args...)
+ if err != nil {
+ logging.Error("Failed to create LSP client for", name, err)
+ return
+ }
+
+ // Initialize with the initialization context
+ _, err = lspClient.InitializeLSPClient(initCtx, config.WorkingDirectory())
+ if err != nil {
+ logging.Error("Initialize failed", "name", name, "error", err)
+ // Clean up the client to prevent resource leaks
+ lspClient.Close()
+ return
+ }
+
+ // Create a child context that can be canceled when the app is shutting down
+ watchCtx, cancelFunc := context.WithCancel(ctx)
+ workspaceWatcher := watcher.NewWorkspaceWatcher(lspClient)
+
+ // Store the cancel function to be called during cleanup
+ app.cancelFuncsMutex.Lock()
+ app.watcherCancelFuncs = append(app.watcherCancelFuncs, cancelFunc)
+ app.cancelFuncsMutex.Unlock()
+
+ // Add the watcher to a WaitGroup to track active goroutines
+ app.watcherWG.Add(1)
+
+ // Add to map with mutex protection before starting goroutine
+ app.clientsMutex.Lock()
+ app.LSPClients[name] = lspClient
+ app.clientsMutex.Unlock()
+
+ go app.runWorkspaceWatcher(watchCtx, name, workspaceWatcher)
+}
+
+// runWorkspaceWatcher executes the workspace watcher for an LSP client
+func (app *App) runWorkspaceWatcher(ctx context.Context, name string, workspaceWatcher *watcher.WorkspaceWatcher) {
+ defer app.watcherWG.Done()
+ defer func() {
+ if r := recover(); r != nil {
+ logging.Error("LSP client crashed", "client", name, "panic", r)
+
+ // Try to restart the client
+ app.restartLSPClient(ctx, name)
+ }
+ }()
+
+ workspaceWatcher.WatchWorkspace(ctx, config.WorkingDirectory())
+ logging.Info("Workspace watcher stopped", "client", name)
+}
+
+// restartLSPClient attempts to restart a crashed or failed LSP client
+func (app *App) restartLSPClient(ctx context.Context, name string) {
+ // Get the original configuration
+ cfg := config.Get()
+ clientConfig, exists := cfg.LSP[name]
+ if !exists {
+ logging.Error("Cannot restart client, configuration not found", "client", name)
+ return
+ }
+
+ // Clean up the old client if it exists
+ app.clientsMutex.Lock()
+ oldClient, exists := app.LSPClients[name]
+ if exists {
+ delete(app.LSPClients, name) // Remove from map before potentially slow shutdown
+ }
+ app.clientsMutex.Unlock()
+
+ if exists && oldClient != nil {
+ // Try to shut it down gracefully, but don't block on errors
+ shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ _ = oldClient.Shutdown(shutdownCtx)
+ cancel()
+ }
+
+ // Create a new client using the shared function
+ app.createAndStartLSPClient(ctx, name, clientConfig.Command, clientConfig.Args...)
+ logging.Info("Successfully restarted LSP client", "client", name)
+}