internal/lsp: check file content on disk when opening
As per discussion on golang/go#32810, to avoid the `go list` storm caused by many files being opened, we check if the file content opened is equivalent to the content on disk. If so, we mark this file as "on disk" so that we don't send it as an overlay to go/packages. Updates golang/go#32810 Change-Id: I0a520cf91bbe933c9afb76d0842f5556ac4e5b28 Reviewed-on: https://go-review.googlesource.com/c/tools/+/184257 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
parent
58bb5bbe30
commit
e47c3d98c3
|
@ -44,9 +44,9 @@ type overlay struct {
|
||||||
hash string
|
hash string
|
||||||
kind source.FileKind
|
kind source.FileKind
|
||||||
|
|
||||||
// onDisk is true if a file has been saved on disk,
|
// sameContentOnDisk is true if a file has been saved on disk,
|
||||||
// and therefore does not need to be part of the overlay sent to go/packages.
|
// and therefore does not need to be part of the overlay sent to go/packages.
|
||||||
onDisk bool
|
sameContentOnDisk bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) Shutdown(ctx context.Context) {
|
func (s *session) Shutdown(ctx context.Context) {
|
||||||
|
@ -182,14 +182,17 @@ func (s *session) Logger() xlog.Logger {
|
||||||
return s.log
|
return s.log
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) DidOpen(ctx context.Context, uri span.URI) {
|
func (s *session) DidOpen(ctx context.Context, uri span.URI, text []byte) {
|
||||||
|
// Mark the file as open.
|
||||||
s.openFiles.Store(uri, true)
|
s.openFiles.Store(uri, true)
|
||||||
|
|
||||||
|
// Read the file on disk and compare it to the text provided.
|
||||||
|
// If it is the same as on disk, we can avoid sending it as an overlay to go/packages.
|
||||||
|
s.openOverlay(ctx, uri, text)
|
||||||
|
|
||||||
// Mark the file as just opened so that we know to re-run packages.Load on it.
|
// Mark the file as just opened so that we know to re-run packages.Load on it.
|
||||||
// We do this because we may not be aware of all of the packages the file belongs to.
|
// We do this because we may not be aware of all of the packages the file belongs to.
|
||||||
|
|
||||||
// A file may be in multiple views.
|
// A file may be in multiple views.
|
||||||
// For each view, get the file and mark it as just opened.
|
|
||||||
for _, view := range s.views {
|
for _, view := range s.views {
|
||||||
if strings.HasPrefix(string(uri), string(view.Folder())) {
|
if strings.HasPrefix(string(uri), string(view.Folder())) {
|
||||||
f, err := view.GetFile(ctx, uri)
|
f, err := view.GetFile(ctx, uri)
|
||||||
|
@ -215,7 +218,7 @@ func (s *session) DidSave(uri span.URI) {
|
||||||
defer s.overlayMu.Unlock()
|
defer s.overlayMu.Unlock()
|
||||||
|
|
||||||
if overlay, ok := s.overlays[uri]; ok {
|
if overlay, ok := s.overlays[uri]; ok {
|
||||||
overlay.onDisk = true
|
overlay.sameContentOnDisk = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,6 +258,30 @@ func (s *session) SetOverlay(uri span.URI, data []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// openOverlay adds the file content to the overlay.
|
||||||
|
// It also checks if the provided content is equivalent to the file's content on disk.
|
||||||
|
func (s *session) openOverlay(ctx context.Context, uri span.URI, data []byte) {
|
||||||
|
s.overlayMu.Lock()
|
||||||
|
defer func() {
|
||||||
|
s.overlayMu.Unlock()
|
||||||
|
s.filesWatchMap.Notify(uri)
|
||||||
|
}()
|
||||||
|
s.overlays[uri] = &overlay{
|
||||||
|
session: s,
|
||||||
|
uri: uri,
|
||||||
|
data: data,
|
||||||
|
hash: hashContents(data),
|
||||||
|
}
|
||||||
|
_, hash, err := s.cache.GetFile(uri).Read(ctx)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorf(ctx, "failed to read %s: %v", uri, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hash == s.overlays[uri].hash {
|
||||||
|
s.overlays[uri].sameContentOnDisk = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *session) readOverlay(uri span.URI) *overlay {
|
func (s *session) readOverlay(uri span.URI) *overlay {
|
||||||
s.overlayMu.Lock()
|
s.overlayMu.Lock()
|
||||||
defer s.overlayMu.Unlock()
|
defer s.overlayMu.Unlock()
|
||||||
|
@ -272,7 +299,7 @@ func (s *session) buildOverlay() map[string][]byte {
|
||||||
|
|
||||||
overlays := make(map[string][]byte)
|
overlays := make(map[string][]byte)
|
||||||
for uri, overlay := range s.overlays {
|
for uri, overlay := range s.overlays {
|
||||||
if overlay.onDisk {
|
if overlay.sameContentOnDisk {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
overlays[uri.Filename()] = overlay.data
|
overlays[uri.Filename()] = overlay.data
|
||||||
|
|
|
@ -154,7 +154,7 @@ type Session interface {
|
||||||
FileSystem
|
FileSystem
|
||||||
|
|
||||||
// DidOpen is invoked each time a file is opened in the editor.
|
// DidOpen is invoked each time a file is opened in the editor.
|
||||||
DidOpen(ctx context.Context, uri span.URI)
|
DidOpen(ctx context.Context, uri span.URI, text []byte)
|
||||||
|
|
||||||
// DidSave is invoked each time an open file is saved in the editor.
|
// DidSave is invoked each time an open file is saved in the editor.
|
||||||
DidSave(uri span.URI)
|
DidSave(uri span.URI)
|
||||||
|
|
|
@ -17,8 +17,18 @@ import (
|
||||||
|
|
||||||
func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
|
func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
|
||||||
uri := span.NewURI(params.TextDocument.URI)
|
uri := span.NewURI(params.TextDocument.URI)
|
||||||
s.session.DidOpen(ctx, uri)
|
text := []byte(params.TextDocument.Text)
|
||||||
return s.cacheAndDiagnose(ctx, uri, []byte(params.TextDocument.Text))
|
|
||||||
|
// Open the file.
|
||||||
|
s.session.DidOpen(ctx, uri, text)
|
||||||
|
|
||||||
|
// Run diagnostics on the newly-changed file.
|
||||||
|
view := s.session.ViewOf(uri)
|
||||||
|
go func() {
|
||||||
|
ctx := view.BackgroundContext()
|
||||||
|
s.Diagnostics(ctx, view, uri)
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
|
func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
|
||||||
|
@ -46,16 +56,12 @@ func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the new file content and send fresh diagnostics.
|
// Cache the new file content and send fresh diagnostics.
|
||||||
return s.cacheAndDiagnose(ctx, uri, []byte(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content []byte) error {
|
|
||||||
view := s.session.ViewOf(uri)
|
view := s.session.ViewOf(uri)
|
||||||
if err := view.SetContent(ctx, uri, content); err != nil {
|
if err := view.SetContent(ctx, uri, []byte(text)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Run diagnostics on the newly-changed file.
|
||||||
go func() {
|
go func() {
|
||||||
ctx := view.BackgroundContext()
|
ctx := view.BackgroundContext()
|
||||||
s.Diagnostics(ctx, view, uri)
|
s.Diagnostics(ctx, view, uri)
|
||||||
|
|
Loading…
Reference in New Issue