@@ -3,7 +3,6 @@ package watcher
8 "path/filepath"
7 "path/filepath"
@@ -11,11 +10,13 @@ import (
13 "github.com/fsnotify/fsnotify"
12 "github.com/fsnotify/fsnotify"
13+ "github.com/kujtimiihoxha/termai/internal/config"
14+ "github.com/kujtimiihoxha/termai/internal/logging"
14 "github.com/kujtimiihoxha/termai/internal/lsp"
15 "github.com/kujtimiihoxha/termai/internal/lsp"
15 "github.com/kujtimiihoxha/termai/internal/lsp/protocol"
16 "github.com/kujtimiihoxha/termai/internal/lsp/protocol"
18−var debug = false // Force debug logging on
19+var logger = logging.Get()
20// WorkspaceWatcher manages LSP file watching
21// WorkspaceWatcher manages LSP file watching
21type WorkspaceWatcher struct {
22type WorkspaceWatcher struct {
@@ -43,6 +44,7 @@ func NewWorkspaceWatcher(client *lsp.Client) *WorkspaceWatcher {
44// AddRegistrations adds file watchers to track
45// AddRegistrations adds file watchers to track
45func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watchers []protocol.FileSystemWatcher) {
46func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watchers []protocol.FileSystemWatcher) {
46 w.registrationMu.Lock()
48 w.registrationMu.Lock()
47 defer w.registrationMu.Unlock()
49 defer w.registrationMu.Unlock()
@@ -50,31 +52,35 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
50 w.registrations = append(w.registrations, watchers...)
52 w.registrations = append(w.registrations, watchers...)
52 // Print detailed registration information for debugging
54 // Print detailed registration information for debugging
53− if debug {
55+ if cnf.Debug {
54− log.Printf("Added %d file watcher registrations (id: %s), total: %d",
56+ logger.Debug("Adding file watcher registrations",
58+ "watchers", len(watchers),
55− len(watchers), id, len(w.registrations))
59+ "total", len(w.registrations),
60+ "watchers", watchers,
57 for i, watcher := range watchers {
63 for i, watcher := range watchers {
58− log.Printf("Registration #%d raw data:", i+1)
64+ logger.Debug("Registration", "index", i+1)
60 // Log the GlobPattern
66 // Log the GlobPattern
61 switch v := watcher.GlobPattern.Value.(type) {
67 switch v := watcher.GlobPattern.Value.(type) {
62 case string:
68 case string:
63− log.Printf(" GlobPattern: string pattern '%s'", v)
69+ logger.Debug("GlobPattern", "pattern", v)
64 case protocol.RelativePattern:
70 case protocol.RelativePattern:
65− log.Printf(" GlobPattern: RelativePattern with pattern '%s'", v.Pattern)
71+ logger.Debug("GlobPattern", "pattern", v.Pattern)
67 // Log BaseURI details
73 // Log BaseURI details
68 switch u := v.BaseURI.Value.(type) {
74 switch u := v.BaseURI.Value.(type) {
69 case string:
75 case string:
70− log.Printf(" BaseURI: string '%s'", u)
76+ logger.Debug("BaseURI", "baseURI", u)
71 case protocol.DocumentUri:
77 case protocol.DocumentUri:
72− log.Printf(" BaseURI: DocumentUri '%s'", u)
78+ logger.Debug("BaseURI", "baseURI", u)
74− log.Printf(" BaseURI: unknown type %T", u)
80+ logger.Debug("BaseURI", "baseURI", u)
77− log.Printf(" GlobPattern: unknown type %T", v)
83+ logger.Debug("GlobPattern", "unknown type", fmt.Sprintf("%T", v))
80 // Log WatchKind
86 // Log WatchKind
@@ -82,11 +88,8 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
82 if watcher.Kind != nil {
88 if watcher.Kind != nil {
83 watchKind = *watcher.Kind
89 watchKind = *watcher.Kind
85− log.Printf(" WatchKind: %d (Create:%v, Change:%v, Delete:%v)",
91+
86− watchKind,
92+ logger.Debug("WatchKind", "kind", watchKind)
87− watchKind&protocol.WatchCreate != 0,
88− watchKind&protocol.WatchChange != 0,
89− watchKind&protocol.WatchDelete != 0)
91 // Test match against some example paths
94 // Test match against some example paths
92 testPaths := []string{
95 testPaths := []string{
@@ -96,7 +99,7 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
97 for _, testPath := range testPaths {
100 for _, testPath := range testPaths {
98 isMatch := w.matchesPattern(testPath, watcher.GlobPattern)
101 isMatch := w.matchesPattern(testPath, watcher.GlobPattern)
99− log.Printf(" Test path '%s': %v", testPath, isMatch)
102+ logger.Debug("Test path", "path", testPath, "matches", isMatch)
@@ -114,10 +117,9 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
115 // Skip directories that should be excluded
118 // Skip directories that should be excluded
116 if d.IsDir() {
119 if d.IsDir() {
118 if path != w.workspacePath && shouldExcludeDir(path) {
120 if path != w.workspacePath && shouldExcludeDir(path) {
119− if debug {
121+ if cnf.Debug {
120− log.Printf("Skipping excluded directory!!: %s", path)
122+ logger.Debug("Skipping excluded directory", "path", path)
122 return filepath.SkipDir
124 return filepath.SkipDir
@@ -136,18 +138,23 @@ func (w *WorkspaceWatcher) AddRegistrations(ctx context.Context, id string, watc
138 elapsedTime := time.Since(startTime)
140 elapsedTime := time.Since(startTime)
139− if debug {
141+ if cnf.Debug {
140− log.Printf("Workspace scan complete: processed %d files in %.2f seconds", filesOpened, elapsedTime.Seconds())
142+ logger.Debug("Workspace scan complete",
143+ "filesOpened", filesOpened,
144+ "elapsedTime", elapsedTime.Seconds(),
145+ "workspacePath", w.workspacePath,
143− if err != nil && debug {
149+ if err != nil && cnf.Debug {
144− log.Printf("Error scanning workspace for files to open: %v", err)
150+ logger.Debug("Error scanning workspace for files to open", "error", err)
149// WatchWorkspace sets up file watching for a workspace
155// WatchWorkspace sets up file watching for a workspace
150func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath string) {
156func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath string) {
151 w.workspacePath = workspacePath
158 w.workspacePath = workspacePath
153 // Register handler for file watcher registrations from the server
160 // Register handler for file watcher registrations from the server
@@ -157,7 +164,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
158 watcher, err := fsnotify.NewWatcher()
165 watcher, err := fsnotify.NewWatcher()
159 if err != nil {
166 if err != nil {
160− log.Fatalf("Error creating watcher: %v", err)
167+ logger.Error("Error creating watcher", "error", err)
162 defer watcher.Close()
169 defer watcher.Close()
@@ -170,8 +177,8 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
170 // Skip excluded directories (except workspace root)
177 // Skip excluded directories (except workspace root)
171 if d.IsDir() && path != workspacePath {
178 if d.IsDir() && path != workspacePath {
172 if shouldExcludeDir(path) {
179 if shouldExcludeDir(path) {
173− if debug {
180+ if cnf.Debug {
174− log.Printf("Skipping watching excluded directory: %s", path)
181+ logger.Debug("Skipping excluded directory", "path", path)
176 return filepath.SkipDir
183 return filepath.SkipDir
@@ -181,14 +188,14 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
181 if d.IsDir() {
188 if d.IsDir() {
182 err = watcher.Add(path)
189 err = watcher.Add(path)
183 if err != nil {
190 if err != nil {
184− log.Printf("Error watching path %s: %v", path, err)
191+ logger.Error("Error watching path", "path", path, "error", err)
188 return nil
195 return nil
190 if err != nil {
197 if err != nil {
191− log.Fatalf("Error walking workspace: %v", err)
198+ logger.Error("Error walking workspace", "error", err)
194 // Event loop
201 // Event loop
@@ -210,7 +217,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
210 // Skip excluded directories
217 // Skip excluded directories
211 if !shouldExcludeDir(event.Name) {
218 if !shouldExcludeDir(event.Name) {
212 if err := watcher.Add(event.Name); err != nil {
219 if err := watcher.Add(event.Name); err != nil {
213− log.Printf("Error watching new directory: %v", err)
220+ logger.Error("Error adding directory to watcher", "path", event.Name, "error", err)
@@ -223,10 +230,15 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
225 // Debug logging
232 // Debug logging
226− if debug {
233+ if cnf.Debug {
227 matched, kind := w.isPathWatched(event.Name)
234 matched, kind := w.isPathWatched(event.Name)
228− log.Printf("Event: %s, Op: %s, Watched: %v, Kind: %d",
235+ logger.Debug("File event",
229− event.Name, event.Op.String(), matched, kind)
237+ "operation", event.Op.String(),
232 // Check if this path should be watched according to server registrations
244 // Check if this path should be watched according to server registrations
@@ -265,7 +277,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str
268− log.Printf("Watcher error: %v\n", err)
280+ logger.Error("Error watching file", "error", err)
@@ -390,7 +402,7 @@ func matchesSimpleGlob(pattern, path string) bool {
390 // Fall back to simple matching for simpler patterns
402 // Fall back to simple matching for simpler patterns
391 matched, err := filepath.Match(pattern, path)
403 matched, err := filepath.Match(pattern, path)
392 if err != nil {
404 if err != nil {
393− log.Printf("Error matching pattern %s: %v", pattern, err)
405+ logger.Error("Error matching pattern", "pattern", pattern, "path", path, "error", err)
394 return false
406 return false
@@ -401,7 +413,7 @@ func matchesSimpleGlob(pattern, path string) bool {
401func (w *WorkspaceWatcher) matchesPattern(path string, pattern protocol.GlobPattern) bool {
413func (w *WorkspaceWatcher) matchesPattern(path string, pattern protocol.GlobPattern) bool {
402 patternInfo, err := pattern.AsPattern()
414 patternInfo, err := pattern.AsPattern()
403 if err != nil {
415 if err != nil {
404− log.Printf("Error parsing pattern: %v", err)
416+ logger.Error("Error parsing pattern", "pattern", pattern, "error", err)
405 return false
417 return false
@@ -426,7 +438,7 @@ func (w *WorkspaceWatcher) matchesPattern(path string, pattern protocol.GlobPatt
426 // Make path relative to basePath for matching
438 // Make path relative to basePath for matching
427 relPath, err := filepath.Rel(basePath, path)
439 relPath, err := filepath.Rel(basePath, path)
428 if err != nil {
440 if err != nil {
429− log.Printf("Error getting relative path for %s: %v", path, err)
441+ logger.Error("Error getting relative path", "path", path, "basePath", basePath, "error", err)
430 return false
442 return false
432 relPath = filepath.ToSlash(relPath)
444 relPath = filepath.ToSlash(relPath)
@@ -467,21 +479,25 @@ func (w *WorkspaceWatcher) handleFileEvent(ctx context.Context, uri string, chan
467 if changeType == protocol.FileChangeType(protocol.Changed) && w.client.IsFileOpen(filePath) {
479 if changeType == protocol.FileChangeType(protocol.Changed) && w.client.IsFileOpen(filePath) {
468 err := w.client.NotifyChange(ctx, filePath)
480 err := w.client.NotifyChange(ctx, filePath)
469 if err != nil {
481 if err != nil {
470− log.Printf("Error notifying change: %v", err)
482+ logger.Error("Error notifying change", "error", err)
475 // Notify LSP server about the file event using didChangeWatchedFiles
487 // Notify LSP server about the file event using didChangeWatchedFiles
476 if err := w.notifyFileEvent(ctx, uri, changeType); err != nil {
488 if err := w.notifyFileEvent(ctx, uri, changeType); err != nil {
477− log.Printf("Error notifying LSP server about file event: %v", err)
489+ logger.Error("Error notifying LSP server about file event", "error", err)
481// notifyFileEvent sends a didChangeWatchedFiles notification for a file event
493// notifyFileEvent sends a didChangeWatchedFiles notification for a file event
482func (w *WorkspaceWatcher) notifyFileEvent(ctx context.Context, uri string, changeType protocol.FileChangeType) error {
494func (w *WorkspaceWatcher) notifyFileEvent(ctx context.Context, uri string, changeType protocol.FileChangeType) error {
483− if debug {
495+ cnf := config.Get()
484− log.Printf("Notifying file event: %s (type: %d)", uri, changeType)
497+ logger.Debug("Notifying file event",
499+ "changeType", changeType,
487 params := protocol.DidChangeWatchedFilesParams{
503 params := protocol.DidChangeWatchedFilesParams{
@@ -575,7 +591,7 @@ func shouldExcludeDir(dirPath string) bool {
575// shouldExcludeFile returns true if the file should be excluded from opening
591// shouldExcludeFile returns true if the file should be excluded from opening
576func shouldExcludeFile(filePath string) bool {
592func shouldExcludeFile(filePath string) bool {
577 fileName := filepath.Base(filePath)
593 fileName := filepath.Base(filePath)
578−
594+ cnf := config.Get()
579 // Skip dot files
595 // Skip dot files
580 if strings.HasPrefix(fileName, ".") {
596 if strings.HasPrefix(fileName, ".") {
581 return true
597 return true
@@ -601,8 +617,15 @@ func shouldExcludeFile(filePath string) bool {
602 // Skip large files
618 // Skip large files
603 if info.Size() > maxFileSize {
619 if info.Size() > maxFileSize {
604− if debug {
620+ if cnf.Debug {
621+ logger.Debug("Skipping large file",
623+ "size", info.Size(),
624+ "maxSize", maxFileSize,
605− log.Printf("Skipping large file: %s (%.2f MB)", filePath, float64(info.Size())/(1024*1024))
626+ "sizeMB", float64(info.Size())/(1024*1024),
627+ "maxSizeMB", float64(maxFileSize)/(1024*1024),
607 return true
630 return true
@@ -612,6 +635,7 @@ func shouldExcludeFile(filePath string) bool {
613// openMatchingFile opens a file if it matches any of the registered patterns
636// openMatchingFile opens a file if it matches any of the registered patterns
614func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
637func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
615 // Skip directories
639 // Skip directories
616 info, err := os.Stat(path)
640 info, err := os.Stat(path)
617 if err != nil || info.IsDir() {
641 if err != nil || info.IsDir() {
@@ -626,8 +650,8 @@ func (w *WorkspaceWatcher) openMatchingFile(ctx context.Context, path string) {
626 // Check if this path should be watched according to server registrations
650 // Check if this path should be watched according to server registrations
627 if watched, _ := w.isPathWatched(path); watched {
651 if watched, _ := w.isPathWatched(path); watched {
628 // Don't need to check if it's already open - the client.OpenFile handles that
652 // Don't need to check if it's already open - the client.OpenFile handles that
629− if err := w.client.OpenFile(ctx, path); err != nil && debug {
653+ if err := w.client.OpenFile(ctx, path); err != nil && cnf.Debug {
630− log.Printf("Error opening file %s: %v", path, err)
654+ logger.Error("Error opening file", "path", path, "error", err)