1
0
mirror of https://github.com/golang/go synced 2024-10-01 16:38:34 -06:00
go/internal/lsp/cache/overlay.go
Rebecca Stambler 86d412b4c6 internal/lsp: fix support for watching changed files
This is the beginning of the CLs to refactor the file watching code with
the normal text synchronization code. This hasn't yet been tested other
than with some minimal local testing, so follow-up CLs will be needed.

Updates golang/go#31553

Change-Id: Id081ecc59dd2903057164171bd95f0dc07baa5f1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/214277
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-01-13 19:19:55 +00:00

134 lines
3.3 KiB
Go

// 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 (
"context"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
errors "golang.org/x/xerrors"
)
type overlay struct {
session *session
uri span.URI
text []byte
hash string
version float64
kind source.FileKind
// saved 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.
saved bool
}
func (o *overlay) FileSystem() source.FileSystem {
return o.session
}
func (o *overlay) Identity() source.FileIdentity {
return source.FileIdentity{
URI: o.uri,
Identifier: o.hash,
Version: o.version,
Kind: o.kind,
}
}
func (o *overlay) Read(ctx context.Context) ([]byte, string, error) {
return o.text, o.hash, nil
}
func (s *session) updateOverlay(ctx context.Context, c source.FileModification) (source.FileKind, error) {
// Make sure that the file was not changed on disk.
if c.OnDisk {
return source.UnknownKind, errors.Errorf("updateOverlay called for an on-disk change: %s", c.URI)
}
s.overlayMu.Lock()
defer s.overlayMu.Unlock()
o, ok := s.overlays[c.URI]
// Determine the file kind on open, otherwise, assume it has been cached.
var kind source.FileKind
switch c.Action {
case source.Open:
kind = source.DetectLanguage(c.LanguageID, c.URI.Filename())
default:
if !ok {
return -1, errors.Errorf("updateOverlay: modifying unopened overlay %v", c.URI)
}
kind = o.kind
}
if kind == source.UnknownKind {
return -1, errors.Errorf("updateOverlay: unknown file kind for %s", c.URI)
}
// Closing a file just deletes its overlay.
if c.Action == source.Close {
delete(s.overlays, c.URI)
return kind, nil
}
// If the file is on disk, check if its content is the same as the overlay.
text := c.Text
if text == nil {
text = o.text
}
hash := hashContents(text)
var sameContentOnDisk bool
switch c.Action {
case source.Open:
_, h, err := s.cache.GetFile(c.URI).Read(ctx)
sameContentOnDisk = (err == nil && h == hash)
case source.Save:
// Make sure the version and content (if present) is the same.
if o.version != c.Version {
return -1, errors.Errorf("updateOverlay: saving %s at version %v, currently at %v", c.URI, c.Version, o.version)
}
if c.Text != nil && o.hash != hash {
return -1, errors.Errorf("updateOverlay: overlay %s changed on save", c.URI)
}
sameContentOnDisk = true
}
s.overlays[c.URI] = &overlay{
session: s,
uri: c.URI,
version: c.Version,
text: text,
kind: kind,
hash: hash,
saved: sameContentOnDisk,
}
return kind, nil
}
func (s *session) readOverlay(uri span.URI) *overlay {
s.overlayMu.Lock()
defer s.overlayMu.Unlock()
// We might have the content saved in an overlay.
if overlay, ok := s.overlays[uri]; ok {
return overlay
}
return nil
}
func (s *session) buildOverlay() map[string][]byte {
s.overlayMu.Lock()
defer s.overlayMu.Unlock()
overlays := make(map[string][]byte)
for uri, overlay := range s.overlays {
// TODO(rstambler): Make sure not to send overlays outside of the current view.
if overlay.saved {
continue
}
overlays[uri.Filename()] = overlay.text
}
return overlays
}