aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Cottrell <iancottrell@google.com>2019-05-17 12:15:22 -0400
committerIan Cottrell <iancottrell@google.com>2019-05-24 14:03:12 +0000
commit2c0ae70061356820330c96810d9483beb9a6da8e (patch)
treeb817deda05633e8024a52e78a7fb7040dbe34b46
parent75713da8968e979b986f375a10f681d3323cfe17 (diff)
downloadgolang-x-tools-2c0ae70061356820330c96810d9483beb9a6da8e.tar.gz
internal/lsp: add file watching and use it to trigger invalidations
Change-Id: I6148539509364655e7d42044b73789870d30fbb6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/178161 Run-TryBot: Ian Cottrell <iancottrell@google.com> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
-rw-r--r--internal/lsp/cache/cache.go7
-rw-r--r--internal/lsp/cache/file.go1
-rw-r--r--internal/lsp/cache/session.go9
-rw-r--r--internal/lsp/cache/view.go17
-rw-r--r--internal/lsp/cache/watcher.go56
5 files changed, 70 insertions, 20 deletions
diff --git a/internal/lsp/cache/cache.go b/internal/lsp/cache/cache.go
index b5eaa15e8..cdb1d4905 100644
--- a/internal/lsp/cache/cache.go
+++ b/internal/lsp/cache/cache.go
@@ -28,9 +28,10 @@ type cache struct {
func (c *cache) NewSession(log xlog.Logger) source.Session {
return &session{
- cache: c,
- log: log,
- overlays: make(map[span.URI]*source.FileContent),
+ cache: c,
+ log: log,
+ overlays: make(map[span.URI]*source.FileContent),
+ filesWatchMap: NewWatchMap(),
}
}
diff --git a/internal/lsp/cache/file.go b/internal/lsp/cache/file.go
index 70c588bc2..d0683ebe8 100644
--- a/internal/lsp/cache/file.go
+++ b/internal/lsp/cache/file.go
@@ -18,7 +18,6 @@ import (
// viewFile extends source.File with helper methods for the view package.
type viewFile interface {
source.File
- invalidate()
filename() string
addURI(uri span.URI) int
}
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 53dee8a57..210915353 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -27,7 +27,8 @@ type session struct {
overlayMu sync.Mutex
overlays map[span.URI]*source.FileContent
- openFiles sync.Map
+ openFiles sync.Map
+ filesWatchMap *WatchMap
}
func (s *session) Shutdown(ctx context.Context) {
@@ -184,8 +185,10 @@ func (s *session) ReadFile(uri span.URI) *source.FileContent {
func (s *session) SetOverlay(uri span.URI, data []byte) {
s.overlayMu.Lock()
- defer s.overlayMu.Unlock()
- //TODO: we also need to invoke and watchers in here
+ defer func() {
+ s.overlayMu.Unlock()
+ s.filesWatchMap.Notify(uri)
+ }()
if data == nil {
delete(s.overlays, uri)
return
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 32204e848..7908f0b62 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -207,7 +207,7 @@ func (v *view) SetContent(ctx context.Context, uri span.URI, content []byte) err
v.backgroundCtx, v.cancel = context.WithCancel(v.baseCtx)
v.contentChanges[uri] = func() {
- v.applyContentChange(uri, content)
+ v.session.SetOverlay(uri, content)
}
return nil
@@ -232,17 +232,6 @@ func (v *view) applyContentChanges(ctx context.Context) error {
return nil
}
-// applyContentChange applies a content update for a given file. It assumes that the
-// caller is holding the view's mutex.
-func (v *view) applyContentChange(uri span.URI, content []byte) {
- v.session.SetOverlay(uri, content)
- f, err := v.getFile(uri)
- if err != nil {
- return
- }
- f.invalidate()
-}
-
func (f *goFile) invalidate() {
// TODO(rstambler): Should we recompute these here?
f.ast = nil
@@ -331,7 +320,9 @@ func (v *view) getFile(uri span.URI) (viewFile, error) {
default:
return nil, fmt.Errorf("unsupported file extension: %s", ext)
}
-
+ v.session.filesWatchMap.Watch(uri, func() {
+ f.invalidate()
+ })
v.mapFile(uri, f)
return f, nil
}
diff --git a/internal/lsp/cache/watcher.go b/internal/lsp/cache/watcher.go
new file mode 100644
index 000000000..c8f619b1e
--- /dev/null
+++ b/internal/lsp/cache/watcher.go
@@ -0,0 +1,56 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cache
+
+import (
+ "sync"
+)
+
+type watcher struct {
+ id uint64
+ callback func()
+}
+
+type WatchMap struct {
+ mu sync.Mutex
+ nextID uint64
+ watchers map[interface{}][]watcher
+}
+
+func NewWatchMap() *WatchMap {
+ return &WatchMap{watchers: make(map[interface{}][]watcher)}
+}
+func (w *WatchMap) Watch(key interface{}, callback func()) func() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ id := w.nextID
+ w.nextID++
+ w.watchers[key] = append(w.watchers[key], watcher{
+ id: id,
+ callback: callback,
+ })
+ return func() {
+ // unwatch if invoked
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ // find and delete the watcher entry
+ entries := w.watchers[key]
+ for i, entry := range entries {
+ if entry.id == id {
+ // found it
+ entries[i] = entries[len(entries)-1]
+ entries = entries[:len(entries)-1]
+ }
+ }
+ }
+}
+
+func (w *WatchMap) Notify(key interface{}) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ for _, entry := range w.watchers[key] {
+ entry.callback()
+ }
+}