summaryrefslogtreecommitdiffhomepage
path: root/internal
diff options
context:
space:
mode:
authoradamdottv <[email protected]>2025-04-30 12:20:51 -0500
committeradamdottv <[email protected]>2025-04-30 12:20:51 -0500
commited50c3678999e6b0e42bd14367b79e4348db29cf (patch)
treedc43612ea24bf8ac03c6bf86379e3a18f27023d4 /internal
parent98cf65b425014712f711cd7637def12c91f3a54b (diff)
downloadopencode-ed50c3678999e6b0e42bd14367b79e4348db29cf.tar.gz
opencode-ed50c3678999e6b0e42bd14367b79e4348db29cf.zip
fix: lsp issues with tmp and deleted files
Diffstat (limited to 'internal')
-rw-r--r--internal/app/lsp.go6
-rw-r--r--internal/lsp/client.go9
-rw-r--r--internal/lsp/watcher/watcher.go100
3 files changed, 96 insertions, 19 deletions
diff --git a/internal/app/lsp.go b/internal/app/lsp.go
index 872532fd8..934bd1a89 100644
--- a/internal/app/lsp.go
+++ b/internal/app/lsp.go
@@ -118,8 +118,14 @@ func (app *App) restartLSPClient(ctx context.Context, name string) {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
_ = oldClient.Shutdown(shutdownCtx)
cancel()
+
+ // Ensure we close the client to free resources
+ _ = oldClient.Close()
}
+ // Wait a moment before restarting to avoid rapid restart cycles
+ time.Sleep(1 * time.Second)
+
// Create a new client using the shared function
app.createAndStartLSPClient(ctx, name, clientConfig.Command, clientConfig.Args...)
logging.Info("Successfully restarted LSP client", "client", name)
diff --git a/internal/lsp/client.go b/internal/lsp/client.go
index d115b2404..355b05d30 100644
--- a/internal/lsp/client.go
+++ b/internal/lsp/client.go
@@ -627,6 +627,15 @@ func (c *Client) OpenFile(ctx context.Context, filepath string) error {
func (c *Client) NotifyChange(ctx context.Context, filepath string) error {
uri := fmt.Sprintf("file://%s", filepath)
+ // Verify file exists before attempting to read it
+ if _, err := os.Stat(filepath); err != nil {
+ if os.IsNotExist(err) {
+ // File was deleted - close it in the LSP client instead of notifying change
+ return c.CloseFile(ctx, filepath)
+ }
+ return fmt.Errorf("error checking file: %w", err)
+ }
+
content, err := os.ReadFile(filepath)
if err != nil {
return fmt.Errorf("error reading file: %w", err)
diff --git a/internal/lsp/watcher/watcher.go b/internal/lsp/watcher/watcher.go
index fd7e04837..58ad25695 100644
--- a/internal/lsp/watcher/watcher.go
+++ b/internal/lsp/watcher/watcher.go
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
+ "strconv"
"strings"
"sync"
"time"
@@ -375,20 +376,30 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
// Add new directories to the watcher
if event.Op&fsnotify.Create != 0 {
- if info, err := os.Stat(event.Name); err == nil {
- if info.IsDir() {
- // Skip excluded directories
- if !shouldExcludeDir(event.Name) {
- if err := watcher.Add(event.Name); err != nil {
- logging.Error("Error adding directory to watcher", "path", event.Name, "error", err)
- }
- }
- } else {
- // For newly created files
- if !shouldExcludeFile(event.Name) {
- w.openMatchingFile(ctx, event.Name)
+ // Check if the file/directory still exists before processing
+ info, err := os.Stat(event.Name)
+ if err != nil {
+ if os.IsNotExist(err) {
+ // File was deleted between event and processing - ignore
+ logging.Debug("File deleted between create event and stat", "path", event.Name)
+ continue
+ }
+ logging.Error("Error getting file info", "path", event.Name, "error", err)
+ continue
+ }
+
+ if info.IsDir() {
+ // Skip excluded directories
+ if !shouldExcludeDir(event.Name) {
+ if err := watcher.Add(event.Name); err != nil {
+ logging.Error("Error adding directory to watcher", "path", event.Name, "error", err)
}
}
+ } else {
+ // For newly created files
+ if !shouldExcludeFile(event.Name) {
+ w.openMatchingFile(ctx, event.Name)
+ }
}
}
@@ -643,14 +654,50 @@ func (w *WorkspaceWatcher) debounceHandleFileEvent(ctx context.Context, uri stri
func (w *WorkspaceWatcher) handleFileEvent(ctx context.Context, uri string, changeType protocol.FileChangeType) {
// If the file is open and it's a change event, use didChange notification
filePath := uri[7:] // Remove "file://" prefix
+
if changeType == protocol.FileChangeType(protocol.Deleted) {
+ // Always clear diagnostics for deleted files
w.client.ClearDiagnosticsForURI(protocol.DocumentUri(uri))
- } else if changeType == protocol.FileChangeType(protocol.Changed) && w.client.IsFileOpen(filePath) {
- err := w.client.NotifyChange(ctx, filePath)
- if err != nil {
- logging.Error("Error notifying change", "error", err)
+
+ // If the file was open, close it in the LSP client
+ if w.client.IsFileOpen(filePath) {
+ if err := w.client.CloseFile(ctx, filePath); err != nil {
+ logging.Debug("Error closing deleted file in LSP client", "file", filePath, "error", err)
+ // Continue anyway - the file is gone
+ }
+ }
+ } else if changeType == protocol.FileChangeType(protocol.Changed) {
+ // For changed files, verify the file still exists before notifying
+ if _, err := os.Stat(filePath); err != nil {
+ if os.IsNotExist(err) {
+ // File was deleted between the event and now - treat as delete
+ logging.Debug("File deleted between change event and processing", "file", filePath)
+ w.handleFileEvent(ctx, uri, protocol.FileChangeType(protocol.Deleted))
+ return
+ }
+ logging.Error("Error getting file info", "path", filePath, "error", err)
+ return
+ }
+
+ // File exists and is open, notify change
+ if w.client.IsFileOpen(filePath) {
+ err := w.client.NotifyChange(ctx, filePath)
+ if err != nil {
+ logging.Error("Error notifying change", "error", err)
+ }
+ return
+ }
+ } else if changeType == protocol.FileChangeType(protocol.Created) {
+ // For created files, verify the file still exists before notifying
+ if _, err := os.Stat(filePath); err != nil {
+ if os.IsNotExist(err) {
+ // File was deleted between the event and now - ignore
+ logging.Debug("File deleted between create event and processing", "file", filePath)
+ return
+ }
+ logging.Error("Error getting file info", "path", filePath, "error", err)
+ return
}
- return
}
// Notify LSP server about the file event using didChangeWatchedFiles
@@ -827,6 +874,11 @@ func shouldExcludeFile(filePath string) bool {
if strings.HasSuffix(filePath, "~") {
return true
}
+
+ // Skip numeric temporary files (often created by editors)
+ if _, err := strconv.Atoi(fileName); err == nil {
+ return true
+ }
// Check file size
info, err := os.Stat(filePath)
@@ -856,9 +908,19 @@ func shouldExcludeFile(filePath string) bool {
// openMatchingFile opens a file if it matches any of the registered patterns
func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
cnf := config.Get()
- // Skip directories
+ // Skip directories and verify file exists
info, err := os.Stat(path)
- if err != nil || info.IsDir() {
+ if err != nil {
+ if os.IsNotExist(err) {
+ // File was deleted between event and processing - ignore
+ logging.Debug("File deleted between event and openMatchingFile", "path", path)
+ return
+ }
+ logging.Error("Error getting file info", "path", path, "error", err)
+ return
+ }
+
+ if info.IsDir() {
return
}