diff options
author | Ian Cottrell <iancottrell@google.com> | 2019-05-17 12:15:22 -0400 |
---|---|---|
committer | Ian Cottrell <iancottrell@google.com> | 2019-05-24 14:03:12 +0000 |
commit | 2c0ae70061356820330c96810d9483beb9a6da8e (patch) | |
tree | b817deda05633e8024a52e78a7fb7040dbe34b46 | |
parent | 75713da8968e979b986f375a10f681d3323cfe17 (diff) | |
download | golang-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.go | 7 | ||||
-rw-r--r-- | internal/lsp/cache/file.go | 1 | ||||
-rw-r--r-- | internal/lsp/cache/session.go | 9 | ||||
-rw-r--r-- | internal/lsp/cache/view.go | 17 | ||||
-rw-r--r-- | internal/lsp/cache/watcher.go | 56 |
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() + } +} |