github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/source/view.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package source
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  	"go/types"
    14  	"io"
    15  
    16  	"golang.org/x/mod/modfile"
    17  	"github.com/april1989/origin-go-tools/go/analysis"
    18  	"github.com/april1989/origin-go-tools/go/packages"
    19  	"github.com/april1989/origin-go-tools/internal/imports"
    20  	"github.com/april1989/origin-go-tools/internal/lsp/protocol"
    21  	"github.com/april1989/origin-go-tools/internal/span"
    22  	errors "golang.org/x/xerrors"
    23  )
    24  
    25  // Snapshot represents the current state for the given view.
    26  type Snapshot interface {
    27  	ID() uint64
    28  
    29  	// View returns the View associated with this snapshot.
    30  	View() View
    31  
    32  	// Fileset returns the Fileset used to parse all the Go files in this snapshot.
    33  	FileSet() *token.FileSet
    34  
    35  	// FindFile returns the FileHandle for the given URI, if it is already
    36  	// in the given snapshot.
    37  	FindFile(uri span.URI) VersionedFileHandle
    38  
    39  	// GetFile returns the FileHandle for a given URI, initializing it
    40  	// if it is not already part of the snapshot.
    41  	GetFile(ctx context.Context, uri span.URI) (VersionedFileHandle, error)
    42  
    43  	// IsOpen returns whether the editor currently has a file open.
    44  	IsOpen(uri span.URI) bool
    45  
    46  	// IsSaved returns whether the contents are saved on disk or not.
    47  	IsSaved(uri span.URI) bool
    48  
    49  	// ParseGo returns the parsed AST for the file.
    50  	// If the file is not available, returns nil and an error.
    51  	ParseGo(ctx context.Context, fh FileHandle, mode ParseMode) (*ParsedGoFile, error)
    52  
    53  	// PosToField is a cache of *ast.Fields by token.Pos. This allows us
    54  	// to quickly find corresponding *ast.Field node given a *types.Var.
    55  	// We must refer to the AST to render type aliases properly when
    56  	// formatting signatures and other types.
    57  	PosToField(ctx context.Context, pgf *ParsedGoFile) (map[token.Pos]*ast.Field, error)
    58  
    59  	// PosToDecl maps certain objects' positions to their surrounding
    60  	// ast.Decl. This mapping is used when building the documentation
    61  	// string for the objects.
    62  	PosToDecl(ctx context.Context, pgf *ParsedGoFile) (map[token.Pos]ast.Decl, error)
    63  
    64  	// Analyze runs the analyses for the given package at this snapshot.
    65  	Analyze(ctx context.Context, pkgID string, analyzers ...*analysis.Analyzer) ([]*Error, error)
    66  
    67  	// RunGoCommandPiped runs the given `go` command in the view, using the
    68  	// provided stdout and stderr. It will use the -modfile flag, if possible.
    69  	RunGoCommandPiped(ctx context.Context, verb string, args []string, stdout, stderr io.Writer) error
    70  
    71  	// RunGoCommand runs the given `go` command in the view. It will use the
    72  	// -modfile flag, if possible.
    73  	RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error)
    74  
    75  	// RunGoCommandDirect runs the given `go` command, never using the
    76  	// -modfile flag.
    77  	RunGoCommandDirect(ctx context.Context, verb string, args []string) error
    78  
    79  	// ParseMod is used to parse go.mod files.
    80  	ParseMod(ctx context.Context, fh FileHandle) (*ParsedModule, error)
    81  
    82  	// ModWhy returns the results of `go mod why` for the module specified by
    83  	// the given go.mod file.
    84  	ModWhy(ctx context.Context, fh FileHandle) (map[string]string, error)
    85  
    86  	// ModUpgrade returns the possible updates for the module specified by the
    87  	// given go.mod file.
    88  	ModUpgrade(ctx context.Context, fh FileHandle) (map[string]string, error)
    89  
    90  	// ModTidy returns the results of `go mod tidy` for the module specified by
    91  	// the given go.mod file.
    92  	ModTidy(ctx context.Context, fh FileHandle) (*TidiedModule, error)
    93  
    94  	// BuiltinPackage returns information about the special builtin package.
    95  	BuiltinPackage(ctx context.Context) (*BuiltinPackage, error)
    96  
    97  	// PackagesForFile returns the packages that this file belongs to, checked
    98  	// in mode.
    99  	PackagesForFile(ctx context.Context, uri span.URI, mode TypecheckMode) ([]Package, error)
   100  
   101  	// GetActiveReverseDeps returns the active files belonging to the reverse
   102  	// dependencies of this file's package, checked in TypecheckWorkspace mode.
   103  	GetReverseDependencies(ctx context.Context, id string) ([]Package, error)
   104  
   105  	// CachedImportPaths returns all the imported packages loaded in this
   106  	// snapshot, indexed by their import path and checked in TypecheckWorkspace
   107  	// mode.
   108  	CachedImportPaths(ctx context.Context) (map[string]Package, error)
   109  
   110  	// KnownPackages returns all the packages loaded in this snapshot, checked
   111  	// in TypecheckWorkspace mode.
   112  	KnownPackages(ctx context.Context) ([]Package, error)
   113  
   114  	// WorkspacePackages returns the snapshot's top-level packages.
   115  	WorkspacePackages(ctx context.Context) ([]Package, error)
   116  
   117  	// WorkspaceDirectories returns any directory known by the view. For views
   118  	// within a module, this is the module root and any replace targets.
   119  	WorkspaceDirectories(ctx context.Context) []span.URI
   120  }
   121  
   122  // View represents a single workspace.
   123  // This is the level at which we maintain configuration like working directory
   124  // and build tags.
   125  type View interface {
   126  	// Session returns the session that created this view.
   127  	Session() Session
   128  
   129  	// Name returns the name this view was constructed with.
   130  	Name() string
   131  
   132  	// Folder returns the root folder for this view.
   133  	Folder() span.URI
   134  
   135  	// ModFile is the go.mod file at the root of this view. It may not exist.
   136  	ModFile() span.URI
   137  
   138  	// BackgroundContext returns a context used for all background processing
   139  	// on behalf of this view.
   140  	BackgroundContext() context.Context
   141  
   142  	// Shutdown closes this view, and detaches it from its session.
   143  	Shutdown(ctx context.Context)
   144  
   145  	// AwaitInitialized waits until a view is initialized
   146  	AwaitInitialized(ctx context.Context)
   147  
   148  	// WriteEnv writes the view-specific environment to the io.Writer.
   149  	WriteEnv(ctx context.Context, w io.Writer) error
   150  
   151  	// RunProcessEnvFunc runs fn with the process env for this snapshot's view.
   152  	// Note: the process env contains cached module and filesystem state.
   153  	RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error
   154  
   155  	// Options returns a copy of the Options for this view.
   156  	Options() Options
   157  
   158  	// SetOptions sets the options of this view to new values.
   159  	// Calling this may cause the view to be invalidated and a replacement view
   160  	// added to the session. If so the new view will be returned, otherwise the
   161  	// original one will be.
   162  	SetOptions(context.Context, Options) (View, error)
   163  
   164  	// Snapshot returns the current snapshot for the view.
   165  	Snapshot(ctx context.Context) (Snapshot, func())
   166  
   167  	// Rebuild rebuilds the current view, replacing the original view in its session.
   168  	Rebuild(ctx context.Context) (Snapshot, func(), error)
   169  
   170  	// InvalidBuildConfiguration returns true if there is some error in the
   171  	// user's workspace. In particular, if they are both outside of a module
   172  	// and their GOPATH.
   173  	ValidBuildConfiguration() bool
   174  
   175  	// IsGoPrivatePath reports whether target is a private import path, as identified
   176  	// by the GOPRIVATE environment variable.
   177  	IsGoPrivatePath(path string) bool
   178  
   179  	// IgnoredFile reports if a file would be ignored by a `go list` of the whole
   180  	// workspace.
   181  	IgnoredFile(uri span.URI) bool
   182  }
   183  
   184  type BuiltinPackage struct {
   185  	Package    *ast.Package
   186  	ParsedFile *ParsedGoFile
   187  }
   188  
   189  // A ParsedGoFile contains the results of parsing a Go file.
   190  type ParsedGoFile struct {
   191  	URI  span.URI
   192  	Mode ParseMode
   193  	File *ast.File
   194  	Tok  *token.File
   195  	// Source code used to build the AST. It may be different from the
   196  	// actual content of the file if we have fixed the AST.
   197  	Src      []byte
   198  	Mapper   *protocol.ColumnMapper
   199  	ParseErr error
   200  }
   201  
   202  // A ParsedModule contains the results of parsing a go.mod file.
   203  type ParsedModule struct {
   204  	File        *modfile.File
   205  	Mapper      *protocol.ColumnMapper
   206  	ParseErrors []Error
   207  }
   208  
   209  // A TidiedModule contains the results of running `go mod tidy` on a module.
   210  type TidiedModule struct {
   211  	// The parsed module, which is guaranteed to have parsed successfully.
   212  	Parsed *ParsedModule
   213  	// Diagnostics representing changes made by `go mod tidy`.
   214  	Errors []Error
   215  	// The bytes of the go.mod file after it was tidied.
   216  	TidiedContent []byte
   217  }
   218  
   219  // Session represents a single connection from a client.
   220  // This is the level at which things like open files are maintained on behalf
   221  // of the client.
   222  // A session may have many active views at any given time.
   223  type Session interface {
   224  	// NewView creates a new View, returning it and its first snapshot.
   225  	NewView(ctx context.Context, name string, folder span.URI, options Options) (View, Snapshot, func(), error)
   226  
   227  	// Cache returns the cache that created this session, for debugging only.
   228  	Cache() interface{}
   229  
   230  	// View returns a view with a matching name, if the session has one.
   231  	View(name string) View
   232  
   233  	// ViewOf returns a view corresponding to the given URI.
   234  	ViewOf(uri span.URI) (View, error)
   235  
   236  	// Views returns the set of active views built by this session.
   237  	Views() []View
   238  
   239  	// Shutdown the session and all views it has created.
   240  	Shutdown(ctx context.Context)
   241  
   242  	// GetFile returns a handle for the specified file.
   243  	GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
   244  
   245  	// DidModifyFile reports a file modification to the session. It returns the
   246  	// resulting snapshots, a guaranteed one per view.
   247  	DidModifyFiles(ctx context.Context, changes []FileModification) ([]Snapshot, []func(), []span.URI, error)
   248  
   249  	// Overlays returns a slice of file overlays for the session.
   250  	Overlays() []Overlay
   251  
   252  	// Options returns a copy of the SessionOptions for this session.
   253  	Options() Options
   254  
   255  	// SetOptions sets the options of this session to new values.
   256  	SetOptions(Options)
   257  }
   258  
   259  // Overlay is the type for a file held in memory on a session.
   260  type Overlay interface {
   261  	VersionedFileHandle
   262  
   263  	// Saved returns whether this overlay has been saved to disk.
   264  	Saved() bool
   265  }
   266  
   267  // FileModification represents a modification to a file.
   268  type FileModification struct {
   269  	URI    span.URI
   270  	Action FileAction
   271  
   272  	// OnDisk is true if a watched file is changed on disk.
   273  	// If true, Version will be -1 and Text will be nil.
   274  	OnDisk bool
   275  
   276  	// Version will be -1 and Text will be nil when they are not supplied,
   277  	// specifically on textDocument/didClose and for on-disk changes.
   278  	Version float64
   279  	Text    []byte
   280  
   281  	// LanguageID is only sent from the language client on textDocument/didOpen.
   282  	LanguageID string
   283  }
   284  
   285  type FileAction int
   286  
   287  const (
   288  	UnknownFileAction = FileAction(iota)
   289  	Open
   290  	Change
   291  	Close
   292  	Save
   293  	Create
   294  	Delete
   295  	InvalidateMetadata
   296  )
   297  
   298  func (a FileAction) String() string {
   299  	switch a {
   300  	case Open:
   301  		return "Open"
   302  	case Change:
   303  		return "Change"
   304  	case Close:
   305  		return "Close"
   306  	case Save:
   307  		return "Save"
   308  	case Create:
   309  		return "Create"
   310  	case Delete:
   311  		return "Delete"
   312  	case InvalidateMetadata:
   313  		return "InvalidateMetadata"
   314  	default:
   315  		return "Unknown"
   316  	}
   317  }
   318  
   319  var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version")
   320  
   321  // ParseMode controls the content of the AST produced when parsing a source file.
   322  type ParseMode int
   323  
   324  const (
   325  	// ParseHeader specifies that the main package declaration and imports are needed.
   326  	// This is the mode used when attempting to examine the package graph structure.
   327  	ParseHeader ParseMode = iota
   328  
   329  	// ParseExported specifies that the public symbols are needed, but things like
   330  	// private symbols and function bodies are not.
   331  	// This mode is used for things where a package is being consumed only as a
   332  	// dependency.
   333  	ParseExported
   334  
   335  	// ParseFull specifies the full AST is needed.
   336  	// This is used for files of direct interest where the entire contents must
   337  	// be considered.
   338  	ParseFull
   339  )
   340  
   341  // TypecheckMode controls what kind of parsing should be done (see ParseMode)
   342  // while type checking a package.
   343  type TypecheckMode int
   344  
   345  const (
   346  	// Invalid default value.
   347  	TypecheckUnknown TypecheckMode = iota
   348  	// TypecheckFull means to use ParseFull.
   349  	TypecheckFull
   350  	// TypecheckWorkspace means to use ParseFull for workspace packages, and
   351  	// ParseExported for others.
   352  	TypecheckWorkspace
   353  	// TypecheckAll means ParseFull for workspace packages, and both Full and
   354  	// Exported for others. Only valid for some functions.
   355  	TypecheckAll
   356  )
   357  
   358  type VersionedFileHandle interface {
   359  	FileHandle
   360  	Version() float64
   361  	Session() string
   362  
   363  	// LSPIdentity returns the version identity of a file.
   364  	VersionedFileIdentity() VersionedFileIdentity
   365  }
   366  
   367  type VersionedFileIdentity struct {
   368  	URI span.URI
   369  
   370  	// SessionID is the ID of the LSP session.
   371  	SessionID string
   372  
   373  	// Version is the version of the file, as specified by the client. It should
   374  	// only be set in combination with SessionID.
   375  	Version float64
   376  }
   377  
   378  // FileHandle represents a handle to a specific version of a single file.
   379  type FileHandle interface {
   380  	URI() span.URI
   381  	Kind() FileKind
   382  
   383  	// Identity returns a FileIdentity for the file, even if there was an error
   384  	// reading it.
   385  	// It is a fatal error to call Identity on a file that has not yet been read.
   386  	FileIdentity() FileIdentity
   387  	// Read reads the contents of a file.
   388  	// If the file is not available, returns a nil slice and an error.
   389  	Read() ([]byte, error)
   390  }
   391  
   392  // FileIdentity uniquely identifies a file at a version from a FileSystem.
   393  type FileIdentity struct {
   394  	URI span.URI
   395  
   396  	// Identifier represents a unique identifier for the file's content.
   397  	Hash string
   398  
   399  	// Kind is the file's kind.
   400  	Kind FileKind
   401  }
   402  
   403  // FileKind describes the kind of the file in question.
   404  // It can be one of Go, mod, or sum.
   405  type FileKind int
   406  
   407  const (
   408  	// UnknownKind is a file type we don't know about.
   409  	UnknownKind = FileKind(iota)
   410  
   411  	// Go is a normal go source file.
   412  	Go
   413  	// Mod is a go.mod file.
   414  	Mod
   415  	// Sum is a go.sum file.
   416  	Sum
   417  )
   418  
   419  // Analyzer represents a go/analysis analyzer with some boolean properties
   420  // that let the user know how to use the analyzer.
   421  type Analyzer struct {
   422  	Analyzer *analysis.Analyzer
   423  	enabled  bool
   424  
   425  	// Command is the name of the command used to invoke the suggested fixes
   426  	// for the analyzer. It is non-nil if we expect this analyzer to provide
   427  	// its fix separately from its diagnostics. That is, we should apply the
   428  	// analyzer's suggested fixes through a Command, not a TextEdit.
   429  	Command *Command
   430  
   431  	// If this is true, then we can apply the suggested fixes
   432  	// as part of a source.FixAll codeaction.
   433  	HighConfidence bool
   434  
   435  	// FixesError is only set for type-error analyzers.
   436  	// It reports true if the message provided indicates an error that could be
   437  	// fixed by the analyzer.
   438  	FixesError func(msg string) bool
   439  }
   440  
   441  func (a Analyzer) Enabled(view View) bool {
   442  	if enabled, ok := view.Options().UserEnabledAnalyses[a.Analyzer.Name]; ok {
   443  		return enabled
   444  	}
   445  	return a.enabled
   446  }
   447  
   448  // Package represents a Go package that has been type-checked. It maintains
   449  // only the relevant fields of a *go/packages.Package.
   450  type Package interface {
   451  	ID() string
   452  	Name() string
   453  	PkgPath() string
   454  	CompiledGoFiles() []*ParsedGoFile
   455  	File(uri span.URI) (*ParsedGoFile, error)
   456  	GetSyntax() []*ast.File
   457  	GetErrors() []*Error
   458  	GetTypes() *types.Package
   459  	GetTypesInfo() *types.Info
   460  	GetTypesSizes() types.Sizes
   461  	IsIllTyped() bool
   462  	ForTest() string
   463  	GetImport(pkgPath string) (Package, error)
   464  	MissingDependencies() []string
   465  	Imports() []Package
   466  	Module() *packages.Module
   467  }
   468  
   469  type Error struct {
   470  	URI            span.URI
   471  	Range          protocol.Range
   472  	Kind           ErrorKind
   473  	Message        string
   474  	Category       string // only used by analysis errors so far
   475  	SuggestedFixes []SuggestedFix
   476  	Related        []RelatedInformation
   477  }
   478  
   479  // GoModTidy is the source for a diagnostic computed by running `go mod tidy`.
   480  const GoModTidy = "go mod tidy"
   481  
   482  type ErrorKind int
   483  
   484  const (
   485  	UnknownError = ErrorKind(iota)
   486  	ListError
   487  	ParseError
   488  	TypeError
   489  	ModTidyError
   490  	Analysis
   491  )
   492  
   493  func (e *Error) Error() string {
   494  	return fmt.Sprintf("%s:%s: %s", e.URI, e.Range, e.Message)
   495  }
   496  
   497  var (
   498  	InconsistentVendoring = errors.New("inconsistent vendoring")
   499  	PackagesLoadError     = errors.New("packages.Load error")
   500  )