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 )