// 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 lsp import ( "context" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/telemetry" "golang.org/x/tools/internal/span" "golang.org/x/tools/internal/telemetry/log" ) func (s *Server) didChangeWatchedFiles(ctx context.Context, params *protocol.DidChangeWatchedFilesParams) error { for _, change := range params.Changes { uri := span.NewURI(change.URI) ctx := telemetry.File.With(ctx, uri) for _, view := range s.session.Views() { action := toFileAction(change.Type) switch action { case source.Change, source.Create: // If client has this file open, don't do anything. // The client's contents must remain the source of truth. if s.session.IsOpen(uri) { break } if s.session.DidChangeOutOfBand(ctx, uri, action) { // If we had been tracking the given file, // recompute diagnostics to reflect updated file contents. snapshot := view.Snapshot() fh, err := snapshot.GetFile(ctx, uri) if err != nil { return err } return s.diagnose(snapshot, fh) } case source.Delete: snapshot := view.Snapshot() fh := snapshot.FindFile(ctx, uri) // If we have never seen this file before, there is nothing to do. if fh == nil { continue } phs, err := snapshot.PackageHandles(ctx, fh) if err != nil { log.Error(ctx, "didChangeWatchedFiles: CheckPackageHandles", err, telemetry.File) continue } ph, err := source.WidestCheckPackageHandle(phs) if err != nil { log.Error(ctx, "didChangeWatchedFiles: WidestCheckPackageHandle", err, telemetry.File) continue } // Find a different file in the same package we can use to trigger diagnostics. // TODO(rstambler): Allow diagnostics to be called per-package to avoid this. var otherFile source.FileHandle for _, pgh := range ph.CompiledGoFiles() { if pgh.File().Identity().URI == fh.Identity().URI { continue } if f := snapshot.FindFile(ctx, pgh.File().Identity().URI); f != nil && s.session.IsOpen(fh.Identity().URI) { otherFile = f break } } // Notify the view of the deletion of the file. s.session.DidChangeOutOfBand(ctx, uri, action) // If this was the only file in the package, clear its diagnostics. if otherFile == nil { if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ URI: protocol.NewURI(uri), Version: fh.Identity().Version, }); err != nil { log.Error(ctx, "failed to clear diagnostics", err, telemetry.URI.Of(uri)) } return nil } // Refresh diagnostics for the package the file belonged to. go s.diagnoseFile(view.Snapshot(), otherFile) } } } return nil } func toFileAction(ct protocol.FileChangeType) source.FileAction { switch ct { case protocol.Changed: return source.Change case protocol.Created: return source.Create case protocol.Deleted: return source.Delete } return source.UnknownFileAction }