From 4b52ceea2b30e26ca90014fdac8c095ca6ce7abb Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 27 Jun 2025 14:28:48 -0700 Subject: [PATCH 1/5] Deduplicate watched file events --- internal/project/service.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/internal/project/service.go b/internal/project/service.go index b4a5249cd1..6fa9d7c583 100644 --- a/internal/project/service.go +++ b/internal/project/service.go @@ -1,11 +1,13 @@ package project import ( + "cmp" "context" "errors" "fmt" "maps" "runtime" + "slices" "strings" "sync" @@ -296,6 +298,17 @@ func (s *Service) Close() { } func (s *Service) OnWatchedFilesChanged(ctx context.Context, changes []*lsproto.FileEvent) error { + { + deduped := slices.Clone(changes) + slices.SortFunc(deduped, func(a, b *lsproto.FileEvent) int { + if c := strings.Compare(string(a.Uri), string(b.Uri)); c != 0 { + return c + } + return cmp.Compare(a.Type, b.Type) + }) + changes = slices.CompactFunc(deduped, func(a, b *lsproto.FileEvent) bool { return *a == *b }) + } + s.projectsMu.RLock() defer s.projectsMu.RUnlock() for _, change := range changes { From 8811ceebd0f26426f7136a2637f260e58e70c348 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 27 Jun 2025 14:35:33 -0700 Subject: [PATCH 2/5] Avoid calling MatchesFileName if entry is already pending --- internal/project/configfileregistry.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/project/configfileregistry.go b/internal/project/configfileregistry.go index e17a52bdf6..26b9060ba9 100644 --- a/internal/project/configfileregistry.go +++ b/internal/project/configfileregistry.go @@ -239,7 +239,10 @@ func (c *ConfigFileRegistry) tryInvokeWildCardDirectories(fileName string, path configFiles := c.ConfigFiles.ToMap() for configPath, entry := range configFiles { entry.mu.Lock() - hasSet := entry.commandLine != nil && entry.commandLine.MatchesFileName(fileName) && entry.SetPendingReload(PendingReloadFileNames) + hasSet := false + if entry.commandLine != nil && entry.pendingReload == PendingReloadNone && entry.commandLine.MatchesFileName(fileName) { + hasSet = entry.SetPendingReload(PendingReloadFileNames) + } var projects map[*Project]struct{} if hasSet { projects = maps.Clone(entry.projects.Keys()) From ffa20081bc375c0fe9bfc17c2f80823432b5fd4b Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 27 Jun 2025 14:42:08 -0700 Subject: [PATCH 3/5] Simplify --- internal/project/service.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/internal/project/service.go b/internal/project/service.go index 6fa9d7c583..f87f120f36 100644 --- a/internal/project/service.go +++ b/internal/project/service.go @@ -1,16 +1,15 @@ package project import ( - "cmp" "context" "errors" "fmt" "maps" "runtime" - "slices" "strings" "sync" + "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/ls" "github.com/microsoft/typescript-go/internal/lsp/lsproto" @@ -299,14 +298,8 @@ func (s *Service) Close() { func (s *Service) OnWatchedFilesChanged(ctx context.Context, changes []*lsproto.FileEvent) error { { - deduped := slices.Clone(changes) - slices.SortFunc(deduped, func(a, b *lsproto.FileEvent) int { - if c := strings.Compare(string(a.Uri), string(b.Uri)); c != 0 { - return c - } - return cmp.Compare(a.Type, b.Type) - }) - changes = slices.CompactFunc(deduped, func(a, b *lsproto.FileEvent) bool { return *a == *b }) + seen := collections.NewSetWithSizeHint[lsproto.FileEvent](len(changes)) + changes = core.Filter(changes, func(change *lsproto.FileEvent) bool { return seen.AddIfAbsent(*change) }) } s.projectsMu.RLock() From f9c5164f8d146aed7d98c7702f6da585c4d9bae4 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 28 Jun 2025 12:59:37 -0700 Subject: [PATCH 4/5] PR feedback --- internal/project/service.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/project/service.go b/internal/project/service.go index f87f120f36..4e0c1e885b 100644 --- a/internal/project/service.go +++ b/internal/project/service.go @@ -297,14 +297,15 @@ func (s *Service) Close() { } func (s *Service) OnWatchedFilesChanged(ctx context.Context, changes []*lsproto.FileEvent) error { - { - seen := collections.NewSetWithSizeHint[lsproto.FileEvent](len(changes)) - changes = core.Filter(changes, func(change *lsproto.FileEvent) bool { return seen.AddIfAbsent(*change) }) - } + seen := collections.NewSetWithSizeHint[lsproto.FileEvent](len(changes)) s.projectsMu.RLock() defer s.projectsMu.RUnlock() for _, change := range changes { + if seen.AddIfAbsent(*change) { + continue + } + fileName := ls.DocumentURIToFileName(change.Uri) path := s.toPath(fileName) if err, ok := s.configFileRegistry.onWatchedFilesChanged(path, change.Type); ok { From 371552e8c9521d06da68efe94f3dbf498279b9df Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 28 Jun 2025 15:49:25 -0700 Subject: [PATCH 5/5] Flip condition --- internal/project/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/project/service.go b/internal/project/service.go index 4e0c1e885b..0759a7f3a1 100644 --- a/internal/project/service.go +++ b/internal/project/service.go @@ -302,7 +302,7 @@ func (s *Service) OnWatchedFilesChanged(ctx context.Context, changes []*lsproto. s.projectsMu.RLock() defer s.projectsMu.RUnlock() for _, change := range changes { - if seen.AddIfAbsent(*change) { + if !seen.AddIfAbsent(*change) { continue }