@@ -21,6 +21,7 @@ import (
21// WorkspaceWatcher manages LSP file watching
21// WorkspaceWatcher manages LSP file watching
22type WorkspaceWatcher struct {
22type WorkspaceWatcher struct {
23 client *lsp.Client
23 client *lsp.Client
24 workspacePath string
25 workspacePath string
26 debounceTime time.Duration
27 debounceTime time.Duration
@@ -33,8 +34,9 @@ type WorkspaceWatcher struct {
35// NewWorkspaceWatcher creates a new workspace watcher
36// NewWorkspaceWatcher creates a new workspace watcher
36−func NewWorkspaceWatcher(client *lsp.Client) *WorkspaceWatcher {
37+func NewWorkspaceWatcher(name string, client *lsp.Client) *WorkspaceWatcher {
37 return &WorkspaceWatcher{
38 return &WorkspaceWatcher{
38 client: client,
40 client: client,
39 debounceTime: 300 * time.Millisecond,
41 debounceTime: 300 * time.Millisecond,
40 debounceMap: make(map[string]*time.Timer),
42 debounceMap: make(map[string]*time.Timer),
@@ -95,7 +97,7 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
97 // Determine server type for specialized handling
99 // Determine server type for specialized handling
98− serverName := getServerNameFromContext(ctx)
100+ serverName := w.name
99 slog.Debug("Server type detected", "serverName", serverName)
101 slog.Debug("Server type detected", "serverName", serverName)
101 // Check if this server has sent file watchers
103 // Check if this server has sent file watchers
@@ -325,17 +327,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
325 cfg := config.Get()
327 cfg := config.Get()
326 w.workspacePath = workspacePath
328 w.workspacePath = workspacePath
328− // Store the watcher in the context for later use
329− ctx = context.WithValue(ctx, "workspaceWatcher", w)
331− // If the server name isn't already in the context, try to detect it
332− if _, ok := ctx.Value("serverName").(string); !ok {
333− serverName := getServerNameFromContext(ctx)
334− ctx = context.WithValue(ctx, "serverName", serverName)
337− serverName := getServerNameFromContext(ctx)
338− slog.Debug("Starting workspace watcher", "workspacePath", workspacePath, "serverName", serverName)
330+ slog.Debug("Starting workspace watcher", "workspacePath", workspacePath, "serverName", w.name)
340 // Register handler for file watcher registrations from the server
332 // Register handler for file watcher registrations from the server
341 lsp.RegisterFileWatchHandler(func(id string, watchers []protocol.FileSystemWatcher) {
333 lsp.RegisterFileWatchHandler(func(id string, watchers []protocol.FileSystemWatcher) {
@@ -697,40 +689,6 @@ func (w *WorkspaceWatcher) notifyFileEvent(ctx context.Context, uri string, chan
697 return w.client.DidChangeWatchedFiles(ctx, params)
689 return w.client.DidChangeWatchedFiles(ctx, params)
700−// getServerNameFromContext extracts the server name from the context
701−// This is a best-effort function that tries to identify which LSP server we're dealing with
702−func getServerNameFromContext(ctx context.Context) string {
703− // First check if the server name is directly stored in the context
704− if serverName, ok := ctx.Value("serverName").(string); ok && serverName != "" {
705− return strings.ToLower(serverName)
708− // Otherwise, try to extract server name from the client command path
709− if w, ok := ctx.Value("workspaceWatcher").(*WorkspaceWatcher); ok && w != nil && w.client != nil && w.client.Cmd != nil {
710− path := strings.ToLower(w.client.Cmd.Path)
712− // Extract server name from path
713− if strings.Contains(path, "typescript") || strings.Contains(path, "tsserver") || strings.Contains(path, "vtsls") {
715− } else if strings.Contains(path, "gopls") {
717− } else if strings.Contains(path, "rust-analyzer") {
718− return "rust-analyzer"
719− } else if strings.Contains(path, "pyright") || strings.Contains(path, "pylsp") || strings.Contains(path, "python") {
721− } else if strings.Contains(path, "clangd") {
723− } else if strings.Contains(path, "jdtls") || strings.Contains(path, "java") {
727− // Return the base name as fallback
728− return filepath.Base(path)
734// shouldPreloadFiles determines if we should preload files for a specific language server
692// shouldPreloadFiles determines if we should preload files for a specific language server
735// Some servers work better with preloaded files, others don't need it
693// Some servers work better with preloaded files, others don't need it
736func shouldPreloadFiles(serverName string) bool {
694func shouldPreloadFiles(serverName string) bool {
@@ -884,64 +842,63 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
886 // Check if this path should be watched according to server registrations
844 // Check if this path should be watched according to server registrations
887− if watched, _ := w.isPathWatched(path); watched {
845+ if watched, _ := w.isPathWatched(path); !watched {
888− // Get server name for specialized handling
846+ return
889− serverName := getServerNameFromContext(ctx)
847+ }
891− // Check if the file is a high-priority file that should be opened immediately
849+ serverName := w.name
892− // This helps with project initialization for certain language servers
893− if isHighPriorityFile(path, serverName) {
894− if cfg.Options.DebugLSP {
895− slog.Debug("Opening high-priority file", "path", path, "serverName", serverName)
897− if err := w.client.OpenFile(ctx, path); err != nil && cfg.Options.DebugLSP {
898− slog.Error("Error opening high-priority file", "path", path, "error", err)
903− // For non-high-priority files, we'll use different strategies based on server type
851+ // Get server name for specialized handling
904− if shouldPreloadFiles(serverName) {
852+ // Check if the file is a high-priority file that should be opened immediately
905− // For servers that benefit from preloading, open files but with limits
853+ // This helps with project initialization for certain language servers
854+ if isHighPriorityFile(path, serverName) {
855+ if cfg.Options.DebugLSP {
856+ slog.Debug("Opening high-priority file", "path", path, "serverName", serverName)
858+ if err := w.client.OpenFile(ctx, path); err != nil && cfg.Options.DebugLSP {
859+ slog.Error("Error opening high-priority file", "path", path, "error", err)
907− // Check file size - for preloading we're more conservative
864+ // For non-high-priority files, we'll use different strategies based on server type
908− if info.Size() > (1 * 1024 * 1024) { // 1MB limit for preloaded files
865+ if !shouldPreloadFiles(serverName) {
909− if cfg.Options.DebugLSP {
910− slog.Debug("Skipping large file for preloading", "path", path, "size", info.Size())
868+ // For servers that benefit from preloading, open files but with limits
915− // Check file extension for common source files
870+ // Check file size - for preloading we're more conservative
916− ext := strings.ToLower(filepath.Ext(path))
871+ if info.Size() > (1 * 1024 * 1024) { // 1MB limit for preloaded files
872+ if cfg.Options.DebugLSP {
873+ slog.Debug("Skipping large file for preloading", "path", path, "size", info.Size())
918− // Only preload source files for the specific language
878+ // Check file extension for common source files
919− shouldOpen := false
879+ ext := strings.ToLower(filepath.Ext(path))
881+ // Only preload source files for the specific language
921− switch serverName {
883+ switch serverName {
922− case "typescript", "typescript-language-server", "tsserver", "vtsls":
884+ case "typescript", "typescript-language-server", "tsserver", "vtsls":
923− shouldOpen = ext == ".ts" || ext == ".js" || ext == ".tsx" || ext == ".jsx"
885+ shouldOpen = ext == ".ts" || ext == ".js" || ext == ".tsx" || ext == ".jsx"
924− case "gopls":
886+ case "gopls":
925− shouldOpen = ext == ".go"
887+ shouldOpen = ext == ".go"
926− case "rust-analyzer":
888+ case "rust-analyzer":
927− shouldOpen = ext == ".rs"
889+ shouldOpen = ext == ".rs"
928− case "python", "pyright", "pylsp":
890+ case "python", "pyright", "pylsp":
929− shouldOpen = ext == ".py"
891+ shouldOpen = ext == ".py"
930− case "clangd":
892+ case "clangd":
931− shouldOpen = ext == ".c" || ext == ".cpp" || ext == ".h" || ext == ".hpp"
893+ shouldOpen = ext == ".c" || ext == ".cpp" || ext == ".h" || ext == ".hpp"
932− case "java", "jdtls":
894+ case "java", "jdtls":
933− shouldOpen = ext == ".java"
895+ shouldOpen = ext == ".java"
935− // For unknown servers, be conservative
939− if shouldOpen {
898+ if shouldOpen {
940− // Don't need to check if it's already open - the client.OpenFile handles that
899+ // Don't need to check if it's already open - the client.OpenFile handles that
941− if err := w.client.OpenFile(ctx, path); err != nil && cfg.Options.DebugLSP {
900+ if err := w.client.OpenFile(ctx, path); err != nil && cfg.Options.DebugLSP {
942− slog.Error("Error opening file", "path", path, "error", err)
901+ slog.Error("Error opening file", "path", path, "error", err)