Detailed changes
@@ -23,7 +23,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, comman
slog.Info("Creating LSP client", "name", name, "command", command, "args", args)
// Create LSP client.
- lspClient, err := lsp.NewClient(ctx, command, args...)
+ lspClient, err := lsp.NewClient(ctx, app.config.WorkingDir(), command, args...)
if err != nil {
slog.Error("Failed to create LSP client for", name, err)
return
@@ -60,7 +60,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, comman
watchCtx, cancelFunc := context.WithCancel(ctx)
// Create the workspace watcher.
- workspaceWatcher := watcher.NewWorkspaceWatcher(name, lspClient)
+ workspaceWatcher := watcher.NewWorkspaceWatcher(name, lspClient, app.config.Options.DebugLSP)
// Store the cancel function to be called during cleanup.
app.watcherCancelFuncs.Append(cancelFunc)
@@ -113,7 +113,7 @@ func waitForLspDiagnostics(ctx context.Context, filePath string, lsps map[string
maps.Copy(originalDiags, client.GetDiagnostics())
handler := func(params json.RawMessage) {
- lsp.HandleDiagnostics(client, params)
+ client.HandleDiagnostics(params)
var diagParams protocol.PublishDiagnosticsParams
if err := json.Unmarshal(params, &diagParams); err != nil {
return
@@ -3,7 +3,6 @@ package lsp
import (
"bufio"
"context"
- "encoding/json"
"fmt"
"io"
"log/slog"
@@ -15,16 +14,17 @@ import (
"sync/atomic"
"time"
- "github.com/charmbracelet/crush/internal/config"
"github.com/charmbracelet/crush/internal/log"
"github.com/charmbracelet/crush/internal/lsp/protocol"
)
type Client struct {
- Cmd *exec.Cmd
- stdin io.WriteCloser
- stdout *bufio.Reader
- stderr io.ReadCloser
+ Cmd *exec.Cmd
+ stdin io.WriteCloser
+ stdout *bufio.Reader
+ stderr io.ReadCloser
+ workDir string
+ debug bool
// Request ID counter
nextID atomic.Int32
@@ -53,7 +53,7 @@ type Client struct {
serverState atomic.Value
}
-func NewClient(ctx context.Context, command string, args ...string) (*Client, error) {
+func NewClient(ctx context.Context, workDir string, command string, args ...string) (*Client, error) {
cmd := exec.CommandContext(ctx, command, args...)
// Copy env
cmd.Env = os.Environ()
@@ -78,6 +78,7 @@ func NewClient(ctx context.Context, command string, args ...string) (*Client, er
stdin: stdin,
stdout: bufio.NewReader(stdout),
stderr: stderr,
+ workDir: workDir,
handlers: make(map[int32]chan *Message),
notificationHandlers: make(map[string]NotificationHandler),
serverRequestHandlers: make(map[string]ServerRequestHandler),
@@ -215,12 +216,11 @@ func (c *Client) InitializeLSPClient(ctx context.Context, workspaceDir string) (
}
// Register handlers
- c.RegisterServerRequestHandler("workspace/applyEdit", HandleApplyEdit)
- c.RegisterServerRequestHandler("workspace/configuration", HandleWorkspaceConfiguration)
- c.RegisterServerRequestHandler("client/registerCapability", HandleRegisterCapability)
- c.RegisterNotificationHandler("window/showMessage", HandleServerMessage)
- c.RegisterNotificationHandler("textDocument/publishDiagnostics",
- func(params json.RawMessage) { HandleDiagnostics(c, params) })
+ c.RegisterServerRequestHandler("workspace/applyEdit", c.HandleApplyEdit)
+ c.RegisterServerRequestHandler("workspace/configuration", c.HandleWorkspaceConfiguration)
+ c.RegisterServerRequestHandler("client/registerCapability", c.HandleRegisterCapability)
+ c.RegisterNotificationHandler("window/showMessage", c.HandleServerMessage)
+ c.RegisterNotificationHandler("textDocument/publishDiagnostics", c.HandleDiagnostics)
// Notify the LSP server
err := c.Initialized(ctx, protocol.InitializedParams{})
@@ -287,8 +287,6 @@ func (c *Client) SetServerState(state ServerState) {
// WaitForServerReady waits for the server to be ready by polling the server
// with a simple request until it responds successfully or times out
func (c *Client) WaitForServerReady(ctx context.Context) error {
- cfg := config.Get()
-
// Set initial state
c.SetServerState(StateStarting)
@@ -300,7 +298,7 @@ func (c *Client) WaitForServerReady(ctx context.Context) error {
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Waiting for LSP server to be ready...")
}
@@ -309,7 +307,7 @@ func (c *Client) WaitForServerReady(ctx context.Context) error {
// For TypeScript-like servers, we need to open some key files first
if serverType == ServerTypeTypeScript {
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("TypeScript-like server detected, opening key configuration files")
}
c.openKeyConfigFiles(ctx)
@@ -326,7 +324,7 @@ func (c *Client) WaitForServerReady(ctx context.Context) error {
if err == nil {
// Server responded successfully
c.SetServerState(StateReady)
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("LSP server is ready")
}
return nil
@@ -334,7 +332,7 @@ func (c *Client) WaitForServerReady(ctx context.Context) error {
slog.Debug("LSP server not ready yet", "error", err, "serverType", serverType)
}
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("LSP server not ready yet", "error", err, "serverType", serverType)
}
}
@@ -377,7 +375,6 @@ func (c *Client) detectServerType() ServerType {
// openKeyConfigFiles opens important configuration files that help initialize the server
func (c *Client) openKeyConfigFiles(ctx context.Context) {
- workDir := config.Get().WorkingDir()
serverType := c.detectServerType()
var filesToOpen []string
@@ -386,22 +383,22 @@ func (c *Client) openKeyConfigFiles(ctx context.Context) {
case ServerTypeTypeScript:
// TypeScript servers need these config files to properly initialize
filesToOpen = []string{
- filepath.Join(workDir, "tsconfig.json"),
- filepath.Join(workDir, "package.json"),
- filepath.Join(workDir, "jsconfig.json"),
+ filepath.Join(c.workDir, "tsconfig.json"),
+ filepath.Join(c.workDir, "package.json"),
+ filepath.Join(c.workDir, "jsconfig.json"),
}
// Also find and open a few TypeScript files to help the server initialize
- c.openTypeScriptFiles(ctx, workDir)
+ c.openTypeScriptFiles(ctx, c.workDir)
case ServerTypeGo:
filesToOpen = []string{
- filepath.Join(workDir, "go.mod"),
- filepath.Join(workDir, "go.sum"),
+ filepath.Join(c.workDir, "go.mod"),
+ filepath.Join(c.workDir, "go.sum"),
}
case ServerTypeRust:
filesToOpen = []string{
- filepath.Join(workDir, "Cargo.toml"),
- filepath.Join(workDir, "Cargo.lock"),
+ filepath.Join(c.workDir, "Cargo.toml"),
+ filepath.Join(c.workDir, "Cargo.lock"),
}
}
@@ -470,8 +467,7 @@ func (c *Client) pingTypeScriptServer(ctx context.Context) error {
}
// If we have no open TypeScript files, try to find and open one
- workDir := config.Get().WorkingDir()
- err := filepath.WalkDir(workDir, func(path string, d os.DirEntry, err error) error {
+ err := filepath.WalkDir(c.workDir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
@@ -502,7 +498,6 @@ func (c *Client) pingTypeScriptServer(ctx context.Context) error {
// openTypeScriptFiles finds and opens TypeScript files to help initialize the server
func (c *Client) openTypeScriptFiles(ctx context.Context, workDir string) {
- cfg := config.Get()
filesOpened := 0
maxFilesToOpen := 5 // Limit to a reasonable number of files
@@ -532,7 +527,7 @@ func (c *Client) openTypeScriptFiles(ctx context.Context, workDir string) {
// Try to open the file
if err := c.OpenFile(ctx, path); err == nil {
filesOpened++
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Opened TypeScript file for initialization", "file", path)
}
}
@@ -541,11 +536,11 @@ func (c *Client) openTypeScriptFiles(ctx context.Context, workDir string) {
return nil
})
- if err != nil && cfg.Options.DebugLSP {
+ if err != nil && c.debug {
slog.Debug("Error walking directory for TypeScript files", "error", err)
}
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Opened TypeScript files for initialization", "count", filesOpened)
}
}
@@ -670,7 +665,6 @@ func (c *Client) NotifyChange(ctx context.Context, filepath string) error {
}
func (c *Client) CloseFile(ctx context.Context, filepath string) error {
- cfg := config.Get()
uri := string(protocol.URIFromPath(filepath))
c.openFilesMu.Lock()
@@ -686,7 +680,7 @@ func (c *Client) CloseFile(ctx context.Context, filepath string) error {
},
}
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Closing file", "file", filepath)
}
if err := c.Notify(ctx, "textDocument/didClose", params); err != nil {
@@ -710,7 +704,6 @@ func (c *Client) IsFileOpen(filepath string) bool {
// CloseAllFiles closes all currently open files
func (c *Client) CloseAllFiles(ctx context.Context) {
- cfg := config.Get()
c.openFilesMu.Lock()
filesToClose := make([]string, 0, len(c.openFiles))
@@ -729,12 +722,12 @@ func (c *Client) CloseAllFiles(ctx context.Context) {
// Then close them all
for _, filePath := range filesToClose {
err := c.CloseFile(ctx, filePath)
- if err != nil && cfg.Options.DebugLSP {
+ if err != nil && c.debug {
slog.Warn("Error closing file", "file", filePath, "error", err)
}
}
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Closed all files", "files", filesToClose)
}
}
@@ -792,3 +785,7 @@ func (c *Client) ClearDiagnosticsForURI(uri protocol.DocumentURI) {
defer c.diagnosticsMu.Unlock()
delete(c.diagnostics, uri)
}
+
+func (c *Client) DebugMode() {
+ c.debug = true
+}
@@ -4,19 +4,17 @@ import (
"encoding/json"
"log/slog"
- "github.com/charmbracelet/crush/internal/config"
-
"github.com/charmbracelet/crush/internal/lsp/protocol"
"github.com/charmbracelet/crush/internal/lsp/util"
)
// Requests
-func HandleWorkspaceConfiguration(params json.RawMessage) (any, error) {
+func (c *Client) HandleWorkspaceConfiguration(params json.RawMessage) (any, error) {
return []map[string]any{{}}, nil
}
-func HandleRegisterCapability(params json.RawMessage) (any, error) {
+func (c *Client) HandleRegisterCapability(params json.RawMessage) (any, error) {
var registerParams protocol.RegistrationParams
if err := json.Unmarshal(params, ®isterParams); err != nil {
slog.Error("Error unmarshaling registration params", "error", err)
@@ -47,7 +45,7 @@ func HandleRegisterCapability(params json.RawMessage) (any, error) {
return nil, nil
}
-func HandleApplyEdit(params json.RawMessage) (any, error) {
+func (c *Client) HandleApplyEdit(params json.RawMessage) (any, error) {
var edit protocol.ApplyWorkspaceEditParams
if err := json.Unmarshal(params, &edit); err != nil {
return nil, err
@@ -82,28 +80,27 @@ func notifyFileWatchRegistration(id string, watchers []protocol.FileSystemWatche
// Notifications
-func HandleServerMessage(params json.RawMessage) {
- cfg := config.Get()
+func (c *Client) HandleServerMessage(params json.RawMessage) {
var msg struct {
Type int `json:"type"`
Message string `json:"message"`
}
if err := json.Unmarshal(params, &msg); err == nil {
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Server message", "type", msg.Type, "message", msg.Message)
}
}
}
-func HandleDiagnostics(client *Client, params json.RawMessage) {
+func (c *Client) HandleDiagnostics(params json.RawMessage) {
var diagParams protocol.PublishDiagnosticsParams
if err := json.Unmarshal(params, &diagParams); err != nil {
slog.Error("Error unmarshaling diagnostics params", "error", err)
return
}
- client.diagnosticsMu.Lock()
- defer client.diagnosticsMu.Unlock()
+ c.diagnosticsMu.Lock()
+ defer c.diagnosticsMu.Unlock()
- client.diagnostics[diagParams.URI] = diagParams.Diagnostics
+ c.diagnostics[diagParams.URI] = diagParams.Diagnostics
}
@@ -8,19 +8,16 @@ import (
"io"
"log/slog"
"strings"
-
- "github.com/charmbracelet/crush/internal/config"
)
// WriteMessage writes an LSP message to the given writer
-func WriteMessage(w io.Writer, msg *Message) error {
+func WriteMessage(w io.Writer, msg *Message, debug bool) error {
data, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("failed to marshal message: %w", err)
}
- cfg := config.Get()
- if cfg.Options.DebugLSP {
+ if debug {
slog.Debug("Sending message to server", "method", msg.Method, "id", msg.ID)
}
@@ -38,8 +35,7 @@ func WriteMessage(w io.Writer, msg *Message) error {
}
// ReadMessage reads a single LSP message from the given reader
-func ReadMessage(r *bufio.Reader) (*Message, error) {
- cfg := config.Get()
+func ReadMessage(r *bufio.Reader, debug bool) (*Message, error) {
// Read headers
var contentLength int
for {
@@ -49,7 +45,7 @@ func ReadMessage(r *bufio.Reader) (*Message, error) {
}
line = strings.TrimSpace(line)
- if cfg.Options.DebugLSP {
+ if debug {
slog.Debug("Received header", "line", line)
}
@@ -65,7 +61,7 @@ func ReadMessage(r *bufio.Reader) (*Message, error) {
}
}
- if cfg.Options.DebugLSP {
+ if debug {
slog.Debug("Content-Length", "length", contentLength)
}
@@ -76,7 +72,7 @@ func ReadMessage(r *bufio.Reader) (*Message, error) {
return nil, fmt.Errorf("failed to read content: %w", err)
}
- if cfg.Options.DebugLSP {
+ if debug {
slog.Debug("Received content", "content", string(content))
}
@@ -91,11 +87,10 @@ func ReadMessage(r *bufio.Reader) (*Message, error) {
// handleMessages reads and dispatches messages in a loop
func (c *Client) handleMessages() {
- cfg := config.Get()
for {
- msg, err := ReadMessage(c.stdout)
+ msg, err := ReadMessage(c.stdout, c.debug)
if err != nil {
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Error("Error reading message", "error", err)
}
return
@@ -103,7 +98,7 @@ func (c *Client) handleMessages() {
// Handle server->client request (has both Method and ID)
if msg.Method != "" && msg.ID != 0 {
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Received request from server", "method", msg.Method, "id", msg.ID)
}
@@ -143,7 +138,7 @@ func (c *Client) handleMessages() {
}
// Send response back to server
- if err := WriteMessage(c.stdin, response); err != nil {
+ if err := WriteMessage(c.stdin, response, c.debug); err != nil {
slog.Error("Error sending response to server", "error", err)
}
@@ -157,11 +152,11 @@ func (c *Client) handleMessages() {
c.notificationMu.RUnlock()
if ok {
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Handling notification", "method", msg.Method)
}
go handler(msg.Params)
- } else if cfg.Options.DebugLSP {
+ } else if c.debug {
slog.Debug("No handler for notification", "method", msg.Method)
}
continue
@@ -174,12 +169,12 @@ func (c *Client) handleMessages() {
c.handlersMu.RUnlock()
if ok {
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Received response for request", "id", msg.ID)
}
ch <- msg
close(ch)
- } else if cfg.Options.DebugLSP {
+ } else if c.debug {
slog.Debug("No handler for response", "id", msg.ID)
}
}
@@ -188,10 +183,9 @@ func (c *Client) handleMessages() {
// Call makes a request and waits for the response
func (c *Client) Call(ctx context.Context, method string, params any, result any) error {
- cfg := config.Get()
id := c.nextID.Add(1)
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Making call", "method", method, "id", id)
}
@@ -213,11 +207,11 @@ func (c *Client) Call(ctx context.Context, method string, params any, result any
}()
// Send request
- if err := WriteMessage(c.stdin, msg); err != nil {
+ if err := WriteMessage(c.stdin, msg, c.debug); err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Request sent", "method", method, "id", id)
}
@@ -226,7 +220,7 @@ func (c *Client) Call(ctx context.Context, method string, params any, result any
case <-ctx.Done():
return ctx.Err()
case resp := <-ch:
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Received response", "id", id)
}
@@ -252,8 +246,7 @@ func (c *Client) Call(ctx context.Context, method string, params any, result any
// Notify sends a notification (a request without an ID that doesn't expect a response)
func (c *Client) Notify(ctx context.Context, method string, params any) error {
- cfg := config.Get()
- if cfg.Options.DebugLSP {
+ if c.debug {
slog.Debug("Sending notification", "method", method)
}
@@ -262,7 +255,7 @@ func (c *Client) Notify(ctx context.Context, method string, params any) error {
return fmt.Errorf("failed to create notification: %w", err)
}
- if err := WriteMessage(c.stdin, msg); err != nil {
+ if err := WriteMessage(c.stdin, msg, c.debug); err != nil {
return fmt.Errorf("failed to send notification: %w", err)
}
@@ -11,9 +11,8 @@ import (
"time"
"github.com/bmatcuk/doublestar/v4"
- "github.com/charmbracelet/crush/internal/config"
- "github.com/charmbracelet/crush/internal/csync"
+ "github.com/charmbracelet/crush/internal/csync"
"github.com/charmbracelet/crush/internal/lsp"
"github.com/charmbracelet/crush/internal/lsp/protocol"
"github.com/fsnotify/fsnotify"
@@ -23,6 +22,7 @@ import (
type WorkspaceWatcher struct {
client *lsp.Client
name string
+ debug bool
workspacePath string
debounceTime time.Duration
@@ -41,10 +41,11 @@ func init() {
}
// NewWorkspaceWatcher creates a new workspace watcher
-func NewWorkspaceWatcher(name string, client *lsp.Client) *WorkspaceWatcher {
+func NewWorkspaceWatcher(name string, client *lsp.Client, debub bool) *WorkspaceWatcher {
return &WorkspaceWatcher{
name: name,
client: client,
+ debug: debub,
debounceTime: 300 * time.Millisecond,
debounceMap: csync.NewMap[string, *time.Timer](),
registrations: []protocol.FileSystemWatcher{},
@@ -53,8 +54,6 @@ func NewWorkspaceWatcher(name string, client *lsp.Client) *WorkspaceWatcher {
// AddRegistrations adds file watchers to track
func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watchers []protocol.FileSystemWatcher) {
- cfg := config.Get()
-
slog.Debug("Adding file watcher registrations")
w.registrationMu.Lock()
defer w.registrationMu.Unlock()
@@ -63,7 +62,7 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
w.registrations = append(w.registrations, watchers...)
// Print detailed registration information for debugging
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Adding file watcher registrations",
"id", id,
"watchers", len(watchers),
@@ -132,7 +131,7 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
highPriorityFilesOpened := w.openHighPriorityFiles(ctx, serverName)
filesOpened += highPriorityFilesOpened
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Opened high-priority files",
"count", highPriorityFilesOpened,
"serverName", serverName)
@@ -140,7 +139,7 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
// If we've already opened enough high-priority files, we might not need more
if filesOpened >= maxFilesToOpen {
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Reached file limit with high-priority files",
"filesOpened", filesOpened,
"maxFiles", maxFilesToOpen)
@@ -158,7 +157,7 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
// Skip directories that should be excluded
if d.IsDir() {
if path != w.workspacePath && shouldExcludeDir(path) {
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Skipping excluded directory", "path", path)
}
return filepath.SkipDir
@@ -186,7 +185,7 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
})
elapsedTime := time.Since(startTime)
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Limited workspace scan complete",
"filesOpened", filesOpened,
"maxFiles", maxFilesToOpen,
@@ -195,11 +194,11 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
)
}
- if err != nil && cfg.Options.DebugLSP {
+ if err != nil && w.debug {
slog.Debug("Error scanning workspace for files to open", "error", err)
}
}()
- } else if cfg.Options.DebugLSP {
+ } else if w.debug {
slog.Debug("Using on-demand file loading for server", "server", serverName)
}
}
@@ -207,7 +206,6 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
// openHighPriorityFiles opens important files for the server type
// Returns the number of files opened
func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName string) int {
- cfg := config.Get()
filesOpened := 0
// Define patterns for high-priority files based on server type
@@ -275,7 +273,7 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
// Use doublestar.Glob to find files matching the pattern (supports ** patterns)
matches, err := doublestar.Glob(os.DirFS(w.workspacePath), pattern)
if err != nil {
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Error finding high-priority files", "pattern", pattern, "error", err)
}
continue
@@ -287,7 +285,7 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
// Skip directories and excluded files
info, err := os.Stat(fullPath)
- if err != nil || info.IsDir() || shouldExcludeFile(fullPath) {
+ if err != nil || info.IsDir() || shouldExcludeFile(fullPath, w.debug) {
continue
}
@@ -309,12 +307,12 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
for j := i; j < end; j++ {
fullPath := filesToOpen[j]
if err := w.client.OpenFile(ctx, fullPath); err != nil {
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Error opening high-priority file", "path", fullPath, "error", err)
}
} else {
filesOpened++
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Opened high-priority file", "path", fullPath)
}
}
@@ -331,7 +329,6 @@ func (w *WorkspaceWatcher) openHighPriorityFiles(ctx context.Context, serverName
// WatchWorkspace sets up file watching for a workspace
func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath string) {
- cfg := config.Get()
w.workspacePath = workspacePath
slog.Debug("Starting workspace watcher", "workspacePath", workspacePath, "serverName", w.name)
@@ -356,7 +353,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
// Skip excluded directories (except workspace root)
if d.IsDir() && path != workspacePath {
if shouldExcludeDir(path) {
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Skipping excluded directory", "path", path)
}
return filepath.SkipDir
@@ -401,7 +398,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
}
} else {
// For newly created files
- if !shouldExcludeFile(event.Name) {
+ if !shouldExcludeFile(event.Name, w.debug) {
w.openMatchingFile(ctx, event.Name)
}
}
@@ -409,7 +406,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
}
// Debug logging
- if cfg.Options.DebugLSP {
+ if w.debug {
matched, kind := w.isPathWatched(event.Name)
slog.Debug("File event",
"path", event.Name,
@@ -650,8 +647,6 @@ func (w *WorkspaceWatcher) debounceHandleFileEvent(ctx context.Context, uri stri
// Create new timer
w.debounceMap.Set(key, time.AfterFunc(w.debounceTime, func() {
w.handleFileEvent(ctx, uri, changeType)
-
- // Cleanup timer after execution
w.debounceMap.Del(key)
}))
}
@@ -684,8 +679,7 @@ func (w *WorkspaceWatcher) handleFileEvent(ctx context.Context, uri string, chan
// notifyFileEvent sends a didChangeWatchedFiles notification for a file event
func (w *WorkspaceWatcher) notifyFileEvent(ctx context.Context, uri string, changeType protocol.FileChangeType) error {
- cfg := config.Get()
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Notifying file event",
"uri", uri,
"changeType", changeType,
@@ -798,9 +792,8 @@ func shouldExcludeDir(dirPath string) bool {
}
// shouldExcludeFile returns true if the file should be excluded from opening
-func shouldExcludeFile(filePath string) bool {
+func shouldExcludeFile(filePath string, debug bool) bool {
fileName := filepath.Base(filePath)
- cfg := config.Get()
// Skip dot files
if strings.HasPrefix(fileName, ".") {
return true
@@ -826,12 +819,11 @@ func shouldExcludeFile(filePath string) bool {
// Skip large files
if info.Size() > maxFileSize {
- if cfg.Options.DebugLSP {
+ if debug {
slog.Debug("Skipping large file",
"path", filePath,
"size", info.Size(),
"maxSize", maxFileSize,
- "debug", cfg.Options.Debug,
"sizeMB", float64(info.Size())/(1024*1024),
"maxSizeMB", float64(maxFileSize)/(1024*1024),
)
@@ -844,7 +836,6 @@ 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) {
- cfg := config.Get()
// Skip directories
info, err := os.Stat(path)
if err != nil || info.IsDir() {
@@ -852,7 +843,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
}
// Skip excluded files
- if shouldExcludeFile(path) {
+ if shouldExcludeFile(path, w.debug) {
return
}
@@ -867,10 +858,10 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
// Check if the file is a high-priority file that should be opened immediately
// This helps with project initialization for certain language servers
if isHighPriorityFile(path, serverName) {
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Opening high-priority file", "path", path, "serverName", serverName)
}
- if err := w.client.OpenFile(ctx, path); err != nil && cfg.Options.DebugLSP {
+ if err := w.client.OpenFile(ctx, path); err != nil && w.debug {
slog.Error("Error opening high-priority file", "path", path, "error", err)
}
return
@@ -884,7 +875,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
// Check file size - for preloading we're more conservative
if info.Size() > (1 * 1024 * 1024) { // 1MB limit for preloaded files
- if cfg.Options.DebugLSP {
+ if w.debug {
slog.Debug("Skipping large file for preloading", "path", path, "size", info.Size())
}
return
@@ -912,7 +903,7 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
if shouldOpen {
// Don't need to check if it's already open - the client.OpenFile handles that
- if err := w.client.OpenFile(ctx, path); err != nil && cfg.Options.DebugLSP {
+ if err := w.client.OpenFile(ctx, path); err != nil && w.debug {
slog.Error("Error opening file", "path", path, "error", err)
}
}