@@ -112,8 +112,8 @@ require (
github.com/ncruces/julianday v1.0.0 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/raphamorim/notify v0.9.3
github.com/rivo/uniseg v0.4.7
- github.com/rjeczalik/notify v0.9.3
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
@@ -86,7 +86,7 @@ github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqI
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674 h1:+Cz+VfxD5DO+JT1LlswXWhre0HYLj6l2HW8HVGfMuC0=
github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674/go.mod h1:9gCUAHmVx5BwSafeyNr3GI0GgvlB1WYjL21SkPp1jyU=
-github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018 h1:PU4Zvpagsk5sgaDxn5W4sxHuLp9QRMBZB3bFSk40A4w=
+github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018 h1:5KgReOUbYf1O8+dIiGF0JVirb5NJNjE0gLQMwxDJap4=
github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018/go.mod h1:Z/GLmp9fzaqX4ze3nXG7StgWez5uBM5XtlLHK8V/qSk=
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250721205738-ea66aa652ee0 h1:sWRGoSw/JsO2S4t2+fmmEkRbkOxphI0AxZkQPQVKWbs=
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250721205738-ea66aa652ee0/go.mod h1:XIuqKpZTUXtVyeyiN1k9Tc/U7EzfaDnVc34feFHfBws=
@@ -94,7 +94,7 @@ github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 h1:WkwO6Ks3mS
github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706/go.mod h1:mjJGp00cxcfvD5xdCa+bso251Jt4owrQvuimJtVmEmM=
github.com/charmbracelet/ultraviolet v0.0.0-20250912143111-9785ff826cbf h1:2fs3BT8BFjpJ4134Tq4VoBm/fE9FB2f2P/FhmzsWelQ=
github.com/charmbracelet/ultraviolet v0.0.0-20250912143111-9785ff826cbf/go.mod h1:V21rZtvULxJyG8tUsRC8caTBvKNHOuRJVxH+G6ghH0Y=
-github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
+github.com/charmbracelet/x/ansi v0.10.1 h1:LT77A3bpevRD0yZ5NDR5nonS7N83mxzzGwuZcTGezLE=
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250811133356-e0c5dbe5ea4a h1:zYSNtEJM9jwHbJts2k+Hroj+xQwsW1yxc4Wopdv7KaI=
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250811133356-e0c5dbe5ea4a/go.mod h1:rc2bsPC6MWae3LdOxNO1mOb443NlMrrDL0xEya48NNc=
@@ -232,13 +232,13 @@ github.com/pressly/goose/v3 v3.25.0 h1:6WeYhMWGRCzpyd89SpODFnCBCKz41KrVbRT58nVjG
github.com/pressly/goose/v3 v3.25.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY=
github.com/qjebbs/go-jsons v0.0.0-20221222033332-a534c5fc1c4c h1:kmzxiX+OB0knCo1V0dkEkdPelzCdAzCURCfmFArn2/A=
github.com/qjebbs/go-jsons v0.0.0-20221222033332-a534c5fc1c4c/go.mod h1:wNJrtinHyC3YSf6giEh4FJN8+yZV7nXBjvmfjhBIcw4=
+github.com/raphamorim/notify v0.9.3 h1:sOUIE8U6wtt93QA3/2HOXsGsrsVvT7US5Ye01+Hzl9E=
+github.com/raphamorim/notify v0.9.3/go.mod h1:3FXSIPyrunV10GCnLGPrpSxoY/Dxi+saeQb9hf+TDSo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
-github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -16,12 +16,12 @@ import (
"github.com/charmbracelet/crush/internal/csync"
"github.com/charmbracelet/crush/internal/fsext"
"github.com/charmbracelet/crush/internal/lsp/protocol"
- "github.com/rjeczalik/notify"
+ "github.com/raphamorim/notify"
)
// global manages file watching shared across all LSP clients.
//
-// IMPORTANT: This implementation uses github.com/rjeczalik/notify which provides
+// IMPORTANT: This implementation uses github.com/raphamorim/notify which provides
// recursive watching on all platforms. On macOS it uses FSEvents, on Linux it
// uses inotify (with recursion handled by the library), and on Windows it uses
// ReadDirectoryChangesW.
@@ -30,6 +30,7 @@ import (
// - Single watch point for entire directory tree
// - Automatic recursive watching without manually adding subdirectories
// - No file descriptor exhaustion issues
+// - Built-in ignore system for filtering file events
type global struct {
// Channel for receiving file system events
events chan notify.EventInfo
@@ -83,7 +84,7 @@ func (gw *global) unregister(name string) {
// Start sets up recursive watching on the workspace root.
//
-// Note: We use github.com/rjeczalik/notify which provides recursive watching
+// Note: We use github.com/raphamorim/notify which provides recursive watching
// with a single watch point. The "..." suffix means watch recursively.
// This is much more efficient than manually walking and watching each directory.
func Start() error {
@@ -103,6 +104,12 @@ func Start() error {
gw.root = root
gw.started.Store(true)
+ // Set up ignore system
+ if err := setupIgnoreSystem(root); err != nil {
+ slog.Warn("lsp watcher: Failed to set up ignore system", "error", err)
+ // Continue anyway, but without ignore functionality
+ }
+
// Start the event processing goroutine
gw.wg.Add(1)
go gw.processEvents()
@@ -162,11 +169,6 @@ func (gw *global) processEvents() {
path := event.Path()
- // Skip ignored files
- if fsext.ShouldExcludeFile(gw.root, path) {
- continue
- }
-
if cfg != nil && cfg.Options.DebugLSP {
slog.Debug("lsp watcher: Global watcher received event", "path", path, "event", event.Event().String())
}
@@ -362,3 +364,31 @@ func isFileLimitError(err error) bool {
// Check for common file limit errors
return errors.Is(err, syscall.EMFILE) || errors.Is(err, syscall.ENFILE)
}
+
+// setupIgnoreSystem configures the notify library's ignore system
+// to use .crushignore and .gitignore files for filtering file events
+func setupIgnoreSystem(root string) error {
+ // Create a new ignore matcher for the workspace root
+ im := notify.NewIgnoreMatcher(root)
+
+ // Load .crushignore file if it exists
+ crushignorePath := filepath.Join(root, ".crushignore")
+ if _, err := os.Stat(crushignorePath); err == nil {
+ if err := im.LoadIgnoreFile(crushignorePath); err != nil {
+ slog.Warn("lsp watcher: Failed to load .crushignore file", "error", err)
+ }
+ }
+
+ // Load .gitignore file if it exists
+ gitignorePath := filepath.Join(root, ".gitignore")
+ if _, err := os.Stat(gitignorePath); err == nil {
+ if err := im.LoadIgnoreFile(gitignorePath); err != nil {
+ slog.Warn("lsp watcher: Failed to load .gitignore file", "error", err)
+ }
+ }
+
+ // Set as the global ignore matcher
+ notify.SetIgnoreMatcher(im)
+
+ return nil
+}