2018-11-02 16:10:49 -06:00
|
|
|
// Copyright 2018 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 source
|
|
|
|
|
|
|
|
import (
|
2018-12-18 14:18:03 -07:00
|
|
|
"context"
|
2018-11-02 16:10:49 -06:00
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
2019-03-06 14:33:47 -07:00
|
|
|
"go/types"
|
2019-07-15 15:02:40 -06:00
|
|
|
"sort"
|
2019-04-08 07:22:58 -06:00
|
|
|
"strings"
|
2018-11-07 13:21:31 -07:00
|
|
|
|
2019-03-06 14:33:47 -07:00
|
|
|
"golang.org/x/tools/go/analysis"
|
2018-11-07 13:21:31 -07:00
|
|
|
"golang.org/x/tools/go/packages"
|
2019-07-03 13:23:05 -06:00
|
|
|
"golang.org/x/tools/internal/imports"
|
2019-04-08 07:22:58 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/diff"
|
2019-02-19 19:11:15 -07:00
|
|
|
"golang.org/x/tools/internal/span"
|
2018-11-02 16:10:49 -06:00
|
|
|
)
|
|
|
|
|
2019-05-31 17:41:39 -06:00
|
|
|
// FileIdentity uniquely identifies a file at a version from a FileSystem.
|
|
|
|
type FileIdentity struct {
|
|
|
|
URI span.URI
|
|
|
|
Version string
|
|
|
|
}
|
|
|
|
|
|
|
|
// FileHandle represents a handle to a specific version of a single file from
|
|
|
|
// a specific file system.
|
|
|
|
type FileHandle interface {
|
2019-06-03 23:04:18 -06:00
|
|
|
// FileSystem returns the file system this handle was acquired from.
|
2019-05-31 17:41:39 -06:00
|
|
|
FileSystem() FileSystem
|
2019-06-13 13:55:53 -06:00
|
|
|
|
2019-06-21 15:00:02 -06:00
|
|
|
// Identity returns the FileIdentity for the file.
|
2019-05-31 17:41:39 -06:00
|
|
|
Identity() FileIdentity
|
2019-06-13 13:55:53 -06:00
|
|
|
|
2019-06-21 15:00:02 -06:00
|
|
|
// Kind returns the FileKind for the file.
|
|
|
|
Kind() FileKind
|
|
|
|
|
2019-06-13 13:55:53 -06:00
|
|
|
// 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.
|
2019-06-03 23:04:18 -06:00
|
|
|
Read(ctx context.Context) ([]byte, string, error)
|
2019-05-31 17:41:39 -06:00
|
|
|
}
|
|
|
|
|
2019-05-17 10:15:22 -06:00
|
|
|
// FileSystem is the interface to something that provides file contents.
|
|
|
|
type FileSystem interface {
|
2019-05-31 17:41:39 -06:00
|
|
|
// GetFile returns a handle for the specified file.
|
|
|
|
GetFile(uri span.URI) FileHandle
|
2019-05-17 10:15:22 -06:00
|
|
|
}
|
|
|
|
|
2019-06-21 15:00:02 -06:00
|
|
|
// 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
|
|
|
|
)
|
|
|
|
|
2019-06-13 13:55:53 -06:00
|
|
|
// TokenHandle represents a handle to the *token.File for a file.
|
|
|
|
type TokenHandle interface {
|
|
|
|
// File returns a file handle for which to get the *token.File.
|
|
|
|
File() FileHandle
|
|
|
|
|
|
|
|
// Token returns the *token.File for the file.
|
|
|
|
Token(ctx context.Context) (*token.File, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseGoHandle represents a handle to the AST for a file.
|
2019-06-04 20:14:37 -06:00
|
|
|
type ParseGoHandle interface {
|
2019-06-13 13:55:53 -06:00
|
|
|
// File returns a file handle for which to get the AST.
|
2019-06-04 20:14:37 -06:00
|
|
|
File() FileHandle
|
2019-06-13 13:55:53 -06:00
|
|
|
|
2019-06-04 20:14:37 -06:00
|
|
|
// Mode returns the parse mode of this handle.
|
|
|
|
Mode() ParseMode
|
2019-06-13 13:55:53 -06:00
|
|
|
|
2019-06-04 20:14:37 -06:00
|
|
|
// 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, error)
|
2019-08-06 16:51:17 -06:00
|
|
|
|
|
|
|
// Cached returns the AST for this handle, if it has already been stored.
|
|
|
|
Cached(ctx context.Context) (*ast.File, error)
|
2019-06-04 20:14:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2019-06-13 13:55:53 -06:00
|
|
|
|
2019-06-04 20:14:37 -06:00
|
|
|
// 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
|
2019-06-13 13:55:53 -06:00
|
|
|
|
2019-06-04 20:14:37 -06:00
|
|
|
// ParseFull specifies the full AST is needed.
|
|
|
|
// This is used for files of direct interest where the entire contents must
|
|
|
|
// be considered.
|
|
|
|
ParseFull
|
|
|
|
)
|
|
|
|
|
2019-05-15 10:24:49 -06:00
|
|
|
// 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
|
2018-12-18 13:46:14 -07:00
|
|
|
// package does not directly access the file system.
|
2019-05-15 10:24:49 -06:00
|
|
|
// 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 {
|
2019-05-17 10:15:22 -06:00
|
|
|
// A FileSystem that reads file contents from external storage.
|
|
|
|
FileSystem
|
|
|
|
|
2019-05-15 10:24:49 -06:00
|
|
|
// NewSession creates a new Session manager and returns it.
|
2019-07-10 19:01:12 -06:00
|
|
|
NewSession(ctx context.Context) Session
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// FileSet returns the shared fileset used by all files in the system.
|
|
|
|
FileSet() *token.FileSet
|
2019-06-04 20:14:37 -06:00
|
|
|
|
2019-06-13 13:55:53 -06:00
|
|
|
// Token returns a TokenHandle for the given file handle.
|
2019-08-06 16:51:17 -06:00
|
|
|
TokenHandle(fh FileHandle) TokenHandle
|
2019-06-13 13:55:53 -06:00
|
|
|
|
|
|
|
// ParseGo returns a ParseGoHandle for the given file handle.
|
2019-08-06 16:51:17 -06:00
|
|
|
ParseGoHandle(fh FileHandle, mode ParseMode) ParseGoHandle
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Session represents a single connection from a client.
|
|
|
|
// This is the level at which things like open files are maintained on behalf
|
|
|
|
// of the client.
|
|
|
|
// A session may have many active views at any given time.
|
|
|
|
type Session interface {
|
|
|
|
// NewView creates a new View and returns it.
|
2019-07-10 19:11:23 -06:00
|
|
|
NewView(ctx context.Context, name string, folder span.URI) View
|
2019-05-15 10:24:49 -06:00
|
|
|
|
|
|
|
// Cache returns the cache that created this session.
|
|
|
|
Cache() Cache
|
|
|
|
|
2019-05-17 08:51:19 -06:00
|
|
|
// View returns a view with a mathing name, if the session has one.
|
2019-05-15 10:24:49 -06:00
|
|
|
View(name string) View
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// ViewOf returns a view corresponding to the given URI.
|
2019-05-15 10:24:49 -06:00
|
|
|
ViewOf(uri span.URI) View
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// Views returns the set of active views built by this session.
|
2019-05-15 10:24:49 -06:00
|
|
|
Views() []View
|
|
|
|
|
2019-05-17 08:51:19 -06:00
|
|
|
// Shutdown the session and all views it has created.
|
2019-05-15 10:24:49 -06:00
|
|
|
Shutdown(ctx context.Context)
|
2019-05-17 10:15:22 -06:00
|
|
|
|
|
|
|
// A FileSystem prefers the contents from overlays, and falls back to the
|
|
|
|
// content from the underlying cache if no overlay is present.
|
|
|
|
FileSystem
|
|
|
|
|
|
|
|
// DidOpen is invoked each time a file is opened in the editor.
|
2019-06-27 12:59:09 -06:00
|
|
|
DidOpen(ctx context.Context, uri span.URI, kind FileKind, text []byte)
|
2019-05-17 10:15:22 -06:00
|
|
|
|
|
|
|
// DidSave is invoked each time an open file is saved in the editor.
|
|
|
|
DidSave(uri span.URI)
|
|
|
|
|
|
|
|
// DidClose is invoked each time an open file is closed in the editor.
|
|
|
|
DidClose(uri span.URI)
|
|
|
|
|
|
|
|
// IsOpen can be called to check if the editor has a file currently open.
|
|
|
|
IsOpen(uri span.URI) bool
|
|
|
|
|
|
|
|
// Called to set the effective contents of a file from this session.
|
|
|
|
SetOverlay(uri span.URI, data []byte)
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// View represents a single workspace.
|
|
|
|
// This is the level at which we maintain configuration like working directory
|
|
|
|
// and build tags.
|
2018-12-18 13:46:14 -07:00
|
|
|
type View interface {
|
2019-05-15 10:24:49 -06:00
|
|
|
// Session returns the session that created this view.
|
|
|
|
Session() Session
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// Name returns the name this view was constructed with.
|
2019-05-14 21:04:23 -06:00
|
|
|
Name() string
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// Folder returns the root folder for this view.
|
2019-05-14 21:04:23 -06:00
|
|
|
Folder() span.URI
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// BuiltinPackage returns the ast for the special "builtin" package.
|
2019-04-29 19:08:16 -06:00
|
|
|
BuiltinPackage() *ast.Package
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// GetFile returns the file object for a given uri.
|
2019-02-19 19:11:15 -07:00
|
|
|
GetFile(ctx context.Context, uri span.URI) (File, error)
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// Called to set the effective contents of a file from this view.
|
2019-02-19 19:11:15 -07:00
|
|
|
SetContent(ctx context.Context, uri span.URI, content []byte) error
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// BackgroundContext returns a context used for all background processing
|
|
|
|
// on behalf of this view.
|
2019-05-14 21:04:23 -06:00
|
|
|
BackgroundContext() context.Context
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// Env returns the current set of environment overrides on this view.
|
|
|
|
Env() []string
|
|
|
|
|
|
|
|
// SetEnv is used to adjust the environment applied to the view.
|
2019-05-14 21:04:23 -06:00
|
|
|
SetEnv([]string)
|
2019-05-17 08:51:19 -06:00
|
|
|
|
2019-05-24 06:32:26 -06:00
|
|
|
// SetBuildFlags is used to adjust the build flags applied to the view.
|
|
|
|
SetBuildFlags([]string)
|
|
|
|
|
2019-05-17 08:51:19 -06:00
|
|
|
// Shutdown closes this view, and detaches it from it's session.
|
2019-05-02 08:55:04 -06:00
|
|
|
Shutdown(ctx context.Context)
|
2019-05-17 08:51:19 -06:00
|
|
|
|
|
|
|
// Ignore returns true if this file should be ignored by this view.
|
2019-05-15 15:58:16 -06:00
|
|
|
Ignore(span.URI) bool
|
2019-06-28 14:21:07 -06:00
|
|
|
|
2019-07-14 11:59:24 -06:00
|
|
|
Config(ctx context.Context) *packages.Config
|
2019-07-03 13:23:05 -06:00
|
|
|
|
2019-07-12 16:54:06 -06:00
|
|
|
// 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
|
2018-12-18 13:46:14 -07:00
|
|
|
}
|
|
|
|
|
2019-05-03 22:04:18 -06:00
|
|
|
// File represents a source file of any type.
|
2018-12-05 15:00:36 -07:00
|
|
|
type File interface {
|
2019-02-19 19:11:15 -07:00
|
|
|
URI() span.URI
|
2019-04-17 16:21:47 -06:00
|
|
|
View() View
|
2019-06-03 23:04:18 -06:00
|
|
|
Handle(ctx context.Context) FileHandle
|
2019-05-17 08:51:19 -06:00
|
|
|
FileSet() *token.FileSet
|
2019-07-11 19:05:55 -06:00
|
|
|
GetToken(ctx context.Context) (*token.File, error)
|
2019-05-03 22:04:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// GoFile represents a Go source file that has been type-checked.
|
|
|
|
type GoFile interface {
|
|
|
|
File
|
2019-05-23 11:51:56 -06:00
|
|
|
|
2019-08-06 16:51:17 -06:00
|
|
|
// GetAST returns the AST for the file, at or above the given mode.
|
2019-07-11 19:05:55 -06:00
|
|
|
GetAST(ctx context.Context, mode ParseMode) (*ast.File, error)
|
2019-05-23 11:51:56 -06:00
|
|
|
|
2019-08-06 16:51:17 -06:00
|
|
|
// GetCachedPackage returns the cached package for the file, if any.
|
|
|
|
GetCachedPackage(ctx context.Context) (Package, error)
|
|
|
|
|
2019-05-23 11:51:56 -06:00
|
|
|
// GetPackage returns the package that this file belongs to.
|
2019-05-03 22:04:18 -06:00
|
|
|
GetPackage(ctx context.Context) Package
|
2019-05-01 20:46:07 -06:00
|
|
|
|
2019-06-24 14:34:21 -06:00
|
|
|
// GetPackages returns all of the packages that this file belongs to.
|
|
|
|
GetPackages(ctx context.Context) []Package
|
|
|
|
|
2019-05-01 20:46:07 -06:00
|
|
|
// GetActiveReverseDeps returns the active files belonging to the reverse
|
|
|
|
// dependencies of this file's package.
|
2019-05-03 22:04:18 -06:00
|
|
|
GetActiveReverseDeps(ctx context.Context) []GoFile
|
2018-11-05 15:54:12 -07:00
|
|
|
}
|
|
|
|
|
2019-05-23 13:03:11 -06:00
|
|
|
type ModFile interface {
|
|
|
|
File
|
|
|
|
}
|
|
|
|
|
|
|
|
type SumFile interface {
|
|
|
|
File
|
|
|
|
}
|
|
|
|
|
2019-03-06 14:33:47 -07:00
|
|
|
// Package represents a Go package that has been type-checked. It maintains
|
|
|
|
// only the relevant fields of a *go/packages.Package.
|
|
|
|
type Package interface {
|
2019-06-11 16:06:27 -06:00
|
|
|
ID() string
|
2019-05-01 20:46:07 -06:00
|
|
|
PkgPath() string
|
2019-08-06 16:51:17 -06:00
|
|
|
GetHandles() []ParseGoHandle
|
2019-07-11 19:05:55 -06:00
|
|
|
GetSyntax(context.Context) []*ast.File
|
2019-03-06 14:33:47 -07:00
|
|
|
GetErrors() []packages.Error
|
|
|
|
GetTypes() *types.Package
|
|
|
|
GetTypesInfo() *types.Info
|
2019-03-29 14:39:22 -06:00
|
|
|
GetTypesSizes() types.Sizes
|
2019-03-11 15:14:55 -06:00
|
|
|
IsIllTyped() bool
|
2019-03-06 14:33:47 -07:00
|
|
|
GetActionGraph(ctx context.Context, a *analysis.Analyzer) (*Action, error)
|
2019-05-10 08:32:25 -06:00
|
|
|
GetImport(pkgPath string) Package
|
2019-06-20 14:57:45 -06:00
|
|
|
GetDiagnostics() []Diagnostic
|
|
|
|
SetDiagnostics(diags []Diagnostic)
|
2019-03-06 14:33:47 -07:00
|
|
|
}
|
|
|
|
|
2018-11-07 10:58:55 -07:00
|
|
|
// TextEdit represents a change to a section of a document.
|
2019-02-19 19:11:15 -07:00
|
|
|
// The text within the specified span should be replaced by the supplied new text.
|
2018-11-07 10:58:55 -07:00
|
|
|
type TextEdit struct {
|
2019-02-19 19:11:15 -07:00
|
|
|
Span span.Span
|
2018-11-07 10:58:55 -07:00
|
|
|
NewText string
|
|
|
|
}
|
2019-04-08 07:22:58 -06:00
|
|
|
|
|
|
|
// DiffToEdits converts from a sequence of diff operations to a sequence of
|
|
|
|
// source.TextEdit
|
|
|
|
func DiffToEdits(uri span.URI, ops []*diff.Op) []TextEdit {
|
|
|
|
edits := make([]TextEdit, 0, len(ops))
|
|
|
|
for _, op := range ops {
|
|
|
|
s := span.New(uri, span.NewPoint(op.I1+1, 1, 0), span.NewPoint(op.I2+1, 1, 0))
|
|
|
|
switch op.Kind {
|
|
|
|
case diff.Delete:
|
|
|
|
// Delete: unformatted[i1:i2] is deleted.
|
|
|
|
edits = append(edits, TextEdit{Span: s})
|
|
|
|
case diff.Insert:
|
|
|
|
// Insert: formatted[j1:j2] is inserted at unformatted[i1:i1].
|
|
|
|
if content := strings.Join(op.Content, ""); content != "" {
|
|
|
|
edits = append(edits, TextEdit{Span: s, NewText: content})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return edits
|
|
|
|
}
|
|
|
|
|
|
|
|
func EditsToDiff(edits []TextEdit) []*diff.Op {
|
|
|
|
iToJ := 0
|
|
|
|
ops := make([]*diff.Op, len(edits))
|
|
|
|
for i, edit := range edits {
|
|
|
|
i1 := edit.Span.Start().Line() - 1
|
|
|
|
i2 := edit.Span.End().Line() - 1
|
|
|
|
kind := diff.Insert
|
|
|
|
if edit.NewText == "" {
|
|
|
|
kind = diff.Delete
|
|
|
|
}
|
|
|
|
ops[i] = &diff.Op{
|
|
|
|
Kind: kind,
|
|
|
|
Content: diff.SplitLines(edit.NewText),
|
|
|
|
I1: i1,
|
|
|
|
I2: i2,
|
|
|
|
J1: i1 + iToJ,
|
|
|
|
}
|
|
|
|
if kind == diff.Insert {
|
|
|
|
iToJ += len(ops[i].Content)
|
|
|
|
} else {
|
|
|
|
iToJ -= i2 - i1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ops
|
|
|
|
}
|
2019-07-15 15:02:40 -06:00
|
|
|
|
|
|
|
func sortTextEdits(d []TextEdit) {
|
|
|
|
// Use a stable sort to maintain the order of edits inserted at the same position.
|
|
|
|
sort.SliceStable(d, func(i int, j int) bool {
|
|
|
|
return span.Compare(d[i].Span, d[j].Span) < 0
|
|
|
|
})
|
|
|
|
}
|