2019-12-17 13:43:36 -07:00
|
|
|
// 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"
|
2019-12-17 14:53:57 -07:00
|
|
|
errors "golang.org/x/xerrors"
|
2019-12-17 13:43:36 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
type overlay struct {
|
|
|
|
session *session
|
|
|
|
uri span.URI
|
|
|
|
data []byte
|
|
|
|
hash string
|
|
|
|
version float64
|
|
|
|
kind source.FileKind
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
sameContentOnDisk 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.data, o.hash, nil
|
|
|
|
}
|
|
|
|
|
2019-12-18 22:59:50 -07:00
|
|
|
func (s *session) updateOverlay(ctx context.Context, c source.FileModification) error {
|
2019-12-17 13:43:36 -07:00
|
|
|
s.overlayMu.Lock()
|
2019-12-17 14:53:57 -07:00
|
|
|
defer s.overlayMu.Unlock()
|
2019-12-17 13:43:36 -07:00
|
|
|
|
2019-12-18 22:59:50 -07:00
|
|
|
o, ok := s.overlays[c.URI]
|
2019-12-17 13:43:36 -07:00
|
|
|
|
2019-12-18 22:59:50 -07:00
|
|
|
// 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 errors.Errorf("updateOverlay: modifying unopened overlay %v", c.URI)
|
|
|
|
}
|
|
|
|
kind = o.kind
|
2019-12-17 14:53:57 -07:00
|
|
|
}
|
|
|
|
if kind == source.UnknownKind {
|
2019-12-18 22:59:50 -07:00
|
|
|
return errors.Errorf("updateOverlay: unknown file kind for %s", c.URI)
|
2019-12-17 14:53:57 -07:00
|
|
|
}
|
|
|
|
|
2019-12-18 22:59:50 -07:00
|
|
|
// Closing a file just deletes its overlay.
|
|
|
|
if c.Action == source.Close {
|
|
|
|
delete(s.overlays, c.URI)
|
|
|
|
return nil
|
2019-12-17 13:43:36 -07:00
|
|
|
}
|
2019-12-18 22:59:50 -07:00
|
|
|
|
2019-12-17 13:43:36 -07:00
|
|
|
// If the file is on disk, check if its content is the same as the overlay.
|
2019-12-18 22:59:50 -07:00
|
|
|
hash := hashContents(c.Text)
|
|
|
|
var sameContentOnDisk bool
|
|
|
|
switch c.Action {
|
|
|
|
case source.Open:
|
|
|
|
_, h, err := s.cache.GetFile(c.URI, kind).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 errors.Errorf("updateOverlay: saving %s at version %v, currently at %v", c.URI, c.Version, o.version)
|
2019-12-17 13:43:36 -07:00
|
|
|
}
|
2019-12-18 22:59:50 -07:00
|
|
|
if c.Text != nil && o.hash != hash {
|
|
|
|
return errors.Errorf("updateOverlay: overlay %s changed on save", c.URI)
|
2019-12-17 14:53:57 -07:00
|
|
|
}
|
2019-12-18 22:59:50 -07:00
|
|
|
sameContentOnDisk = true
|
|
|
|
}
|
|
|
|
s.overlays[c.URI] = &overlay{
|
|
|
|
session: s,
|
|
|
|
uri: c.URI,
|
|
|
|
data: c.Text,
|
|
|
|
version: c.Version,
|
|
|
|
kind: kind,
|
|
|
|
hash: hash,
|
|
|
|
sameContentOnDisk: sameContentOnDisk,
|
2019-12-17 14:53:57 -07:00
|
|
|
}
|
|
|
|
return nil
|
2019-12-17 13:43:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2019-12-17 14:53:57 -07:00
|
|
|
// TODO(rstambler): Make sure not to send overlays outside of the current view.
|
2019-12-17 13:43:36 -07:00
|
|
|
if overlay.sameContentOnDisk {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
overlays[uri.Filename()] = overlay.data
|
|
|
|
}
|
|
|
|
return overlays
|
|
|
|
}
|