@@ -96,19 +96,19 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
96 // Determine server type for specialized handling
96 // Determine server type for specialized handling
97 serverName := getServerNameFromContext(ctx)
97 serverName := getServerNameFromContext(ctx)
98 logging.Debug("Server type detected", "serverName", serverName)
98 logging.Debug("Server type detected", "serverName", serverName)
100 // Check if this server has sent file watchers
100 // Check if this server has sent file watchers
101 hasFileWatchers := len(watchers) > 0
101 hasFileWatchers := len(watchers) > 0
103 // For servers that need file preloading, we'll use a smart approach
103 // For servers that need file preloading, we'll use a smart approach
104 if shouldPreloadFiles(serverName) || !hasFileWatchers {
104 if shouldPreloadFiles(serverName) || !hasFileWatchers {
105 go func() {
105 go func() {
106 startTime := time.Now()
106 startTime := time.Now()
107 filesOpened := 0
107 filesOpened := 0
109 // Determine max files to open based on server type
109 // Determine max files to open based on server type
110 maxFilesToOpen := 50 // Default conservative limit
110 maxFilesToOpen := 50 // Default conservative limit
112 switch serverName {
112 switch serverName {
113 case "typescript", "typescript-language-server", "tsserver", "vtsls":
113 case "typescript", "typescript-language-server", "tsserver", "vtsls":
114 // TypeScript servers benefit from seeing more files
114 // TypeScript servers benefit from seeing more files
@@ -117,17 +117,17 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
117 // Java servers need to see many files for project model
117 // Java servers need to see many files for project model
118 maxFilesToOpen = 200
118 maxFilesToOpen = 200
121 // First, open high-priority files
121 // First, open high-priority files
122 highPriorityFilesOpened := w.openHighPriorityFiles(ctx, serverName)
122 highPriorityFilesOpened := w.openHighPriorityFiles(ctx, serverName)
123 filesOpened += highPriorityFilesOpened
123 filesOpened += highPriorityFilesOpened
125 if cnf.DebugLSP {
125 if cnf.DebugLSP {
126− logging.Debug("Opened high-priority files",
126+ logging.Debug("Opened high-priority files",
127 "count", highPriorityFilesOpened,
127 "count", highPriorityFilesOpened,
128 "serverName", serverName)
128 "serverName", serverName)
131 // If we've already opened enough high-priority files, we might not need more
131 // If we've already opened enough high-priority files, we might not need more
132 if filesOpened >= maxFilesToOpen {
132 if filesOpened >= maxFilesToOpen {
133 if cnf.DebugLSP {
133 if cnf.DebugLSP {
@@ -137,9 +137,9 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
141 // For the remaining slots, walk the directory and open matching files
141 // For the remaining slots, walk the directory and open matching files
143 err := filepath.WalkDir(w.workspacePath, func(path string, d os.DirEntry, err error) error {
143 err := filepath.WalkDir(w.workspacePath, func(path string, d os.DirEntry, err error) error {
144 if err != nil {
144 if err != nil {
145 return err
145 return err
@@ -199,10 +199,10 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
199func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName string) int {
199func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName string) int {
200 cnf := config.Get()
200 cnf := config.Get()
201 filesOpened := 0
201 filesOpened := 0
203 // Define patterns for high-priority files based on server type
203 // Define patterns for high-priority files based on server type
204 var patterns []string
204 var patterns []string
206 switch serverName {
206 switch serverName {
207 case "typescript", "typescript-language-server", "tsserver", "vtsls":
207 case "typescript", "typescript-language-server", "tsserver", "vtsls":
208 patterns = []string{
208 patterns = []string{
@@ -256,7 +256,7 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
256 "**/.editorconfig",
256 "**/.editorconfig",
260 // For each pattern, find and open matching files
260 // For each pattern, find and open matching files
261 for _, pattern := range patterns {
261 for _, pattern := range patterns {
262 // Use doublestar.Glob to find files matching the pattern (supports ** patterns)
262 // Use doublestar.Glob to find files matching the pattern (supports ** patterns)
@@ -267,17 +267,17 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
271 for _, match := range matches {
271 for _, match := range matches {
272 // Convert relative path to absolute
272 // Convert relative path to absolute
273 fullPath := filepath.Join(w.workspacePath, match)
273 fullPath := filepath.Join(w.workspacePath, match)
275 // Skip directories and excluded files
275 // Skip directories and excluded files
276 info, err := os.Stat(fullPath)
276 info, err := os.Stat(fullPath)
277 if err != nil || info.IsDir() || shouldExcludeFile(fullPath) {
277 if err != nil || info.IsDir() || shouldExcludeFile(fullPath) {
281 // Open the file
281 // Open the file
282 if err := w.client.OpenFile(ctx, fullPath); err != nil {
282 if err := w.client.OpenFile(ctx, fullPath); err != nil {
283 if cnf.DebugLSP {
283 if cnf.DebugLSP {
@@ -289,17 +289,17 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
289 logging.Debug("Opened high-priority file", "path", fullPath)
289 logging.Debug("Opened high-priority file", "path", fullPath)
293 // Add a small delay to prevent overwhelming the server
293 // Add a small delay to prevent overwhelming the server
294 time.Sleep(20 * time.Millisecond)
294 time.Sleep(20 * time.Millisecond)
296 // Limit the number of files opened per pattern
296 // Limit the number of files opened per pattern
297 if filesOpened >= 5 && (serverName != "java" && serverName != "jdtls") {
297 if filesOpened >= 5 && (serverName != "java" && serverName != "jdtls") {
303 return filesOpened
303 return filesOpened
@@ -310,16 +310,16 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
311 // Store the watcher in the context for later use
311 // Store the watcher in the context for later use
312 ctx = context.WithValue(ctx, "workspaceWatcher", w)
312 ctx = context.WithValue(ctx, "workspaceWatcher", w)
314 // If the server name isn't already in the context, try to detect it
314 // If the server name isn't already in the context, try to detect it
315 if _, ok := ctx.Value("serverName").(string); !ok {
315 if _, ok := ctx.Value("serverName").(string); !ok {
316 serverName := getServerNameFromContext(ctx)
316 serverName := getServerNameFromContext(ctx)
317 ctx = context.WithValue(ctx, "serverName", serverName)
317 ctx = context.WithValue(ctx, "serverName", serverName)
320 serverName := getServerNameFromContext(ctx)
320 serverName := getServerNameFromContext(ctx)
321 logging.Debug("Starting workspace watcher", "workspacePath", workspacePath, "serverName", serverName)
321 logging.Debug("Starting workspace watcher", "workspacePath", workspacePath, "serverName", serverName)
323 // Register handler for file watcher registrations from the server
323 // Register handler for file watcher registrations from the server
324 lsp.RegisterFileWatchHandler(func(id string, watchers []protocol.FileSystemWatcher) {
324 lsp.RegisterFileWatchHandler(func(id string, watchers []protocol.FileSystemWatcher) {
325 w.AddRegistrations(ctx, id, watchers)
325 w.AddRegistrations(ctx, id, watchers)
@@ -414,7 +414,11 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
414 case event.Op&fsnotify.Create != 0:
414 case event.Op&fsnotify.Create != 0:
415 // Already handled earlier in the event loop
415 // Already handled earlier in the event loop
416 // Just send the notification if needed
416 // Just send the notification if needed
417− info, _ := os.Stat(event.Name)
417+ info, err := os.Stat(event.Name)
419+ logging.Error("Error getting file info", "path", event.Name, "error", err)
418 if !info.IsDir() && watchKind&protocol.WatchCreate != 0 {
422 if !info.IsDir() && watchKind&protocol.WatchCreate != 0 {
419 w.debounceHandleFileEvent(ctx, uri, protocol.FileChangeType(protocol.Created))
423 w.debounceHandleFileEvent(ctx, uri, protocol.FileChangeType(protocol.Created))
@@ -682,7 +686,7 @@ func getServerNameFromContext(ctx context.Context) string {
682 if serverName, ok := ctx.Value("serverName").(string); ok && serverName != "" {
686 if serverName, ok := ctx.Value("serverName").(string); ok && serverName != "" {
683 return strings.ToLower(serverName)
687 return strings.ToLower(serverName)
686 // Otherwise, try to extract server name from the client command path
690 // Otherwise, try to extract server name from the client command path
687 if w, ok := ctx.Value("workspaceWatcher").(*WorkspaceWatcher); ok && w != nil && w.client != nil && w.client.Cmd != nil {
691 if w, ok := ctx.Value("workspaceWatcher").(*WorkspaceWatcher); ok && w != nil && w.client != nil && w.client.Cmd != nil {
688 path := strings.ToLower(w.client.Cmd.Path)
692 path := strings.ToLower(w.client.Cmd.Path)
@@ -865,7 +869,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
865 if watched, _ := w.isPathWatched(path); watched {
869 if watched, _ := w.isPathWatched(path); watched {
866 // Get server name for specialized handling
870 // Get server name for specialized handling
867 serverName := getServerNameFromContext(ctx)
871 serverName := getServerNameFromContext(ctx)
869 // Check if the file is a high-priority file that should be opened immediately
873 // Check if the file is a high-priority file that should be opened immediately
870 // This helps with project initialization for certain language servers
874 // This helps with project initialization for certain language servers
871 if isHighPriorityFile(path, serverName) {
875 if isHighPriorityFile(path, serverName) {
@@ -881,7 +885,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
881 // For non-high-priority files, we'll use different strategies based on server type
885 // For non-high-priority files, we'll use different strategies based on server type
882 if shouldPreloadFiles(serverName) {
886 if shouldPreloadFiles(serverName) {
883 // For servers that benefit from preloading, open files but with limits
887 // For servers that benefit from preloading, open files but with limits
885 // Check file size - for preloading we're more conservative
889 // Check file size - for preloading we're more conservative
886 if info.Size() > (1 * 1024 * 1024) { // 1MB limit for preloaded files
890 if info.Size() > (1 * 1024 * 1024) { // 1MB limit for preloaded files
887 if cnf.DebugLSP {
891 if cnf.DebugLSP {
@@ -889,13 +893,13 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
893 // Check file extension for common source files
897 // Check file extension for common source files
894 ext := strings.ToLower(filepath.Ext(path))
898 ext := strings.ToLower(filepath.Ext(path))
896 // Only preload source files for the specific language
900 // Only preload source files for the specific language
897 shouldOpen := false
901 shouldOpen := false
899 switch serverName {
903 switch serverName {
900 case "typescript", "typescript-language-server", "tsserver", "vtsls":
904 case "typescript", "typescript-language-server", "tsserver", "vtsls":
901 shouldOpen = ext == ".ts" || ext == ".js" || ext == ".tsx" || ext == ".jsx"
905 shouldOpen = ext == ".ts" || ext == ".js" || ext == ".tsx" || ext == ".jsx"
@@ -913,7 +917,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
913 // For unknown servers, be conservative
917 // For unknown servers, be conservative
914 shouldOpen = false
918 shouldOpen = false
917 if shouldOpen {
921 if shouldOpen {
918 // Don't need to check if it's already open - the client.OpenFile handles that
922 // Don't need to check if it's already open - the client.OpenFile handles that
919 if err := w.client.OpenFile(ctx, path); err != nil && cnf.DebugLSP {
923 if err := w.client.OpenFile(ctx, path); err != nil && cnf.DebugLSP {
@@ -943,13 +947,13 @@ func isHighPriorityFile(path string, serverName string) bool {
943 fileName == "main.js"
947 fileName == "main.js"
944 case "gopls":
948 case "gopls":
945 // For Go, we want to open go.mod files immediately
949 // For Go, we want to open go.mod files immediately
946− return fileName == "go.mod" ||
950+ return fileName == "go.mod" ||
947 fileName == "go.sum" ||
951 fileName == "go.sum" ||
948 // Also open main.go files
952 // Also open main.go files
949 fileName == "main.go"
953 fileName == "main.go"
950 case "rust-analyzer":
954 case "rust-analyzer":
951 // For Rust, we want to open Cargo.toml files immediately
955 // For Rust, we want to open Cargo.toml files immediately
952− return fileName == "Cargo.toml" ||
956+ return fileName == "Cargo.toml" ||
953 fileName == "Cargo.lock" ||
957 fileName == "Cargo.lock" ||
954 // Also open lib.rs and main.rs
958 // Also open lib.rs and main.rs
955 fileName == "lib.rs" ||
959 fileName == "lib.rs" ||