1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:14:46 -07:00

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>
This commit is contained in:
Ian Cottrell 2019-05-17 12:15:22 -04:00
parent 75713da896
commit 2c0ae70061
5 changed files with 70 additions and 20 deletions

View File

@ -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(),
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

56
internal/lsp/cache/watcher.go vendored Normal file
View File

@ -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()
}
}