internal/lsp: reorganize and refactor code

This change cleans up internal/lsp/source/view.go to have a more logical
ordering and deletes the view.CheckPackageHandle function. Now, the only
way to get a CheckPackageHandle is through a snapshot (so all of the
corresponding edits).

Also, renamed fuzzy tests to fuzzymatch. Noticed this weird error when
debugging - I had golang.org/x/tools/internal/lsp/fuzzy in my module
cache and it conflicted with the test version.

Change-Id: Ib87836796a8e76e6b6ed1306c2a93e9a5db91cce
Reviewed-on: https://go-review.googlesource.com/c/tools/+/208099
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Rebecca Stambler 2019-11-20 16:38:43 -05:00
parent 8fd459516a
commit 35ba81b9fb
33 changed files with 332 additions and 338 deletions

View File

@ -13,21 +13,7 @@ import (
errors "golang.org/x/xerrors"
)
func (v *view) CheckPackageHandles(ctx context.Context, f source.File) (source.Snapshot, []source.CheckPackageHandle, error) {
// Get the snapshot that will be used for type-checking.
s := v.getSnapshot()
cphs, err := s.CheckPackageHandles(ctx, f)
if err != nil {
return nil, nil, err
}
if len(cphs) == 0 {
return nil, nil, errors.Errorf("no CheckPackageHandles for %s", f.URI())
}
return s, cphs, nil
}
func (s *snapshot) CheckPackageHandles(ctx context.Context, f source.File) ([]source.CheckPackageHandle, error) {
func (s *snapshot) PackageHandles(ctx context.Context, f source.File) ([]source.CheckPackageHandle, error) {
ctx = telemetry.File.With(ctx, f.URI())
fh := s.Handle(ctx, f)
@ -109,7 +95,7 @@ func (v *view) GetActiveReverseDeps(ctx context.Context, f source.File) (results
if _, ok := seen[f.URI()]; ok {
continue
}
cphs, err := s.CheckPackageHandles(ctx, f)
cphs, err := s.PackageHandles(ctx, f)
if err != nil {
continue
}

View File

@ -72,7 +72,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
},
})
case source.Go:
edits, editsPerFix, err := source.AllImportsFixes(ctx, view, f)
edits, editsPerFix, err := source.AllImportsFixes(ctx, snapshot, f)
if err != nil {
return nil, err
}
@ -204,9 +204,9 @@ func importDiagnostics(fix *imports.ImportFix, diagnostics []protocol.Diagnostic
return results
}
func quickFixes(ctx context.Context, s source.Snapshot, f source.File, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error) {
func quickFixes(ctx context.Context, snapshot source.Snapshot, f source.File, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error) {
var codeActions []protocol.CodeAction
cphs, err := s.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, err
}
@ -217,7 +217,7 @@ func quickFixes(ctx context.Context, s source.Snapshot, f source.File, diagnosti
return nil, err
}
for _, diag := range diagnostics {
srcErr, err := s.FindAnalysisError(ctx, cph.ID(), diag)
srcErr, err := snapshot.FindAnalysisError(ctx, cph.ID(), diag)
if err != nil {
continue
}
@ -229,12 +229,12 @@ func quickFixes(ctx context.Context, s source.Snapshot, f source.File, diagnosti
Edit: protocol.WorkspaceEdit{},
}
for uri, edits := range fix.Edits {
f, err := s.View().GetFile(ctx, uri)
f, err := snapshot.View().GetFile(ctx, uri)
if err != nil {
log.Error(ctx, "no file", err, telemetry.URI.Of(uri))
continue
}
fh := s.Handle(ctx, f)
fh := snapshot.Handle(ctx, f)
action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, documentChanges(fh, edits)...)
}
codeActions = append(codeActions, action)

View File

@ -22,13 +22,14 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
options := view.Options()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
options.Completion.FullDocumentation = options.HoverKind == source.FullDocumentation
candidates, surrounding, err := source.Completion(ctx, view, f, params.Position, options.Completion)
candidates, surrounding, err := source.Completion(ctx, snapshot, f, params.Position, options.Completion)
if err != nil {
log.Print(ctx, "no completions found", tag.Of("At", params.Position), tag.Of("Failure", err))
}

View File

@ -18,11 +18,12 @@ func (s *Server) definition(ctx context.Context, params *protocol.DefinitionPara
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, view, f, params.Position)
ident, err := source.Identifier(ctx, snapshot, f, params.Position)
if err != nil {
return nil, err
}
@ -44,11 +45,12 @@ func (s *Server) typeDefinition(ctx context.Context, params *protocol.TypeDefini
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, view, f, params.Position)
ident, err := source.Identifier(ctx, snapshot, f, params.Position)
if err != nil {
return nil, err
}

View File

@ -16,7 +16,7 @@ import (
"golang.org/x/tools/internal/telemetry/trace"
)
func (s *Server) diagnoseView(view source.View, cphs []source.CheckPackageHandle) {
func (s *Server) diagnoseSnapshot(snapshot source.Snapshot, cphs []source.CheckPackageHandle) {
for _, cph := range cphs {
if len(cph.CompiledGoFiles()) == 0 {
continue
@ -24,24 +24,24 @@ func (s *Server) diagnoseView(view source.View, cphs []source.CheckPackageHandle
f := cph.CompiledGoFiles()[0]
// Run diagnostics on the workspace package.
go func(view source.View, uri span.URI) {
s.diagnostics(view, uri)
}(view, f.File().Identity().URI)
go func(snapshot source.Snapshot, uri span.URI) {
s.diagnostics(snapshot, uri)
}(snapshot, f.File().Identity().URI)
}
}
func (s *Server) diagnostics(view source.View, uri span.URI) error {
ctx := view.BackgroundContext()
func (s *Server) diagnostics(snapshot source.Snapshot, uri span.URI) error {
ctx := snapshot.View().BackgroundContext()
ctx, done := trace.StartSpan(ctx, "lsp:background-worker")
defer done()
ctx = telemetry.File.With(ctx, uri)
f, err := view.GetFile(ctx, uri)
f, err := snapshot.View().GetFile(ctx, uri)
if err != nil {
return err
}
reports, warningMsg, err := source.Diagnostics(ctx, view, f, view.Options().DisabledAnalyses)
reports, warningMsg, err := source.Diagnostics(ctx, snapshot, f, snapshot.View().Options().DisabledAnalyses)
if err != nil {
return err
}

View File

@ -14,11 +14,12 @@ func (s *Server) foldingRange(ctx context.Context, params *protocol.FoldingRange
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
ranges, err := source.FoldingRange(ctx, view, f, view.Options().LineFoldingOnly)
ranges, err := source.FoldingRange(ctx, snapshot, f, view.Options().LineFoldingOnly)
if err != nil {
return nil, err
}

View File

@ -18,9 +18,10 @@ func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormat
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
return source.Format(ctx, view, f)
return source.Format(ctx, snapshot, f)
}

View File

@ -101,7 +101,7 @@ func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitializ
},
},
Workspace: protocol.WorkspaceGn{
protocol.WorkspaceFoldersGn{
WorkspaceFolders: protocol.WorkspaceFoldersGn{
Supported: true,
ChangeNotifications: "workspace/didChangeWorkspaceFolders",
},
@ -163,7 +163,7 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
viewErrors[uri] = err
continue
}
s.diagnoseView(view, workspacePackages)
s.diagnoseSnapshot(view.Snapshot(), workspacePackages)
}
if len(viewErrors) > 0 {
errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(s.pendingFolders), len(s.session.Views()))

View File

@ -20,7 +20,12 @@ func (s *Server) documentHighlight(ctx context.Context, params *protocol.Documen
if err != nil {
return nil, err
}
rngs, err := source.Highlight(ctx, view, uri, params.Position)
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
rngs, err := source.Highlight(ctx, snapshot, f, params.Position)
if err != nil {
log.Error(ctx, "no highlight", err, telemetry.URI.Of(uri))
}

View File

@ -21,11 +21,12 @@ func (s *Server) hover(ctx context.Context, params *protocol.HoverParams) (*prot
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, view, f, params.Position)
ident, err := source.Identifier(ctx, snapshot, f, params.Position)
if err != nil {
return nil, nil
}

View File

@ -18,11 +18,12 @@ func (s *Server) implementation(ctx context.Context, params *protocol.Implementa
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, view, f, params.Position)
ident, err := source.Identifier(ctx, snapshot, f, params.Position)
if err != nil {
return nil, err
}

View File

@ -78,7 +78,7 @@ func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []source.Diagnosti
if err != nil {
t.Fatalf("no file for %s: %v", f, err)
}
results, _, err := source.Diagnostics(r.ctx, v, f, nil)
results, _, err := source.Diagnostics(r.ctx, v.Snapshot(), f, nil)
if err != nil {
t.Fatal(err)
}
@ -336,7 +336,7 @@ func (r *runner) SuggestedFix(t *testing.T, spn span.Span) {
if err != nil {
t.Fatal(err)
}
diagnostics, _, err := source.Diagnostics(r.ctx, view, f, nil)
diagnostics, _, err := source.Diagnostics(r.ctx, view.Snapshot(), f, nil)
if err != nil {
t.Fatal(err)
}

View File

@ -20,12 +20,13 @@ func (s *Server) references(ctx context.Context, params *protocol.ReferenceParam
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
// Find all references to the identifier at the position.
ident, err := source.Identifier(ctx, view, f, params.Position)
ident, err := source.Identifier(ctx, snapshot, f, params.Position)
if err != nil {
return nil, err
}

View File

@ -18,11 +18,12 @@ func (s *Server) rename(ctx context.Context, params *protocol.RenameParams) (*pr
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, view, f, params.Position)
ident, err := source.Identifier(ctx, snapshot, f, params.Position)
if err != nil {
return nil, err
}
@ -50,11 +51,12 @@ func (s *Server) prepareRename(ctx context.Context, params *protocol.PrepareRena
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, view, f, params.Position)
ident, err := source.Identifier(ctx, snapshot, f, params.Position)
if err != nil {
return nil, nil // ignore errors
}

View File

@ -20,11 +20,12 @@ func (s *Server) signatureHelp(ctx context.Context, params *protocol.SignatureHe
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
info, err := source.SignatureHelp(ctx, view, f, params.Position)
info, err := source.SignatureHelp(ctx, snapshot, f, params.Position)
if err != nil {
log.Print(ctx, "no signature help", tag.Of("At", params.Position), tag.Of("Failure", err))
return nil, nil

View File

@ -132,9 +132,6 @@ type completer struct {
qf types.Qualifier
opts CompletionOptions
// view is the View associated with this completion request.
view View
// ctx is the context associated with this completion request.
ctx context.Context
@ -259,7 +256,7 @@ func (c *completer) setSurrounding(ident *ast.Ident) {
cursor: c.pos,
mappedRange: mappedRange{
// Overwrite the prefix only.
spanRange: span.NewRange(c.view.Session().Cache().FileSet(), ident.Pos(), c.pos),
spanRange: span.NewRange(c.snapshot.View().Session().Cache().FileSet(), ident.Pos(), c.pos),
m: c.mapper,
},
}
@ -279,7 +276,7 @@ func (c *completer) getSurrounding() *Selection {
content: "",
cursor: c.pos,
mappedRange: mappedRange{
spanRange: span.NewRange(c.view.Session().Cache().FileSet(), c.pos, c.pos),
spanRange: span.NewRange(c.snapshot.View().Session().Cache().FileSet(), c.pos, c.pos),
m: c.mapper,
},
}
@ -397,13 +394,13 @@ func (e ErrIsDefinition) Error() string {
// The selection is computed based on the preceding identifier and can be used by
// the client to score the quality of the completion. For instance, some clients
// may tolerate imperfect matches as valid completion results, since users may make typos.
func Completion(ctx context.Context, view View, f File, pos protocol.Position, opts CompletionOptions) ([]CompletionItem, *Selection, error) {
func Completion(ctx context.Context, snapshot Snapshot, f File, pos protocol.Position, opts CompletionOptions) ([]CompletionItem, *Selection, error) {
ctx, done := trace.StartSpan(ctx, "source.Completion")
defer done()
startTime := time.Now()
snapshot, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, nil, err
}
@ -452,7 +449,6 @@ func Completion(ctx context.Context, view View, f File, pos protocol.Position, o
pkg: pkg,
snapshot: snapshot,
qf: qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
view: view,
ctx: ctx,
filename: f.URI().Filename(),
file: file,
@ -586,7 +582,7 @@ func (c *completer) selector(sel *ast.SelectorExpr) error {
// Try unimported packages.
if id, ok := sel.X.(*ast.Ident); ok {
pkgExports, err := PackageExports(c.ctx, c.view, id.Name, c.filename)
pkgExports, err := PackageExports(c.ctx, c.snapshot.View(), id.Name, c.filename)
if err != nil {
return err
}
@ -736,7 +732,7 @@ func (c *completer) lexical() error {
if c.opts.Unimported {
// Suggest packages that have not been imported yet.
pkgs, err := CandidateImports(c.ctx, c.view, c.filename)
pkgs, err := CandidateImports(c.ctx, c.snapshot.View(), c.filename)
if err != nil {
return err
}

View File

@ -139,7 +139,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
if !c.opts.Documentation {
return item, nil
}
pos := c.view.Session().Cache().FileSet().Position(obj.Pos())
pos := c.snapshot.View().Session().Cache().FileSet().Position(obj.Pos())
// We ignore errors here, because some types, like "unsafe" or "error",
// may not have valid positions that we can use to get documentation.
@ -154,7 +154,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
if cand.imp != nil && cand.imp.pkg != nil {
searchPkg = cand.imp.pkg
}
file, _, pkg, err := c.view.FindPosInPackage(searchPkg, obj.Pos())
file, _, pkg, err := c.snapshot.View().FindPosInPackage(searchPkg, obj.Pos())
if err != nil {
log.Error(c.ctx, "error finding file in package", err, telemetry.URI.Of(uri), telemetry.Package.Of(searchPkg.ID()))
return item, nil
@ -193,7 +193,7 @@ func (c *completer) importEdits(imp *importInfo) ([]protocol.TextEdit, error) {
return nil, errors.Errorf("no ParseGoHandle for %s", c.filename)
}
return computeOneImportFixEdits(c.ctx, c.view, ph, &imports.ImportFix{
return computeOneImportFixEdits(c.ctx, c.snapshot.View(), ph, &imports.ImportFix{
StmtInfo: imports.ImportInfo{
ImportPath: imp.importPath,
Name: imp.name,
@ -215,7 +215,7 @@ func (c *completer) formatBuiltin(cand candidate) CompletionItem {
item.Kind = protocol.ConstantCompletion
case *types.Builtin:
item.Kind = protocol.FunctionCompletion
builtin := c.view.BuiltinPackage().Lookup(obj.Name())
builtin := c.snapshot.View().BuiltinPackage().Lookup(obj.Name())
if obj == nil {
break
}
@ -223,8 +223,8 @@ func (c *completer) formatBuiltin(cand candidate) CompletionItem {
if !ok {
break
}
params, _ := formatFieldList(c.ctx, c.view, decl.Type.Params)
results, writeResultParens := formatFieldList(c.ctx, c.view, decl.Type.Results)
params, _ := formatFieldList(c.ctx, c.snapshot.View(), decl.Type.Params)
results, writeResultParens := formatFieldList(c.ctx, c.snapshot.View(), decl.Type.Results)
item.Label = obj.Name()
item.Detail = "func" + formatFunction(params, results, writeResultParens)
item.snippet = c.functionCallSnippet(obj.Name(), params)

View File

@ -104,7 +104,7 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
// If we are in a selector we must place the "&" before the selector.
// For example, "foo.B<>" must complete to "&foo.Bar{}", not
// "foo.&Bar{}".
edits, err := referenceEdit(c.view.Session().Cache().FileSet(), c.mapper, sel)
edits, err := referenceEdit(c.snapshot.View().Session().Cache().FileSet(), c.mapper, sel)
if err != nil {
log.Error(c.ctx, "error making edit for literal pointer completion", err)
return

View File

@ -41,7 +41,7 @@ func (c *completer) structFieldSnippet(label, detail string) *snippet.Builder {
}
})
fset := c.view.Session().Cache().FileSet()
fset := c.snapshot.View().Session().Cache().FileSet()
// If the cursor position is on a different line from the literal's opening brace,
// we are in a multiline literal.

View File

@ -39,11 +39,11 @@ type RelatedInformation struct {
Message string
}
func Diagnostics(ctx context.Context, view View, f File, disabledAnalyses map[string]struct{}) (map[span.URI][]Diagnostic, string, error) {
func Diagnostics(ctx context.Context, snapshot Snapshot, f File, disabledAnalyses map[string]struct{}) (map[span.URI][]Diagnostic, string, error) {
ctx, done := trace.StartSpan(ctx, "source.Diagnostics", telemetry.File.Of(f.URI()))
defer done()
snapshot, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, "", err
}
@ -56,7 +56,7 @@ func Diagnostics(ctx context.Context, view View, f File, disabledAnalyses map[st
// not correctly configured. Report errors, if possible.
var warningMsg string
if len(cph.MissingDependencies()) > 0 {
warningMsg, err = checkCommonErrors(ctx, view, f.URI())
warningMsg, err = checkCommonErrors(ctx, snapshot.View(), f.URI())
if err != nil {
log.Error(ctx, "error checking common errors", err, telemetry.File.Of(f.URI))
}
@ -70,7 +70,7 @@ func Diagnostics(ctx context.Context, view View, f File, disabledAnalyses map[st
// Prepare the reports we will send for the files in this package.
reports := make(map[span.URI][]Diagnostic)
for _, fh := range pkg.CompiledGoFiles() {
clearReports(view, reports, fh.File().Identity().URI)
clearReports(snapshot, reports, fh.File().Identity().URI)
}
// Prepare any additional reports for the errors in this package.
@ -78,27 +78,27 @@ func Diagnostics(ctx context.Context, view View, f File, disabledAnalyses map[st
if err.Kind != ListError {
continue
}
clearReports(view, reports, err.URI)
clearReports(snapshot, reports, err.URI)
}
// Run diagnostics for the package that this URI belongs to.
if !diagnostics(ctx, view, pkg, reports) {
if !diagnostics(ctx, pkg, reports) {
// If we don't have any list, parse, or type errors, run analyses.
if err := analyses(ctx, snapshot, cph, disabledAnalyses, reports); err != nil {
log.Error(ctx, "failed to run analyses", err, telemetry.File.Of(f.URI()))
}
}
// Updates to the diagnostics for this package may need to be propagated.
revDeps := view.GetActiveReverseDeps(ctx, f)
revDeps := snapshot.View().GetActiveReverseDeps(ctx, f)
for _, cph := range revDeps {
pkg, err := cph.Check(ctx)
if err != nil {
return nil, warningMsg, err
}
for _, fh := range pkg.CompiledGoFiles() {
clearReports(view, reports, fh.File().Identity().URI)
clearReports(snapshot, reports, fh.File().Identity().URI)
}
diagnostics(ctx, view, pkg, reports)
diagnostics(ctx, pkg, reports)
}
return reports, warningMsg, nil
}
@ -107,7 +107,7 @@ type diagnosticSet struct {
listErrors, parseErrors, typeErrors []*Diagnostic
}
func diagnostics(ctx context.Context, view View, pkg Package, reports map[span.URI][]Diagnostic) bool {
func diagnostics(ctx context.Context, pkg Package, reports map[span.URI][]Diagnostic) bool {
ctx, done := trace.StartSpan(ctx, "source.diagnostics", telemetry.Package.Of(pkg.ID()))
_ = ctx // circumvent SA4006
defer done()
@ -181,7 +181,7 @@ func analyses(ctx context.Context, snapshot Snapshot, cph CheckPackageHandle, di
if onlyDeletions(e.SuggestedFixes) {
tags = append(tags, protocol.Unnecessary)
}
addReport(snapshot.View(), reports, Diagnostic{
addReport(snapshot, reports, Diagnostic{
URI: e.URI,
Range: e.Range,
Message: e.Message,
@ -195,15 +195,15 @@ func analyses(ctx context.Context, snapshot Snapshot, cph CheckPackageHandle, di
return nil
}
func clearReports(v View, reports map[span.URI][]Diagnostic, uri span.URI) {
if v.Ignore(uri) {
func clearReports(snapshot Snapshot, reports map[span.URI][]Diagnostic, uri span.URI) {
if snapshot.View().Ignore(uri) {
return
}
reports[uri] = []Diagnostic{}
}
func addReport(v View, reports map[span.URI][]Diagnostic, diagnostic Diagnostic) {
if v.Ignore(diagnostic.URI) {
func addReport(snapshot Snapshot, reports map[span.URI][]Diagnostic, diagnostic Diagnostic) {
if snapshot.View().Ignore(diagnostic.URI) {
return
}
if _, ok := reports[diagnostic.URI]; ok {

View File

@ -16,19 +16,19 @@ type FoldingRangeInfo struct {
}
// FoldingRange gets all of the folding range for f.
func FoldingRange(ctx context.Context, view View, f File, lineFoldingOnly bool) (ranges []*FoldingRangeInfo, err error) {
func FoldingRange(ctx context.Context, snapshot Snapshot, f File, lineFoldingOnly bool) (ranges []*FoldingRangeInfo, err error) {
// TODO(suzmue): consider limiting the number of folding ranges returned, and
// implement a way to prioritize folding ranges in that case.
s := view.Snapshot()
fh := s.Handle(ctx, f)
ph := view.Session().Cache().ParseGoHandle(fh, ParseFull)
fh := snapshot.Handle(ctx, f)
ph := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
file, m, _, err := ph.Parse(ctx)
if err != nil {
return nil, err
}
fset := snapshot.View().Session().Cache().FileSet()
// Get folding ranges for comments separately as they are not walked by ast.Inspect.
ranges = append(ranges, commentsFoldingRange(view, m, file)...)
ranges = append(ranges, commentsFoldingRange(fset, m, file)...)
foldingFunc := foldingRange
if lineFoldingOnly {
@ -36,7 +36,7 @@ func FoldingRange(ctx context.Context, view View, f File, lineFoldingOnly bool)
}
visit := func(n ast.Node) bool {
rng := foldingFunc(view, m, n)
rng := foldingFunc(fset, m, n)
if rng != nil {
ranges = append(ranges, rng)
}
@ -55,7 +55,7 @@ func FoldingRange(ctx context.Context, view View, f File, lineFoldingOnly bool)
}
// foldingRange calculates the folding range for n.
func foldingRange(view View, m *protocol.ColumnMapper, n ast.Node) *FoldingRangeInfo {
func foldingRange(fset *token.FileSet, m *protocol.ColumnMapper, n ast.Node) *FoldingRangeInfo {
var kind protocol.FoldingRangeKind
var start, end token.Pos
switch n := n.(type) {
@ -85,15 +85,14 @@ func foldingRange(view View, m *protocol.ColumnMapper, n ast.Node) *FoldingRange
return &FoldingRangeInfo{
mappedRange: mappedRange{
m: m,
spanRange: span.NewRange(view.Session().Cache().FileSet(), start, end),
spanRange: span.NewRange(fset, start, end),
},
Kind: kind,
}
}
// lineFoldingRange calculates the line folding range for n.
func lineFoldingRange(view View, m *protocol.ColumnMapper, n ast.Node) *FoldingRangeInfo {
fset := view.Session().Cache().FileSet()
func lineFoldingRange(fset *token.FileSet, m *protocol.ColumnMapper, n ast.Node) *FoldingRangeInfo {
// TODO(suzmue): include trailing empty lines before the closing
// parenthesis/brace.
@ -183,7 +182,7 @@ func lineFoldingRange(view View, m *protocol.ColumnMapper, n ast.Node) *FoldingR
// commentsFoldingRange returns the folding ranges for all comment blocks in file.
// The folding range starts at the end of the first comment, and ends at the end of the
// comment block and has kind protocol.Comment.
func commentsFoldingRange(view View, m *protocol.ColumnMapper, file *ast.File) (comments []*FoldingRangeInfo) {
func commentsFoldingRange(fset *token.FileSet, m *protocol.ColumnMapper, file *ast.File) (comments []*FoldingRangeInfo) {
for _, commentGrp := range file.Comments {
// Don't fold single comments.
if len(commentGrp.List) <= 1 {
@ -193,7 +192,7 @@ func commentsFoldingRange(view View, m *protocol.ColumnMapper, file *ast.File) (
mappedRange: mappedRange{
m: m,
// Fold from the end of the first line comment to the end of the comment block.
spanRange: span.NewRange(view.Session().Cache().FileSet(), commentGrp.List[0].End(), commentGrp.End()),
spanRange: span.NewRange(fset, commentGrp.List[0].End(), commentGrp.End()),
},
Kind: protocol.Comment,
})

View File

@ -23,11 +23,11 @@ import (
)
// Format formats a file with a given range.
func Format(ctx context.Context, view View, f File) ([]protocol.TextEdit, error) {
func Format(ctx context.Context, snapshot Snapshot, f File) ([]protocol.TextEdit, error) {
ctx, done := trace.StartSpan(ctx, "source.Format")
defer done()
snapshot, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, err
}
@ -61,10 +61,10 @@ func Format(ctx context.Context, view View, f File) ([]protocol.TextEdit, error)
if err != nil {
return nil, err
}
return computeTextEdits(ctx, view, ph.File(), m, string(formatted))
return computeTextEdits(ctx, snapshot.View(), ph.File(), m, string(formatted))
}
fset := view.Session().Cache().FileSet()
fset := snapshot.View().Session().Cache().FileSet()
buf := &bytes.Buffer{}
// format.Node changes slightly from one release to another, so the version
@ -74,7 +74,7 @@ func Format(ctx context.Context, view View, f File) ([]protocol.TextEdit, error)
if err := format.Node(buf, fset, file); err != nil {
return nil, err
}
return computeTextEdits(ctx, view, ph.File(), m, buf.String())
return computeTextEdits(ctx, snapshot.View(), ph.File(), m, buf.String())
}
func formatSource(ctx context.Context, s Snapshot, f File) ([]byte, error) {
@ -97,11 +97,11 @@ type ImportFix struct {
// In addition to returning the result of applying all edits,
// it returns a list of fixes that could be applied to the file, with the
// corresponding TextEdits that would be needed to apply that fix.
func AllImportsFixes(ctx context.Context, view View, f File) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
func AllImportsFixes(ctx context.Context, snapshot Snapshot, f File) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
ctx, done := trace.StartSpan(ctx, "source.AllImportsFixes")
defer done()
_, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, nil, err
}
@ -135,8 +135,8 @@ func AllImportsFixes(ctx context.Context, view View, f File) (allFixEdits []prot
TabIndent: true,
TabWidth: 8,
}
err = view.RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
allFixEdits, editsPerFix, err = computeImportEdits(ctx, view, ph, opts)
err = snapshot.View().RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
allFixEdits, editsPerFix, err = computeImportEdits(ctx, snapshot.View(), ph, opts)
return err
}, options)
if err != nil {

View File

@ -10,20 +10,15 @@ import (
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors"
)
func Highlight(ctx context.Context, view View, uri span.URI, pos protocol.Position) ([]protocol.Range, error) {
func Highlight(ctx context.Context, snapshot Snapshot, f File, pos protocol.Position) ([]protocol.Range, error) {
ctx, done := trace.StartSpan(ctx, "source.Highlight")
defer done()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
_, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, err
}
@ -79,13 +74,11 @@ func Highlight(ctx context.Context, view View, uri span.URI, pos protocol.Positi
if n.Obj != id.Obj {
return true
}
nodeObj := pkg.GetTypesInfo().ObjectOf(n)
if nodeObj != idObj {
return false
}
if rng, err := nodeToProtocolRange(ctx, view, m, n); err == nil {
if rng, err := nodeToProtocolRange(ctx, snapshot.View(), m, n); err == nil {
result = append(result, rng)
}
return true

View File

@ -46,8 +46,8 @@ type Declaration struct {
// Identifier returns identifier information for a position
// in a file, accounting for a potentially incomplete selector.
func Identifier(ctx context.Context, view View, f File, pos protocol.Position) (*IdentifierInfo, error) {
snapshot, cphs, err := view.CheckPackageHandles(ctx, f)
func Identifier(ctx context.Context, snapshot Snapshot, f File, pos protocol.Position) (*IdentifierInfo, error) {
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, err
}

View File

@ -27,11 +27,11 @@ type ParameterInformation struct {
Label string
}
func SignatureHelp(ctx context.Context, view View, f File, pos protocol.Position) (*SignatureInformation, error) {
func SignatureHelp(ctx context.Context, snapshot Snapshot, f File, pos protocol.Position) (*SignatureInformation, error) {
ctx, done := trace.StartSpan(ctx, "source.SignatureHelp")
defer done()
_, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, err
}
@ -97,7 +97,7 @@ FindCall:
// Handle builtin functions separately.
if obj, ok := obj.(*types.Builtin); ok {
return builtinSignature(ctx, view, callExpr, obj.Name(), rng.Start)
return builtinSignature(ctx, snapshot.View(), callExpr, obj.Name(), rng.Start)
}
// Get the type information for the function being called.
@ -121,11 +121,11 @@ FindCall:
comment *ast.CommentGroup
)
if obj != nil {
node, err := objToNode(ctx, view, pkg, obj)
node, err := objToNode(ctx, snapshot.View(), pkg, obj)
if err != nil {
return nil, err
}
rng, err := objToMappedRange(ctx, view, pkg, obj)
rng, err := objToMappedRange(ctx, snapshot.View(), pkg, obj)
if err != nil {
return nil, err
}

View File

@ -71,7 +71,7 @@ func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []source.Diagnosti
if err != nil {
t.Fatal(err)
}
results, _, err := source.Diagnostics(r.ctx, r.view, f, nil)
results, _, err := source.Diagnostics(r.ctx, r.view.Snapshot(), f, nil)
if err != nil {
t.Fatal(err)
}
@ -239,7 +239,7 @@ func (r *runner) callCompletion(t *testing.T, src span.Span, options source.Comp
if err != nil {
t.Fatal(err)
}
list, surrounding, err := source.Completion(r.ctx, r.view, f, protocol.Position{
list, surrounding, err := source.Completion(r.ctx, r.view.Snapshot(), f, protocol.Position{
Line: float64(src.Start().Line() - 1),
Character: float64(src.Start().Column() - 1),
}, options)
@ -288,7 +288,7 @@ func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
}
// Test all folding ranges.
ranges, err := source.FoldingRange(r.ctx, r.view, f, false)
ranges, err := source.FoldingRange(r.ctx, r.view.Snapshot(), f, false)
if err != nil {
t.Error(err)
return
@ -296,7 +296,7 @@ func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
r.foldingRanges(t, "foldingRange", uri, string(data), ranges)
// Test folding ranges with lineFoldingOnly
ranges, err = source.FoldingRange(r.ctx, r.view, f, true)
ranges, err = source.FoldingRange(r.ctx, r.view.Snapshot(), f, true)
if err != nil {
t.Error(err)
return
@ -424,7 +424,7 @@ func (r *runner) Format(t *testing.T, spn span.Span) {
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
}
edits, err := source.Format(ctx, r.view, f)
edits, err := source.Format(ctx, r.view.Snapshot(), f)
if err != nil {
if gofmted != "" {
t.Error(err)
@ -459,7 +459,7 @@ func (r *runner) Import(t *testing.T, spn span.Span) {
t.Fatalf("failed for %v: %v", spn, err)
}
fh := r.view.Snapshot().Handle(r.ctx, f)
edits, _, err := source.AllImportsFixes(ctx, r.view, f)
edits, _, err := source.AllImportsFixes(ctx, r.view.Snapshot(), f)
if err != nil {
t.Error(err)
}
@ -497,7 +497,7 @@ func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
if err != nil {
t.Fatal(err)
}
ident, err := source.Identifier(ctx, r.view, f, srcRng.Start)
ident, err := source.Identifier(ctx, r.view.Snapshot(), f, srcRng.Start)
if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err)
}
@ -559,7 +559,7 @@ func (r *runner) Implementation(t *testing.T, spn span.Span, m tests.Implementat
if err != nil {
t.Fatalf("failed for %v: %v", m.Src, err)
}
ident, err := source.Identifier(ctx, r.view, f, loc.Range.Start)
ident, err := source.Identifier(ctx, r.view.Snapshot(), f, loc.Range.Start)
if err != nil {
t.Fatalf("failed for %v: %v", m.Src, err)
}
@ -591,7 +591,11 @@ func (r *runner) Highlight(t *testing.T, src span.Span, locations []span.Span) {
if err != nil {
t.Fatal(err)
}
highlights, err := source.Highlight(ctx, r.view, src.URI(), srcRng.Start)
f, err := r.view.GetFile(ctx, src.URI())
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
}
highlights, err := source.Highlight(ctx, r.view.Snapshot(), f, srcRng.Start)
if err != nil {
t.Errorf("highlight failed for %s: %v", src.URI(), err)
}
@ -619,7 +623,7 @@ func (r *runner) References(t *testing.T, src span.Span, itemList []span.Span) {
if err != nil {
t.Fatal(err)
}
ident, err := source.Identifier(ctx, r.view, f, srcRng.Start)
ident, err := source.Identifier(ctx, r.view.Snapshot(), f, srcRng.Start)
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
}
@ -666,7 +670,7 @@ func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
if err != nil {
t.Fatal(err)
}
ident, err := source.Identifier(r.ctx, r.view, f, srcRng.Start)
ident, err := source.Identifier(r.ctx, r.view.Snapshot(), f, srcRng.Start)
if err != nil {
t.Error(err)
return
@ -755,7 +759,7 @@ func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.Prepare
t.Fatal(err)
}
// Find the identifier at the position.
ident, err := source.Identifier(ctx, r.view, f, srcRng.Start)
ident, err := source.Identifier(ctx, r.view.Snapshot(), f, srcRng.Start)
if err != nil {
if want.Text != "" { // expected an ident.
t.Errorf("prepare rename failed for %v: got error: %v", src, err)
@ -798,7 +802,7 @@ func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.
if err != nil {
t.Fatalf("failed for %v: %v", uri, err)
}
symbols, err := source.DocumentSymbols(ctx, r.view, f)
symbols, err := source.DocumentSymbols(ctx, r.view.Snapshot(), f)
if err != nil {
t.Errorf("symbols failed for %s: %v", uri, err)
}
@ -864,7 +868,7 @@ func (r *runner) SignatureHelp(t *testing.T, spn span.Span, expectedSignature *s
if err != nil {
t.Fatal(err)
}
gotSignature, err := source.SignatureHelp(ctx, r.view, f, rng.Start)
gotSignature, err := source.SignatureHelp(ctx, r.view.Snapshot(), f, rng.Start)
if err != nil {
// Only fail if we got an error we did not expect.
if expectedSignature != nil {

View File

@ -14,11 +14,11 @@ import (
"golang.org/x/tools/internal/telemetry/trace"
)
func DocumentSymbols(ctx context.Context, view View, f File) ([]protocol.DocumentSymbol, error) {
func DocumentSymbols(ctx context.Context, snapshot Snapshot, f File) ([]protocol.DocumentSymbol, error) {
ctx, done := trace.StartSpan(ctx, "source.DocumentSymbols")
defer done()
_, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := snapshot.PackageHandles(ctx, f)
if err != nil {
return nil, err
}
@ -49,7 +49,7 @@ func DocumentSymbols(ctx context.Context, view View, f File) ([]protocol.Documen
switch decl := decl.(type) {
case *ast.FuncDecl:
if obj := info.ObjectOf(decl.Name); obj != nil {
if fs := funcSymbol(ctx, view, m, decl, obj, q); fs.Kind == protocol.Method {
if fs := funcSymbol(ctx, snapshot.View(), m, decl, obj, q); fs.Kind == protocol.Method {
// Store methods separately, as we want them to appear as children
// of the corresponding type (which we may not have seen yet).
rtype := obj.Type().(*types.Signature).Recv().Type()
@ -63,14 +63,14 @@ func DocumentSymbols(ctx context.Context, view View, f File) ([]protocol.Documen
switch spec := spec.(type) {
case *ast.TypeSpec:
if obj := info.ObjectOf(spec.Name); obj != nil {
ts := typeSymbol(ctx, view, m, info, spec, obj, q)
ts := typeSymbol(ctx, snapshot.View(), m, info, spec, obj, q)
symbols = append(symbols, ts)
symbolsToReceiver[obj.Type()] = len(symbols) - 1
}
case *ast.ValueSpec:
for _, name := range spec.Names {
if obj := info.ObjectOf(name); obj != nil {
symbols = append(symbols, varSymbol(ctx, view, m, decl, name, obj, q))
symbols = append(symbols, varSymbol(ctx, snapshot.View(), m, decl, name, obj, q))
}
}
}

View File

@ -18,98 +18,35 @@ import (
"golang.org/x/tools/internal/span"
)
// FileIdentity uniquely identifies a file at a version from a FileSystem.
type FileIdentity struct {
URI span.URI
// Snapshot represents the current state for the given view.
type Snapshot interface {
ID() uint64
// Version is the version of the file, as specified by the client.
Version float64
// Handle returns the FileHandle for the given file.
Handle(ctx context.Context, f File) FileHandle
// 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
// View returns the View associated with this snapshot.
View() View
// Kind is the file's kind.
Kind FileKind
// Analyze runs the analyses for the given package at this snapshot.
Analyze(ctx context.Context, id string, analyzers []*analysis.Analyzer) ([]*Error, error)
// FindAnalysisError returns the analysis error represented by the diagnostic.
// This is used to get the SuggestedFixes associated with that error.
FindAnalysisError(ctx context.Context, id string, diag protocol.Diagnostic) (*Error, error)
// PackageHandles returns the PackageHandles for the packages
// that this file belongs to.
PackageHandles(ctx context.Context, f File) ([]CheckPackageHandle, error)
// KnownImportPaths returns all the imported packages loaded in this snapshot,
// indexed by their import path.
KnownImportPaths() map[string]Package
// KnownPackages returns all the packages loaded in this snapshot.
KnownPackages(ctx context.Context) []Package
}
func (identity FileIdentity) String() string {
return fmt.Sprintf("%s%f%s%s", identity.URI, identity.Version, identity.Identifier, identity.Kind)
}
// FileHandle represents a handle to a specific version of a single file from
// a specific file system.
type FileHandle interface {
// FileSystem returns the file system this handle was acquired from.
FileSystem() FileSystem
// Identity returns the FileIdentity for the file.
Identity() FileIdentity
// Read reads the contents of a file and returns it along with its hash value.
// If the file is not available, returns a nil slice and an error.
Read(ctx context.Context) ([]byte, string, error)
}
// FileSystem is the interface to something that provides file contents.
type FileSystem interface {
// GetFile returns a handle for the specified file.
GetFile(uri span.URI, kind FileKind) FileHandle
}
// File represents a source file of any type.
type File interface {
URI() span.URI
Kind() FileKind
}
// FileKind describes the kind of the file in question.
// It can be one of Go, mod, or sum.
type FileKind int
const (
Go = FileKind(iota)
Mod
Sum
UnknownKind
)
// ParseGoHandle represents a handle to the AST for a file.
type ParseGoHandle interface {
// File returns a file handle for which to get the AST.
File() FileHandle
// Mode returns the parse mode of this handle.
Mode() ParseMode
// Parse returns the parsed AST for the file.
// If the file is not available, returns nil and an error.
Parse(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error, error)
// Cached returns the AST for this handle, if it has already been stored.
Cached() (*ast.File, *protocol.ColumnMapper, error, error)
}
// ParseMode controls the content of the AST produced when parsing a source file.
type ParseMode int
const (
// ParseHeader specifies that the main package declaration and imports are needed.
// This is the mode used when attempting to examine the package graph structure.
ParseHeader = ParseMode(iota)
// ParseExported specifies that the public symbols are needed, but things like
// private symbols and function bodies are not.
// This mode is used for things where a package is being consumed only as a
// dependency.
ParseExported
// ParseFull specifies the full AST is needed.
// This is used for files of direct interest where the entire contents must
// be considered.
ParseFull
)
// CheckPackageHandle represents a handle to a specific version of a package.
// It is uniquely defined by the file handles that make up the package.
type CheckPackageHandle interface {
@ -129,25 +66,69 @@ type CheckPackageHandle interface {
MissingDependencies() []string
}
// Cache abstracts the core logic of dealing with the environment from the
// higher level logic that processes the information to produce results.
// The cache provides access to files and their contents, so the source
// package does not directly access the file system.
// A single cache is intended to be process wide, and is the primary point of
// sharing between all consumers.
// A cache may have many active sessions at any given time.
type Cache interface {
// A FileSystem that reads file contents from external storage.
FileSystem
// View represents a single workspace.
// This is the level at which we maintain configuration like working directory
// and build tags.
type View interface {
// Session returns the session that created this view.
Session() Session
// NewSession creates a new Session manager and returns it.
NewSession(ctx context.Context) Session
// Name returns the name this view was constructed with.
Name() string
// FileSet returns the shared fileset used by all files in the system.
FileSet() *token.FileSet
// Folder returns the root folder for this view.
Folder() span.URI
// ParseGoHandle returns a ParseGoHandle for the given file handle.
ParseGoHandle(fh FileHandle, mode ParseMode) ParseGoHandle
// BuiltinPackage returns the type information for the special "builtin" package.
BuiltinPackage() BuiltinPackage
// GetFile returns the file object for a given URI, initializing it
// if it is not already part of the view.
GetFile(ctx context.Context, uri span.URI) (File, error)
// FindFile returns the file object for a given URI if it is
// already part of the view.
FindFile(ctx context.Context, uri span.URI) File
// Called to set the effective contents of a file from this view.
SetContent(ctx context.Context, uri span.URI, version float64, content []byte) (wasFirstChange bool, err error)
// BackgroundContext returns a context used for all background processing
// on behalf of this view.
BackgroundContext() context.Context
// Shutdown closes this view, and detaches it from it's session.
Shutdown(ctx context.Context)
// Ignore returns true if this file should be ignored by this view.
Ignore(span.URI) bool
// Config returns the configuration for the view.
Config(ctx context.Context) *packages.Config
// RunProcessEnvFunc runs fn with the process env for this view inserted into opts.
// Note: the process env contains cached module and filesystem state.
RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error, opts *imports.Options) error
// Options returns a copy of the Options for this view.
Options() Options
// SetOptions sets the options of this view to new values.
// Calling this may cause the view to be invalidated and a replacement view
// added to the session. If so the new view will be returned, otherwise the
// original one will be.
SetOptions(context.Context, Options) (View, error)
// GetActiveReverseDeps returns the active files belonging to the reverse
// dependencies of this file's package.
GetActiveReverseDeps(ctx context.Context, f File) []CheckPackageHandle
// FindFileInPackage returns the AST and type information for a file that may
// belong to or be part of a dependency of the given package.
FindPosInPackage(pkg Package, pos token.Pos) (*ast.File, *protocol.ColumnMapper, Package, error)
// Snapshot returns the current snapshot for the view.
Snapshot() Snapshot
}
// Session represents a single connection from a client.
@ -214,104 +195,119 @@ const (
UnknownFileAction
)
// View represents a single workspace.
// This is the level at which we maintain configuration like working directory
// and build tags.
type View interface {
// Session returns the session that created this view.
Session() Session
// Cache abstracts the core logic of dealing with the environment from the
// higher level logic that processes the information to produce results.
// The cache provides access to files and their contents, so the source
// package does not directly access the file system.
// A single cache is intended to be process wide, and is the primary point of
// sharing between all consumers.
// A cache may have many active sessions at any given time.
type Cache interface {
// A FileSystem that reads file contents from external storage.
FileSystem
// Name returns the name this view was constructed with.
Name() string
// NewSession creates a new Session manager and returns it.
NewSession(ctx context.Context) Session
// Folder returns the root folder for this view.
Folder() span.URI
// FileSet returns the shared fileset used by all files in the system.
FileSet() *token.FileSet
// BuiltinPackage returns the type information for the special "builtin" package.
BuiltinPackage() BuiltinPackage
// GetFile returns the file object for a given URI, initializing it
// if it is not already part of the view.
GetFile(ctx context.Context, uri span.URI) (File, error)
// FindFile returns the file object for a given URI if it is
// already part of the view.
FindFile(ctx context.Context, uri span.URI) File
// Called to set the effective contents of a file from this view.
SetContent(ctx context.Context, uri span.URI, version float64, content []byte) (wasFirstChange bool, err error)
// BackgroundContext returns a context used for all background processing
// on behalf of this view.
BackgroundContext() context.Context
// Shutdown closes this view, and detaches it from it's session.
Shutdown(ctx context.Context)
// Ignore returns true if this file should be ignored by this view.
Ignore(span.URI) bool
// Config returns the configuration for the view.
Config(ctx context.Context) *packages.Config
// RunProcessEnvFunc runs fn with the process env for this view inserted into opts.
// Note: the process env contains cached module and filesystem state.
RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error, opts *imports.Options) error
// Options returns a copy of the Options for this view.
Options() Options
// SetOptions sets the options of this view to new values.
// Calling this may cause the view to be invalidated and a replacement view
// added to the session. If so the new view will be returned, otherwise the
// original one will be.
SetOptions(context.Context, Options) (View, error)
// CheckPackageHandles returns the CheckPackageHandles for the packages
// that this file belongs to.
CheckPackageHandles(ctx context.Context, f File) (Snapshot, []CheckPackageHandle, error)
// GetActiveReverseDeps returns the active files belonging to the reverse
// dependencies of this file's package.
GetActiveReverseDeps(ctx context.Context, f File) []CheckPackageHandle
// FindFileInPackage returns the AST and type information for a file that may
// belong to or be part of a dependency of the given package.
FindPosInPackage(pkg Package, pos token.Pos) (*ast.File, *protocol.ColumnMapper, Package, error)
// Snapshot returns the current snapshot for the view.
Snapshot() Snapshot
// ParseGoHandle returns a ParseGoHandle for the given file handle.
ParseGoHandle(fh FileHandle, mode ParseMode) ParseGoHandle
}
// Snapshot represents the current state for the given view.
type Snapshot interface {
ID() uint64
// Handle returns the FileHandle for the given file.
Handle(ctx context.Context, f File) FileHandle
// View returns the View associated with this snapshot.
View() View
// Analyze runs the analyses for the given package at this snapshot.
Analyze(ctx context.Context, id string, analyzers []*analysis.Analyzer) ([]*Error, error)
// FindAnalysisError returns the analysis error represented by the diagnostic.
// This is used to get the SuggestedFixes associated with that error.
FindAnalysisError(ctx context.Context, id string, diag protocol.Diagnostic) (*Error, error)
// CheckPackageHandles returns the CheckPackageHandles for the packages
// that this file belongs to.
CheckPackageHandles(ctx context.Context, f File) ([]CheckPackageHandle, error)
// KnownImportPaths returns all the imported packages loaded in this snapshot,
// indexed by their import path.
KnownImportPaths() map[string]Package
// KnownPackages returns all the packages loaded in this snapshot.
KnownPackages(ctx context.Context) []Package
// FileSystem is the interface to something that provides file contents.
type FileSystem interface {
// GetFile returns a handle for the specified file.
GetFile(uri span.URI, kind FileKind) FileHandle
}
// ParseGoHandle represents a handle to the AST for a file.
type ParseGoHandle interface {
// File returns a file handle for which to get the AST.
File() FileHandle
// Mode returns the parse mode of this handle.
Mode() ParseMode
// Parse returns the parsed AST for the file.
// If the file is not available, returns nil and an error.
Parse(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error, error)
// Cached returns the AST for this handle, if it has already been stored.
Cached() (*ast.File, *protocol.ColumnMapper, error, error)
}
// ParseMode controls the content of the AST produced when parsing a source file.
type ParseMode int
const (
// ParseHeader specifies that the main package declaration and imports are needed.
// This is the mode used when attempting to examine the package graph structure.
ParseHeader = ParseMode(iota)
// ParseExported specifies that the public symbols are needed, but things like
// private symbols and function bodies are not.
// This mode is used for things where a package is being consumed only as a
// dependency.
ParseExported
// ParseFull specifies the full AST is needed.
// This is used for files of direct interest where the entire contents must
// be considered.
ParseFull
)
// FileHandle represents a handle to a specific version of a single file from
// a specific file system.
type FileHandle interface {
// FileSystem returns the file system this handle was acquired from.
FileSystem() FileSystem
// Identity returns the FileIdentity for the file.
Identity() FileIdentity
// Read reads the contents of a file and returns it along with its hash value.
// If the file is not available, returns a nil slice and an error.
Read(ctx context.Context) ([]byte, string, error)
}
// FileIdentity uniquely identifies a file at a version from a FileSystem.
type FileIdentity struct {
URI span.URI
// Version is the version of the file, as specified by the client.
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
// Kind is the file's kind.
Kind FileKind
}
func (identity FileIdentity) String() string {
return fmt.Sprintf("%s%f%s%s", identity.URI, identity.Version, identity.Identifier, identity.Kind)
}
// File represents a source file of any type.
type File interface {
URI() span.URI
Kind() FileKind
}
// FileKind describes the kind of the file in question.
// It can be one of Go, mod, or sum.
type FileKind int
const (
Go = FileKind(iota)
Mod
Sum
UnknownKind
)
type FileURI span.URI
func (f FileURI) URI() span.URI {

View File

@ -22,9 +22,10 @@ func (s *Server) documentSymbol(ctx context.Context, params *protocol.DocumentSy
if err != nil {
return nil, err
}
snapshot := view.Snapshot()
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
}
return source.DocumentSymbols(ctx, view, f)
return source.DocumentSymbols(ctx, snapshot, f)
}

View File

@ -33,9 +33,10 @@ func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocume
if err != nil {
return err
}
snapshot := view.Snapshot()
// Run diagnostics on the newly-changed file.
go s.diagnostics(view, uri)
go s.diagnostics(snapshot, uri)
return nil
}
@ -71,6 +72,8 @@ func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDo
if err != nil {
return err
}
snapshot := view.Snapshot()
wasFirstChange, err := view.SetContent(ctx, uri, params.TextDocument.Version, []byte(text))
if err != nil {
return err
@ -86,7 +89,7 @@ func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDo
}
// Run diagnostics on the newly-changed file.
go s.diagnostics(view, uri)
go s.diagnostics(snapshot, uri)
return nil
}
@ -169,7 +172,7 @@ func (s *Server) didClose(ctx context.Context, params *protocol.DidCloseTextDocu
log.Error(ctx, "no file", err, telemetry.URI)
return nil
}
_, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := view.Snapshot().PackageHandles(ctx, f)
if err != nil {
log.Error(ctx, "no CheckPackageHandles", err, telemetry.URI.Of(uri))
return nil

View File

@ -34,7 +34,7 @@ func (s *Server) didChangeWatchedFiles(ctx context.Context, params *protocol.Did
if s.session.DidChangeOutOfBand(ctx, uri, action) {
// If we had been tracking the given file,
// recompute diagnostics to reflect updated file contents.
go s.diagnostics(view, uri)
go s.diagnostics(view.Snapshot(), uri)
}
case source.Delete:
f := view.FindFile(ctx, uri)
@ -42,7 +42,7 @@ func (s *Server) didChangeWatchedFiles(ctx context.Context, params *protocol.Did
if f == nil {
continue
}
_, cphs, err := view.CheckPackageHandles(ctx, f)
cphs, err := view.Snapshot().PackageHandles(ctx, f)
if err != nil {
log.Error(ctx, "didChangeWatchedFiles: CheckPackageHandles", err, telemetry.File)
continue
@ -77,7 +77,7 @@ func (s *Server) didChangeWatchedFiles(ctx context.Context, params *protocol.Did
}
// Refresh diagnostics for the package the file belonged to.
go s.diagnostics(view, otherFile.URI())
go s.diagnostics(view.Snapshot(), otherFile.URI())
}
}
}

View File

@ -28,7 +28,7 @@ func (s *Server) changeFolders(ctx context.Context, event protocol.WorkspaceFold
if err != nil {
return err
}
go s.diagnoseView(view, cphs)
go s.diagnoseSnapshot(view.Snapshot(), cphs)
}
return nil
}