1
0
mirror of https://github.com/golang/go synced 2024-11-18 10:54:40 -07:00

internal/lsp: separate LSP files from FS files

FileHandle currently includes LSP-level information about Version and
Session. That's dangerous, because the cache operates in terms of
URIs and content only -- we explicitly want to share results across
sessions and versions if they happen to be the same.

Split the LSP information into separate types, VersionedFileHandle and
VersionedFileIdentity.

Change-Id: I158646b783375b58245468599301e2a29c657e71
Reviewed-on: https://go-review.googlesource.com/c/tools/+/245058
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Heschi Kreinick 2020-07-26 18:01:39 -04:00
parent 39fdd541e6
commit c9619e8fac
19 changed files with 141 additions and 113 deletions

View File

@ -139,15 +139,15 @@ func (h *fileHandle) Kind() source.FileKind {
return source.DetectLanguage("", h.uri.Filename())
}
func (h *fileHandle) Version() float64 {
return 0
func (h *fileHandle) Hash() string {
return h.hash
}
func (h *fileHandle) Identity() source.FileIdentity {
func (h *fileHandle) FileIdentity() source.FileIdentity {
return source.FileIdentity{
URI: h.uri,
Identifier: h.hash,
Kind: h.Kind(),
URI: h.uri,
Hash: h.hash,
Kind: h.Kind(),
}
}

View File

@ -174,7 +174,8 @@ func checkPackageKey(ctx context.Context, id packageID, pghs []*parseGoHandle, c
b.WriteString(string(dep))
}
for _, cgf := range pghs {
b.WriteString(cgf.file.Identity().String())
b.WriteString(string(cgf.file.URI()))
b.WriteString(cgf.file.FileIdentity().Hash)
}
return packageHandleKey(hashContents(b.Bytes()))
}

View File

@ -54,7 +54,7 @@ func (s *snapshot) ParseMod(ctx context.Context, modFH source.FileHandle) (*sour
return handle.parse(ctx, s)
}
h := s.view.session.cache.store.Bind(modFH.Identity().String(), func(ctx context.Context, _ memoize.Arg) interface{} {
h := s.view.session.cache.store.Bind(modFH.FileIdentity(), func(ctx context.Context, _ memoize.Arg) interface{} {
_, done := event.Start(ctx, "cache.ParseModHandle", tag.URI.Of(modFH.URI()))
defer done()
@ -96,7 +96,7 @@ func (s *snapshot) sumFH(ctx context.Context, modFH source.FileHandle) (source.F
// cache. Avoid (*snapshot).GetFile here, as we don't want to add
// nonexistent file handles to the snapshot if the file does not exist.
sumURI := span.URIFromPath(sumFilename(modFH.URI()))
sumFH := s.FindFile(sumURI)
var sumFH source.FileHandle = s.FindFile(sumURI)
if sumFH == nil {
var err error
sumFH, err = s.view.session.cache.getFile(ctx, sumURI)
@ -158,8 +158,9 @@ func extractModParseErrors(uri span.URI, m *protocol.ColumnMapper, parseErr erro
// modKey is uniquely identifies cached data for `go mod why` or dependencies
// to upgrade.
type modKey struct {
sessionID, cfg, mod, view string
verb modAction
sessionID, cfg, view string
mod source.FileIdentity
verb modAction
}
type modAction int
@ -201,7 +202,7 @@ func (s *snapshot) ModWhy(ctx context.Context) (map[string]string, error) {
key := modKey{
sessionID: s.view.session.id,
cfg: hashConfig(s.config(ctx)),
mod: fh.Identity().String(),
mod: fh.FileIdentity(),
view: s.view.root.Filename(),
verb: why,
}
@ -281,7 +282,7 @@ func (s *snapshot) ModUpgrade(ctx context.Context) (map[string]string, error) {
key := modKey{
sessionID: s.view.session.id,
cfg: hashConfig(cfg),
mod: fh.Identity().String(),
mod: fh.FileIdentity(),
view: s.view.root.Filename(),
verb: upgrade,
}

View File

@ -25,7 +25,7 @@ import (
type modTidyKey struct {
sessionID string
cfg string
gomod string
gomod source.FileIdentity
imports string
unsavedOverlays string
view string
@ -85,7 +85,7 @@ func (s *snapshot) ModTidy(ctx context.Context) (*source.TidiedModule, error) {
view: s.view.root.Filename(),
imports: importHash,
unsavedOverlays: overlayHash,
gomod: modFH.Identity().String(),
gomod: modFH.FileIdentity(),
cfg: hashConfig(cfg),
}
h := s.view.session.cache.store.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} {

View File

@ -25,7 +25,7 @@ import (
// parseKey uniquely identifies a parsed Go file.
type parseKey struct {
file string // FileIdentity.String()
file source.FileIdentity
mode source.ParseMode
}
@ -53,7 +53,7 @@ type parseGoData struct {
func (c *Cache) parseGoHandle(ctx context.Context, fh source.FileHandle, mode source.ParseMode) *parseGoHandle {
key := parseKey{
file: fh.Identity().String(),
file: fh.FileIdentity(),
mode: mode,
}
parseHandle := c.store.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} {

View File

@ -46,7 +46,7 @@ type overlay struct {
version float64
kind source.FileKind
// saved is true if a file has been saved on disk,
// saved is true if a file matches the state on disk,
// and therefore does not need to be part of the overlay sent to go/packages.
saved bool
}
@ -55,13 +55,19 @@ func (o *overlay) Read() ([]byte, error) {
return o.text, nil
}
func (o *overlay) Identity() source.FileIdentity {
func (o *overlay) FileIdentity() source.FileIdentity {
return source.FileIdentity{
URI: o.uri,
Identifier: o.hash,
SessionID: o.session.id,
Version: o.version,
Kind: o.kind,
URI: o.uri,
Hash: o.hash,
Kind: o.kind,
}
}
func (o *overlay) VersionedFileIdentity() source.VersionedFileIdentity {
return source.VersionedFileIdentity{
URI: o.uri,
SessionID: o.session.id,
Version: o.version,
}
}
@ -77,9 +83,34 @@ func (o *overlay) Version() float64 {
return o.version
}
func (o *overlay) Session() source.Session { return o.session }
func (o *overlay) Saved() bool { return o.saved }
func (o *overlay) Data() []byte { return o.text }
func (o *overlay) Session() string {
return o.session.id
}
func (o *overlay) Saved() bool {
return o.saved
}
// closedFile implements LSPFile for a file that the editor hasn't told us about.
type closedFile struct {
source.FileHandle
}
func (c *closedFile) VersionedFileIdentity() source.VersionedFileIdentity {
return source.VersionedFileIdentity{
URI: c.FileHandle.URI(),
SessionID: "",
Version: 0,
}
}
func (c *closedFile) Session() string {
return ""
}
func (c *closedFile) Version() float64 {
return 0
}
func (s *Session) ID() string { return s.id }
func (s *Session) String() string { return s.id }
@ -146,7 +177,7 @@ func (s *Session) createView(ctx context.Context, name string, folder span.URI,
packages: make(map[packageKey]*packageHandle),
ids: make(map[span.URI][]packageID),
metadata: make(map[packageID]*metadata),
files: make(map[span.URI]source.FileHandle),
files: make(map[span.URI]source.VersionedFileHandle),
importedBy: make(map[packageID][]packageID),
actions: make(map[actionKey]*actionHandle),
workspacePackages: make(map[packageID]packagePath),
@ -332,7 +363,7 @@ func (s *Session) ModifyFiles(ctx context.Context, changes []source.FileModifica
}
func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModification) ([]source.Snapshot, []func(), []span.URI, error) {
views := make(map[*View]map[span.URI]source.FileHandle)
views := make(map[*View]map[span.URI]source.VersionedFileHandle)
// Keep track of deleted files so that we can clear their diagnostics.
// A file might be re-created after deletion, so only mark files that
@ -362,21 +393,21 @@ func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModif
return nil, nil, nil, err
}
if _, ok := views[view]; !ok {
views[view] = make(map[span.URI]source.FileHandle)
views[view] = make(map[span.URI]source.VersionedFileHandle)
}
var (
fh source.FileHandle
ok bool
err error
fh source.VersionedFileHandle
ok bool
)
if fh, ok = overlays[c.URI]; ok {
views[view][c.URI] = fh
delete(deletions, c.URI)
} else {
fh, err = s.cache.getFile(ctx, c.URI)
fsFile, err := s.cache.getFile(ctx, c.URI)
if err != nil {
return nil, nil, nil, err
}
fh = &closedFile{fsFile}
views[view][c.URI] = fh
if _, err := fh.Read(); err != nil {
deletions[c.URI] = struct{}{}
@ -488,7 +519,7 @@ func (s *Session) updateOverlays(ctx context.Context, changes []source.FileModif
return nil, err
}
_, readErr := fh.Read()
sameContentOnDisk = (readErr == nil && fh.Identity().Identifier == hash)
sameContentOnDisk = (readErr == nil && fh.FileIdentity().Hash == hash)
}
o = &overlay{
session: s,

View File

@ -58,7 +58,7 @@ type snapshot struct {
// files maps file URIs to their corresponding FileHandles.
// It may invalidated when a file's content changes.
files map[span.URI]source.FileHandle
files map[span.URI]source.VersionedFileHandle
// packages maps a packageKey to a set of packageHandles to which that file belongs.
// It may be invalidated when a file's content changes.
@ -224,7 +224,7 @@ func (s *snapshot) buildOverlay() map[string][]byte {
return overlays
}
func hashUnsavedOverlays(files map[span.URI]source.FileHandle) string {
func hashUnsavedOverlays(files map[span.URI]source.VersionedFileHandle) string {
var unsaved []string
for uri, fh := range files {
if overlay, ok := fh.(*overlay); ok && !overlay.saved {
@ -574,7 +574,7 @@ func (s *snapshot) isWorkspacePackage(id packageID) (packagePath, bool) {
return scope, ok
}
func (s *snapshot) FindFile(uri span.URI) source.FileHandle {
func (s *snapshot) FindFile(uri span.URI) source.VersionedFileHandle {
f, err := s.view.getFile(uri)
if err != nil {
return nil
@ -588,7 +588,7 @@ func (s *snapshot) FindFile(uri span.URI) source.FileHandle {
// GetFile returns a File for the given URI. It will always succeed because it
// adds the file to the managed set if needed.
func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.VersionedFileHandle, error) {
f, err := s.view.getFile(uri)
if err != nil {
return nil, err
@ -605,8 +605,9 @@ func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle
if err != nil {
return nil, err
}
s.files[f.URI()] = fh
return fh, nil
closed := &closedFile{fh}
s.files[f.URI()] = closed
return closed, nil
}
func (s *snapshot) IsOpen(uri span.URI) bool {
@ -748,7 +749,7 @@ func contains(views []*View, view *View) bool {
return false
}
func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.FileHandle, forceReloadMetadata bool) *snapshot {
func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.VersionedFileHandle, forceReloadMetadata bool) *snapshot {
s.mu.Lock()
defer s.mu.Unlock()
@ -761,7 +762,7 @@ func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.Fi
metadata: make(map[packageID]*metadata),
packages: make(map[packageKey]*packageHandle),
actions: make(map[actionKey]*actionHandle),
files: make(map[span.URI]source.FileHandle),
files: make(map[span.URI]source.VersionedFileHandle),
workspacePackages: make(map[packageID]packagePath),
unloadableFiles: make(map[span.URI]struct{}),
parseModHandles: make(map[span.URI]*parseModHandle),
@ -955,7 +956,7 @@ func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, originalFH, cur
return currentFH.Kind() == source.Go
}
// If the file hasn't changed, there's no need to reload.
if originalFH.Identity().String() == currentFH.Identity().String() {
if originalFH.FileIdentity() == currentFH.FileIdentity() {
return false
}
// If a go.mod file's contents have changed, always invalidate metadata.
@ -1017,7 +1018,7 @@ func (s *snapshot) buildBuiltinPackage(ctx context.Context, goFiles []string) er
if err != nil {
return err
}
h := s.view.session.cache.store.Bind(fh.Identity(), func(ctx context.Context, arg memoize.Arg) interface{} {
h := s.view.session.cache.store.Bind(fh.FileIdentity(), func(ctx context.Context, arg memoize.Arg) interface{} {
snapshot := arg.(*snapshot)
pgh := snapshot.view.session.cache.parseGoHandle(ctx, fh, source.ParseFull)

View File

@ -326,7 +326,7 @@ func (v *View) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options)
if err != nil {
return err
}
modFileIdentifier = modFH.Identity().Identifier
modFileIdentifier = modFH.FileIdentity().Hash
}
if v.sumURI != "" {
sumFH, err = v.session.cache.getFile(ctx, v.sumURI)
@ -695,7 +695,7 @@ func (v *View) awaitInitialized(ctx context.Context) {
// invalidateContent invalidates the content of a Go file,
// including any position and type information that depends on it.
// It returns true if we were already tracking the given file, false otherwise.
func (v *View) invalidateContent(ctx context.Context, uris map[span.URI]source.FileHandle, forceReloadMetadata bool) (source.Snapshot, func()) {
func (v *View) invalidateContent(ctx context.Context, uris map[span.URI]source.VersionedFileHandle, forceReloadMetadata bool) (source.Snapshot, func()) {
// Detach the context so that content invalidation cannot be canceled.
ctx = xcontext.Detach(ctx)

View File

@ -440,7 +440,7 @@ func extractionFixes(ctx context.Context, snapshot source.Snapshot, pkg source.P
return actions, nil
}
func documentChanges(fh source.FileHandle, edits []protocol.TextEdit) []protocol.TextDocumentEdit {
func documentChanges(fh source.VersionedFileHandle, edits []protocol.TextEdit) []protocol.TextDocumentEdit {
return []protocol.TextDocumentEdit{
{
TextDocument: protocol.VersionedTextDocumentIdentifier{

View File

@ -267,7 +267,7 @@ func (i *Instance) getFile(r *http.Request) interface{} {
return nil
}
for _, o := range s.Overlays() {
if o.Identity().Identifier == identifier {
if o.FileIdentity().Hash == identifier {
return o
}
}

View File

@ -25,7 +25,7 @@ import (
// idWithAnalysis is used to track if the diagnostics for a given file were
// computed with analyses.
type idWithAnalysis struct {
id source.FileIdentity
id source.VersionedFileIdentity
withAnalysis bool
}
@ -226,8 +226,7 @@ func (s *Server) publishReports(ctx context.Context, snapshot source.Snapshot, r
}
source.SortDiagnostics(diagnostics)
toSend := sentDiagnostics{
version: key.id.Version,
identifier: key.id.Identifier,
id: key.id,
sorted: diagnostics,
withAnalysis: key.withAnalysis,
snapshotID: snapshot.ID(),
@ -255,7 +254,7 @@ func (s *Server) publishReports(ctx context.Context, snapshot source.Snapshot, r
// If we've already delivered diagnostics for this file, at this
// snapshot, with analyses, do not send diagnostics without analyses.
if delivered.snapshotID == toSend.snapshotID && delivered.version == toSend.version &&
if delivered.snapshotID == toSend.snapshotID && delivered.id == toSend.id &&
delivered.withAnalysis && !toSend.withAnalysis {
// Do not update the delivered map since it already contains better diagnostics.
continue
@ -366,7 +365,7 @@ See https://github.com/golang/go/issues/39164 for more detail on this issue.`,
return false
}
s.publishReports(ctx, snapshot, map[idWithAnalysis]map[string]*source.Diagnostic{
{id: fh.Identity()}: {diagnosticKey(diag): diag},
{id: fh.VersionedFileIdentity()}: {diagnosticKey(diag): diag},
})
return true
}

View File

@ -305,7 +305,7 @@ func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI,
// it to a snapshot.
// We don't want to return errors for benign conditions like wrong file type,
// so callers should do if !ok { return err } rather than if err != nil.
func (s *Server) beginFileRequest(ctx context.Context, pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.FileHandle, bool, func(), error) {
func (s *Server) beginFileRequest(ctx context.Context, pURI protocol.DocumentURI, expectKind source.FileKind) (source.Snapshot, source.VersionedFileHandle, bool, func(), error) {
uri := pURI.SpanURI()
if !uri.IsFile() {
// Not a file URI. Stop processing the request, but don't return an error.

View File

@ -474,7 +474,7 @@ func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string)
}
}
func commandToEdits(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, rng protocol.Range, cmd string) ([]protocol.TextDocumentEdit, error) {
func commandToEdits(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle, rng protocol.Range, cmd string) ([]protocol.TextDocumentEdit, error) {
var command *source.Command
for _, c := range source.Commands {
if c.Name == cmd {

View File

@ -21,7 +21,7 @@ import (
"golang.org/x/tools/internal/span"
)
func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.FileIdentity][]*source.Diagnostic, error) {
func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.VersionedFileIdentity][]*source.Diagnostic, error) {
uri := snapshot.View().ModFile()
if uri == "" {
return nil, nil
@ -38,8 +38,8 @@ func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.File
if err == source.ErrTmpModfileUnsupported {
return nil, nil
}
reports := map[source.FileIdentity][]*source.Diagnostic{
fh.Identity(): {},
reports := map[source.VersionedFileIdentity][]*source.Diagnostic{
fh.VersionedFileIdentity(): {},
}
if err != nil {
return nil, err
@ -59,7 +59,7 @@ func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.File
if err != nil {
return nil, err
}
reports[fh.Identity()] = append(reports[fh.Identity()], diag)
reports[fh.VersionedFileIdentity()] = append(reports[fh.VersionedFileIdentity()], diag)
}
return reports, nil
}

View File

@ -92,8 +92,7 @@ type Server struct {
// sentDiagnostics is used to cache diagnostics that have been sent for a given file.
type sentDiagnostics struct {
version float64
identifier string
id source.VersionedFileIdentity
sorted []*source.Diagnostic
withAnalysis bool
snapshotID uint64

View File

@ -152,7 +152,7 @@ func (c *Command) IsSuggestedFix() bool {
// SuggestedFix applies the command's suggested fix to the given file and
// range, returning the resulting edits.
func (c *Command) SuggestedFix(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) ([]protocol.TextDocumentEdit, error) {
func (c *Command) SuggestedFix(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) ([]protocol.TextDocumentEdit, error) {
if c.suggestedFixFn == nil {
return nil, fmt.Errorf("no suggested fix function for %s", c.Name)
}

View File

@ -38,7 +38,7 @@ type RelatedInformation struct {
Message string
}
func Diagnostics(ctx context.Context, snapshot Snapshot, pkg Package, withAnalysis bool) (map[FileIdentity][]*Diagnostic, bool, error) {
func Diagnostics(ctx context.Context, snapshot Snapshot, pkg Package, withAnalysis bool) (map[VersionedFileIdentity][]*Diagnostic, bool, error) {
onlyIgnoredFiles := true
for _, pgf := range pkg.CompiledGoFiles() {
onlyIgnoredFiles = onlyIgnoredFiles && snapshot.View().IgnoredFile(pgf.URI)
@ -60,7 +60,7 @@ func Diagnostics(ctx context.Context, snapshot Snapshot, pkg Package, withAnalys
warn = true
}
// Prepare the reports we will send for the files in this package.
reports := make(map[FileIdentity][]*Diagnostic)
reports := make(map[VersionedFileIdentity][]*Diagnostic)
for _, pgf := range pkg.CompiledGoFiles() {
clearReports(ctx, snapshot, reports, pgf.URI)
}
@ -124,31 +124,31 @@ func pickAnalyzers(snapshot Snapshot, hadTypeErrors bool) map[string]Analyzer {
return analyzers
}
func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (FileIdentity, []*Diagnostic, error) {
func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (VersionedFileIdentity, []*Diagnostic, error) {
fh, err := snapshot.GetFile(ctx, uri)
if err != nil {
return FileIdentity{}, nil, err
return VersionedFileIdentity{}, nil, err
}
pkg, _, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
if err != nil {
return FileIdentity{}, nil, err
return VersionedFileIdentity{}, nil, err
}
reports, _, err := Diagnostics(ctx, snapshot, pkg, true)
if err != nil {
return FileIdentity{}, nil, err
return VersionedFileIdentity{}, nil, err
}
diagnostics, ok := reports[fh.Identity()]
diagnostics, ok := reports[fh.VersionedFileIdentity()]
if !ok {
return FileIdentity{}, nil, errors.Errorf("no diagnostics for %s", uri)
return VersionedFileIdentity{}, nil, errors.Errorf("no diagnostics for %s", uri)
}
return fh.Identity(), diagnostics, nil
return fh.VersionedFileIdentity(), diagnostics, nil
}
type diagnosticSet struct {
listErrors, parseErrors, typeErrors []*Diagnostic
}
func diagnostics(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, pkg Package, hasMissingDeps bool) (bool, bool, error) {
func diagnostics(ctx context.Context, snapshot Snapshot, reports map[VersionedFileIdentity][]*Diagnostic, pkg Package, hasMissingDeps bool) (bool, bool, error) {
ctx, done := event.Start(ctx, "source.diagnostics", tag.Package.Of(pkg.ID()))
_ = ctx // circumvent SA4006
defer done()
@ -198,7 +198,7 @@ func diagnostics(ctx context.Context, snapshot Snapshot, reports map[FileIdentit
return nonEmptyDiagnostics, hasTypeErrors, nil
}
func analyses(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, pkg Package, analyses map[string]Analyzer) error {
func analyses(ctx context.Context, snapshot Snapshot, reports map[VersionedFileIdentity][]*Diagnostic, pkg Package, analyses map[string]Analyzer) error {
var analyzers []*analysis.Analyzer
for _, a := range analyses {
if !a.Enabled(snapshot.View()) {
@ -241,20 +241,20 @@ func analyses(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][
return nil
}
func clearReports(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, uri span.URI) {
func clearReports(ctx context.Context, snapshot Snapshot, reports map[VersionedFileIdentity][]*Diagnostic, uri span.URI) {
fh := snapshot.FindFile(uri)
if fh == nil {
return
}
reports[fh.Identity()] = []*Diagnostic{}
reports[fh.VersionedFileIdentity()] = []*Diagnostic{}
}
func addReports(ctx context.Context, snapshot Snapshot, reports map[FileIdentity][]*Diagnostic, uri span.URI, diagnostics ...*Diagnostic) error {
func addReports(ctx context.Context, snapshot Snapshot, reports map[VersionedFileIdentity][]*Diagnostic, uri span.URI, diagnostics ...*Diagnostic) error {
fh := snapshot.FindFile(uri)
if fh == nil {
return nil
}
existingDiagnostics, ok := reports[fh.Identity()]
existingDiagnostics, ok := reports[fh.VersionedFileIdentity()]
if !ok {
return fmt.Errorf("diagnostics for unexpected file %s", uri)
}
@ -268,12 +268,12 @@ func addReports(ctx context.Context, snapshot Snapshot, reports map[FileIdentity
if d1.Message != d2.Message {
continue
}
reports[fh.Identity()][i].Tags = append(reports[fh.Identity()][i].Tags, d1.Tags...)
reports[fh.VersionedFileIdentity()][i].Tags = append(reports[fh.VersionedFileIdentity()][i].Tags, d1.Tags...)
}
return nil
}
}
reports[fh.Identity()] = append(reports[fh.Identity()], diagnostics...)
reports[fh.VersionedFileIdentity()] = append(reports[fh.VersionedFileIdentity()], diagnostics...)
return nil
}

View File

@ -18,7 +18,7 @@ import (
"golang.org/x/tools/internal/span"
)
func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.URI) (map[FileIdentity][]*Diagnostic, error) {
func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.URI) (map[VersionedFileIdentity][]*Diagnostic, error) {
outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid()))
if err := os.MkdirAll(outDir, 0700); err != nil {
return nil, err
@ -32,7 +32,7 @@ func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.U
if err != nil {
return nil, err
}
reports := make(map[FileIdentity][]*Diagnostic)
reports := make(map[VersionedFileIdentity][]*Diagnostic)
var parseError error
for _, fn := range files {
fname, v, err := parseDetailsFile(fn)
@ -48,8 +48,7 @@ func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.U
if x == nil {
continue
}
k := x.Identity()
reports[k] = v
reports[x.VersionedFileIdentity()] = v
}
return reports, parseError
}

View File

@ -32,11 +32,11 @@ type Snapshot interface {
// FindFile returns the FileHandle for the given URI, if it is already
// in the given snapshot.
FindFile(uri span.URI) FileHandle
FindFile(uri span.URI) VersionedFileHandle
// GetFile returns the FileHandle for a given URI, initializing it
// if it is not already part of the snapshot.
GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
GetFile(ctx context.Context, uri span.URI) (VersionedFileHandle, error)
// IsOpen returns whether the editor currently has a file open.
IsOpen(uri span.URI) bool
@ -255,17 +255,10 @@ type Session interface {
// Overlay is the type for a file held in memory on a session.
type Overlay interface {
// Session returns the session this overlay belongs to.
Session() Session
// Identity returns the FileIdentity for the overlay.
Identity() FileIdentity
VersionedFileHandle
// Saved returns whether this overlay has been saved to disk.
Saved() bool
// Data is the contents of the overlay held in memory.
Data() []byte
}
// FileModification represents a modification to a file.
@ -357,17 +350,35 @@ const (
ParseFull
)
type VersionedFileHandle interface {
FileHandle
Version() float64
Session() string
// LSPIdentity returns the version identity of a file.
VersionedFileIdentity() VersionedFileIdentity
}
type VersionedFileIdentity struct {
URI span.URI
// SessionID is the ID of the LSP session.
SessionID string
// Version is the version of the file, as specified by the client. It should
// only be set in combination with SessionID.
Version float64
}
// FileHandle represents a handle to a specific version of a single file.
type FileHandle interface {
URI() span.URI
Kind() FileKind
Version() float64
// Identity returns a FileIdentity for the file, even if there was an error
// reading it.
// It is a fatal error to call Identity on a file that has not yet been read.
Identity() FileIdentity
FileIdentity() FileIdentity
// Read reads the contents of a file.
// If the file is not available, returns a nil slice and an error.
Read() ([]byte, error)
@ -377,27 +388,13 @@ type FileHandle interface {
type FileIdentity struct {
URI span.URI
// SessionID is the ID of the LSP session.
SessionID string
// Version is the version of the file, as specified by the client. It should
// only be set in combination with SessionID.
Version float64
// Identifier represents a unique identifier for the file.
// It could be a file's modification time or its SHA1 hash if it is not on disk.
Identifier string
// Identifier represents a unique identifier for the file's content.
Hash string
// Kind is the file's kind.
Kind FileKind
}
func (fileID FileIdentity) String() string {
// Version is not part of the FileIdentity string,
// as it can remain change even if the file does not.
return fmt.Sprintf("%s%s%s", fileID.URI, fileID.Identifier, fileID.Kind)
}
// FileKind describes the kind of the file in question.
// It can be one of Go, mod, or sum.
type FileKind int