whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# Advanced topics
|
||||
|
||||
This documentation is for advanced `gopls` users, who may want to test
|
||||
unreleased versions or try out special features.
|
||||
|
||||
## Installing unreleased versions
|
||||
|
||||
To get a specific version of `gopls` (for example, to test a prerelease
|
||||
version), run:
|
||||
|
||||
```sh
|
||||
GO111MODULE=on go install golang.org/x/tools/gopls@vX.Y.Z
|
||||
```
|
||||
|
||||
Where `vX.Y.Z` is the desired version.
|
||||
|
||||
### Unstable versions
|
||||
|
||||
To update `gopls` to the latest **unstable** version, use the following
|
||||
commands.
|
||||
|
||||
```sh
|
||||
# Create an empty go.mod file, only for tracking requirements.
|
||||
cd $(mktemp -d)
|
||||
go mod init gopls-unstable
|
||||
|
||||
# Use 'go get' to add requirements and to ensure they work together.
|
||||
go get -d golang.org/x/tools/gopls@master golang.org/x/tools@master
|
||||
|
||||
go install golang.org/x/tools/gopls
|
||||
```
|
||||
|
||||
## Working on the Go source distribution
|
||||
|
||||
If you are working on the [Go project] itself, the `go` command that `gopls`
|
||||
invokes will have to correspond to the version of the source you are working
|
||||
on. That is, if you have checked out the Go project to `$HOME/go`, your `go`
|
||||
command should be the `$HOME/go/bin/go` executable that you built with
|
||||
`make.bash` or equivalent.
|
||||
|
||||
You can achieve this by adding the right version of `go` to your `PATH`
|
||||
(`export PATH=$HOME/go/bin:$PATH` on Unix systems) or by configuring your
|
||||
editor.
|
||||
|
||||
To work on both `std` and `cmd` simultaneously, add a `go.work` file to
|
||||
`GOROOT/src`:
|
||||
|
||||
```
|
||||
cd $(go env GOROOT)/src
|
||||
go work init . cmd
|
||||
```
|
||||
|
||||
Note that you must work inside the `GOROOT/src` subdirectory, as the `go`
|
||||
command does not recognize `go.work` files in a parent of `GOROOT/src`
|
||||
(https://go.dev/issue/59429).
|
||||
|
||||
## Working with generic code
|
||||
|
||||
Gopls has support for editing generic Go code. To enable this support, you need
|
||||
to **install gopls using Go 1.18 or later**. The easiest way to do this is by
|
||||
[installing Go 1.18+](https://go.dev/dl) and then using this Go version to
|
||||
install gopls:
|
||||
|
||||
```
|
||||
$ go install golang.org/x/tools/gopls@latest
|
||||
```
|
||||
|
||||
It is strongly recommended that you install the latest version of `gopls`, or
|
||||
the latest **unstable** version as [described above](#installing-unreleased-versions).
|
||||
|
||||
The `gopls` built with these instructions understands generic code. See the
|
||||
[generics tutorial](https://go.dev/doc/tutorial/generics) for more information
|
||||
on how to use generics in Go!
|
||||
|
||||
### Known issues
|
||||
|
||||
* [`staticcheck`](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#staticcheck)
|
||||
on generic code is not supported yet.
|
||||
|
||||
[Go project]: https://go.googlesource.com/go
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
# Code Lenses
|
||||
|
||||
A "code lens" is a command associated with a range of a source file.
|
||||
The VS Code manual describes code lenses as
|
||||
"[actionable, contextual information, interspersed in your source
|
||||
code](https://code.visualstudio.com/blogs/2017/02/12/code-lens-roundup)".
|
||||
The LSP [`textDocument/codeLens`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeLens) operation requests the
|
||||
current set of code lenses for a file.
|
||||
|
||||
Gopls generates code lenses from a number of sources.
|
||||
This document describes them.
|
||||
|
||||
They can be enabled and disabled using the
|
||||
[`codelenses`](settings.md#codelenses) setting.
|
||||
Their features are subject to change.
|
||||
|
||||
<!-- This portion is generated by doc/generate from the ../internal/settings package. -->
|
||||
<!-- BEGIN Lenses: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
## `gc_details`: Toggle display of Go compiler optimization decisions
|
||||
|
||||
|
||||
This codelens source causes the `package` declaration of
|
||||
each file to be annotated with a command to toggle the
|
||||
state of the per-session variable that controls whether
|
||||
optimization decisions from the Go compiler (formerly known
|
||||
as "gc") should be displayed as diagnostics.
|
||||
|
||||
Optimization decisions include:
|
||||
- whether a variable escapes, and how escape is inferred;
|
||||
- whether a nil-pointer check is implied or eliminated;
|
||||
- whether a function can be inlined.
|
||||
|
||||
TODO(adonovan): this source is off by default because the
|
||||
annotation is annoying and because VS Code has a separate
|
||||
"Toggle gc details" command. Replace it with a Code Action
|
||||
("Source action...").
|
||||
|
||||
|
||||
Default: off
|
||||
|
||||
File type: Go
|
||||
|
||||
## `generate`: Run `go generate`
|
||||
|
||||
|
||||
This codelens source annotates any `//go:generate` comments
|
||||
with commands to run `go generate` in this directory, on
|
||||
all directories recursively beneath this one.
|
||||
|
||||
See [Generating code](https://go.dev/blog/generate) for
|
||||
more details.
|
||||
|
||||
|
||||
Default: on
|
||||
|
||||
File type: Go
|
||||
|
||||
## `regenerate_cgo`: Re-generate cgo declarations
|
||||
|
||||
|
||||
This codelens source annotates an `import "C"` declaration
|
||||
with a command to re-run the [cgo
|
||||
command](https://pkg.go.dev/cmd/cgo) to regenerate the
|
||||
corresponding Go declarations.
|
||||
|
||||
Use this after editing the C code in comments attached to
|
||||
the import, or in C header files included by it.
|
||||
|
||||
|
||||
Default: on
|
||||
|
||||
File type: Go
|
||||
|
||||
## `test`: Run tests and benchmarks
|
||||
|
||||
|
||||
This codelens source annotates each `Test` and `Benchmark`
|
||||
function in a `*_test.go` file with a command to run it.
|
||||
|
||||
This source is off by default because VS Code has
|
||||
a client-side custom UI for testing, and because progress
|
||||
notifications are not a great UX for streamed test output.
|
||||
See:
|
||||
- golang/go#67400 for a discussion of this feature.
|
||||
- https://github.com/joaotavora/eglot/discussions/1402
|
||||
for an alternative approach.
|
||||
|
||||
|
||||
Default: off
|
||||
|
||||
File type: Go
|
||||
|
||||
## `run_govulncheck`: Run govulncheck
|
||||
|
||||
|
||||
This codelens source annotates the `module` directive in a
|
||||
go.mod file with a command to run Govulncheck.
|
||||
|
||||
[Govulncheck](https://go.dev/blog/vuln) is a static
|
||||
analysis tool that computes the set of functions reachable
|
||||
within your application, including dependencies;
|
||||
queries a database of known security vulnerabilities; and
|
||||
reports any potential problems it finds.
|
||||
|
||||
|
||||
Default: off
|
||||
|
||||
File type: go.mod
|
||||
|
||||
## `tidy`: Tidy go.mod file
|
||||
|
||||
|
||||
This codelens source annotates the `module` directive in a
|
||||
go.mod file with a command to run [`go mod
|
||||
tidy`](https://go.dev/ref/mod#go-mod-tidy), which ensures
|
||||
that the go.mod file matches the source code in the module.
|
||||
|
||||
|
||||
Default: on
|
||||
|
||||
File type: go.mod
|
||||
|
||||
## `upgrade_dependency`: Update dependencies
|
||||
|
||||
|
||||
This codelens source annotates the `module` directive in a
|
||||
go.mod file with commands to:
|
||||
|
||||
- check for available upgrades,
|
||||
- upgrade direct dependencies, and
|
||||
- upgrade all dependencies transitively.
|
||||
|
||||
|
||||
Default: on
|
||||
|
||||
File type: go.mod
|
||||
|
||||
## `vendor`: Update vendor directory
|
||||
|
||||
|
||||
This codelens source annotates the `module` directive in a
|
||||
go.mod file with a command to run [`go mod
|
||||
vendor`](https://go.dev/ref/mod#go-mod-vendor), which
|
||||
creates or updates the directory named `vendor` in the
|
||||
module root so that it contains an up-to-date copy of all
|
||||
necessary package dependencies.
|
||||
|
||||
|
||||
Default: on
|
||||
|
||||
File type: go.mod
|
||||
|
||||
<!-- END Lenses: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
@@ -0,0 +1,15 @@
|
||||
# Command line
|
||||
|
||||
**Note: The `gopls` command-line is still experimental and subject to change at any point.**
|
||||
|
||||
`gopls` exposes some (but not all) features on the command-line. This can be useful for debugging `gopls` itself.
|
||||
|
||||
<!--TODO(rstambler): Generate this file.-->
|
||||
|
||||
Learn about available commands and flags by running `gopls help`.
|
||||
|
||||
Much of the functionality of `gopls` is available through a command line interface.
|
||||
|
||||
There are two main reasons for this. The first is that we do not want users to rely on separate command line tools when they wish to do some task outside of an editor. The second is that the CLI assists in debugging. It is easier to reproduce behavior via single command.
|
||||
|
||||
It is not a goal of `gopls` to be a high performance command line tool. Its command line is intended for single file/package user interaction speeds, not bulk processing.
|
||||
@@ -0,0 +1,798 @@
|
||||
# Commands
|
||||
|
||||
This document describes the LSP-level commands supported by `gopls`. They cannot be invoked directly by users, and all the details are subject to change, so nobody should rely on this information.
|
||||
|
||||
<!-- BEGIN Commands: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
## `gopls.add_dependency`: **Add a dependency**
|
||||
|
||||
Adds a dependency to the go.mod file for a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The go.mod file URI.
|
||||
"URI": string,
|
||||
// Additional args to pass to the go command.
|
||||
"GoCmdArgs": []string,
|
||||
// Whether to add a require directive.
|
||||
"AddRequire": bool,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.add_import`: **Add an import**
|
||||
|
||||
Ask the server to add an import path to a given Go file. The method will
|
||||
call applyEdit on the client so that clients don't have to apply the edit
|
||||
themselves.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// ImportPath is the target import path that should
|
||||
// be added to the URI file
|
||||
"ImportPath": string,
|
||||
// URI is the file that the ImportPath should be
|
||||
// added to
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.add_telemetry_counters`: **Update the given telemetry counters**
|
||||
|
||||
Gopls will prepend "fwd/" to all the counters updated using this command
|
||||
to avoid conflicts with other counters gopls collects.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// Names and Values must have the same length.
|
||||
"Names": []string,
|
||||
"Values": []int64,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.apply_fix`: **Apply a fix**
|
||||
|
||||
Applies a fix to a region of source code.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The name of the fix to apply.
|
||||
//
|
||||
// For fixes suggested by analyzers, this is a string constant
|
||||
// advertised by the analyzer that matches the Category of
|
||||
// the analysis.Diagnostic with a SuggestedFix containing no edits.
|
||||
//
|
||||
// For fixes suggested by code actions, this is a string agreed
|
||||
// upon by the code action and golang.ApplyFix.
|
||||
"Fix": string,
|
||||
// The file URI for the document to fix.
|
||||
"URI": string,
|
||||
// The document range to scan for fixes.
|
||||
"Range": {
|
||||
"start": {
|
||||
"line": uint32,
|
||||
"character": uint32,
|
||||
},
|
||||
"end": {
|
||||
"line": uint32,
|
||||
"character": uint32,
|
||||
},
|
||||
},
|
||||
// Whether to resolve and return the edits.
|
||||
"ResolveEdits": bool,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Holds changes to existing resources.
|
||||
"changes": map[golang.org/x/tools/gopls/internal/protocol.DocumentURI][]golang.org/x/tools/gopls/internal/protocol.TextEdit,
|
||||
// Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes
|
||||
// are either an array of `TextDocumentEdit`s to express changes to n different text documents
|
||||
// where each text document edit addresses a specific version of a text document. Or it can contain
|
||||
// above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.
|
||||
//
|
||||
// Whether a client supports versioned document edits is expressed via
|
||||
// `workspace.workspaceEdit.documentChanges` client capability.
|
||||
//
|
||||
// If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then
|
||||
// only plain `TextEdit`s using the `changes` property are supported.
|
||||
"documentChanges": []{
|
||||
"TextDocumentEdit": {
|
||||
"textDocument": { ... },
|
||||
"edits": { ... },
|
||||
},
|
||||
"CreateFile": {
|
||||
"kind": string,
|
||||
"uri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
"RenameFile": {
|
||||
"kind": string,
|
||||
"oldUri": string,
|
||||
"newUri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
"DeleteFile": {
|
||||
"kind": string,
|
||||
"uri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
},
|
||||
// A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and
|
||||
// delete file / folder operations.
|
||||
//
|
||||
// Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`.
|
||||
//
|
||||
// @since 3.16.0
|
||||
"changeAnnotations": map[string]golang.org/x/tools/gopls/internal/protocol.ChangeAnnotation,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.assembly`: **Browse assembly listing of current function in a browser.**
|
||||
|
||||
This command opens a web-based disassembly listing of the
|
||||
specified function symbol (plus any nested lambdas and defers).
|
||||
The machine architecture is determined by the view.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
string,
|
||||
string,
|
||||
string
|
||||
```
|
||||
|
||||
## `gopls.change_signature`: **Perform a "change signature" refactoring**
|
||||
|
||||
This command is experimental, currently only supporting parameter removal.
|
||||
Its signature will certainly change in the future (pun intended).
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
"RemoveParameter": {
|
||||
"uri": string,
|
||||
"range": {
|
||||
"start": { ... },
|
||||
"end": { ... },
|
||||
},
|
||||
},
|
||||
// Whether to resolve and return the edits.
|
||||
"ResolveEdits": bool,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Holds changes to existing resources.
|
||||
"changes": map[golang.org/x/tools/gopls/internal/protocol.DocumentURI][]golang.org/x/tools/gopls/internal/protocol.TextEdit,
|
||||
// Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes
|
||||
// are either an array of `TextDocumentEdit`s to express changes to n different text documents
|
||||
// where each text document edit addresses a specific version of a text document. Or it can contain
|
||||
// above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations.
|
||||
//
|
||||
// Whether a client supports versioned document edits is expressed via
|
||||
// `workspace.workspaceEdit.documentChanges` client capability.
|
||||
//
|
||||
// If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then
|
||||
// only plain `TextEdit`s using the `changes` property are supported.
|
||||
"documentChanges": []{
|
||||
"TextDocumentEdit": {
|
||||
"textDocument": { ... },
|
||||
"edits": { ... },
|
||||
},
|
||||
"CreateFile": {
|
||||
"kind": string,
|
||||
"uri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
"RenameFile": {
|
||||
"kind": string,
|
||||
"oldUri": string,
|
||||
"newUri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
"DeleteFile": {
|
||||
"kind": string,
|
||||
"uri": string,
|
||||
"options": { ... },
|
||||
"ResourceOperation": { ... },
|
||||
},
|
||||
},
|
||||
// A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and
|
||||
// delete file / folder operations.
|
||||
//
|
||||
// Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`.
|
||||
//
|
||||
// @since 3.16.0
|
||||
"changeAnnotations": map[string]golang.org/x/tools/gopls/internal/protocol.ChangeAnnotation,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.check_upgrades`: **Check for upgrades**
|
||||
|
||||
Checks for module upgrades.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The go.mod file URI.
|
||||
"URI": string,
|
||||
// The modules to check.
|
||||
"Modules": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.diagnose_files`: **Cause server to publish diagnostics for the specified files.**
|
||||
|
||||
This command is needed by the 'gopls {check,fix}' CLI subcommands.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
"Files": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.doc`: **Browse package documentation.**
|
||||
|
||||
Opens the Go package documentation page for the current
|
||||
package in a browser.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
"uri": string,
|
||||
"range": {
|
||||
"start": {
|
||||
"line": uint32,
|
||||
"character": uint32,
|
||||
},
|
||||
"end": {
|
||||
"line": uint32,
|
||||
"character": uint32,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.edit_go_directive`: **Run go mod edit -go=version**
|
||||
|
||||
Runs `go mod edit -go=version` for a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// Any document URI within the relevant module.
|
||||
"URI": string,
|
||||
// The version to pass to `go mod edit -go`.
|
||||
"Version": string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.fetch_vulncheck_result`: **Get known vulncheck result**
|
||||
|
||||
Fetch the result of latest vulnerability check (`govulncheck`).
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URI.
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
map[golang.org/x/tools/gopls/internal/protocol.DocumentURI]*golang.org/x/tools/gopls/internal/vulncheck.Result
|
||||
```
|
||||
|
||||
## `gopls.free_symbols`: **Browse free symbols referenced by the selection in a browser.**
|
||||
|
||||
This command is a query over a selected range of Go source
|
||||
code. It reports the set of "free" symbols of the
|
||||
selection: the set of symbols that are referenced within
|
||||
the selection but are declared outside of it. This
|
||||
information is useful for understanding at a glance what a
|
||||
block of code depends on, perhaps as a precursor to
|
||||
extracting it into a separate function.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
string,
|
||||
{
|
||||
"uri": string,
|
||||
"range": {
|
||||
"start": {
|
||||
"line": uint32,
|
||||
"character": uint32,
|
||||
},
|
||||
"end": {
|
||||
"line": uint32,
|
||||
"character": uint32,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.gc_details`: **Toggle gc_details**
|
||||
|
||||
Toggle the calculation of gc annotations.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
string
|
||||
```
|
||||
|
||||
## `gopls.generate`: **Run go generate**
|
||||
|
||||
Runs `go generate` for a given directory.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// URI for the directory to generate.
|
||||
"Dir": string,
|
||||
// Whether to generate recursively (go generate ./...)
|
||||
"Recursive": bool,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.go_get_package`: **'go get' a package**
|
||||
|
||||
Runs `go get` to fetch a package.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// Any document URI within the relevant module.
|
||||
"URI": string,
|
||||
// The package to go get.
|
||||
"Pkg": string,
|
||||
"AddRequire": bool,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.list_imports`: **List imports of a file and its package**
|
||||
|
||||
Retrieve a list of imports in the given Go file, and the package it
|
||||
belongs to.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URI.
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Imports is a list of imports in the requested file.
|
||||
"Imports": []{
|
||||
"Path": string,
|
||||
"Name": string,
|
||||
},
|
||||
// PackageImports is a list of all imports in the requested file's package.
|
||||
"PackageImports": []{
|
||||
"Path": string,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.list_known_packages`: **List known packages**
|
||||
|
||||
Retrieve a list of packages that are importable from the given URI.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URI.
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Packages is a list of packages relative
|
||||
// to the URIArg passed by the command request.
|
||||
// In other words, it omits paths that are already
|
||||
// imported or cannot be imported due to compiler
|
||||
// restrictions.
|
||||
"Packages": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.maybe_prompt_for_telemetry`: **Prompt user to enable telemetry**
|
||||
|
||||
Checks for the right conditions, and then prompts the user
|
||||
to ask if they want to enable Go telemetry uploading. If
|
||||
the user responds 'Yes', the telemetry mode is set to "on".
|
||||
|
||||
## `gopls.mem_stats`: **Fetch memory statistics**
|
||||
|
||||
Call runtime.GC multiple times and return memory statistics as reported by
|
||||
runtime.MemStats.
|
||||
|
||||
This command is used for benchmarking, and may change in the future.
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
"HeapAlloc": uint64,
|
||||
"HeapInUse": uint64,
|
||||
"TotalAlloc": uint64,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.regenerate_cgo`: **Regenerate cgo**
|
||||
|
||||
Regenerates cgo definitions.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URI.
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.remove_dependency`: **Remove a dependency**
|
||||
|
||||
Removes a dependency from the go.mod file of a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The go.mod file URI.
|
||||
"URI": string,
|
||||
// The module path to remove.
|
||||
"ModulePath": string,
|
||||
// If the module is tidied apart from the one unused diagnostic, we can
|
||||
// run `go get module@none`, and then run `go mod tidy`. Otherwise, we
|
||||
// must make textual edits.
|
||||
"OnlyDiagnostic": bool,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.reset_go_mod_diagnostics`: **Reset go.mod diagnostics**
|
||||
|
||||
Reset diagnostics in the go.mod file of a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
"URIArg": {
|
||||
"URI": string,
|
||||
},
|
||||
// Optional: source of the diagnostics to reset.
|
||||
// If not set, all resettable go.mod diagnostics will be cleared.
|
||||
"DiagnosticSource": string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.run_go_work_command`: **Run `go work [args...]`, and apply the resulting go.work**
|
||||
|
||||
edits to the current go.work file
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
"ViewID": string,
|
||||
"InitFirst": bool,
|
||||
"Args": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.run_govulncheck`: **Run vulncheck**
|
||||
|
||||
Run vulnerability check (`govulncheck`).
|
||||
|
||||
This command is asynchronous; clients must wait for the 'end' progress notification.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// Any document in the directory from which govulncheck will run.
|
||||
"URI": string,
|
||||
// Package pattern. E.g. "", ".", "./...".
|
||||
"Pattern": string,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// Token holds the progress token for LSP workDone reporting of the vulncheck
|
||||
// invocation.
|
||||
"Token": interface{},
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.run_tests`: **Run test(s)**
|
||||
|
||||
Runs `go test` for a specific set of test or benchmark functions.
|
||||
|
||||
This command is asynchronous; clients must wait for the 'end' progress notification.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The test file containing the tests to run.
|
||||
"URI": string,
|
||||
// Specific test names to run, e.g. TestFoo.
|
||||
"Tests": []string,
|
||||
// Specific benchmarks to run, e.g. BenchmarkFoo.
|
||||
"Benchmarks": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.scan_imports`: **force a sychronous scan of the imports cache.**
|
||||
|
||||
This command is intended for use by gopls tests only.
|
||||
|
||||
## `gopls.start_debugging`: **Start the gopls debug server**
|
||||
|
||||
Start the gopls debug server if it isn't running, and return the debug
|
||||
address.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// Optional: the address (including port) for the debug server to listen on.
|
||||
// If not provided, the debug server will bind to "localhost:0", and the
|
||||
// full debug URL will be contained in the result.
|
||||
//
|
||||
// If there is more than one gopls instance along the serving path (i.e. you
|
||||
// are using a daemon), each gopls instance will attempt to start debugging.
|
||||
// If Addr specifies a port, only the daemon will be able to bind to that
|
||||
// port, and each intermediate gopls instance will fail to start debugging.
|
||||
// For this reason it is recommended not to specify a port (or equivalently,
|
||||
// to specify ":0").
|
||||
//
|
||||
// If the server was already debugging this field has no effect, and the
|
||||
// result will contain the previously configured debug URL(s).
|
||||
"Addr": string,
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// The URLs to use to access the debug servers, for all gopls instances in
|
||||
// the serving path. For the common case of a single gopls instance (i.e. no
|
||||
// daemon), this will be exactly one address.
|
||||
//
|
||||
// In the case of one or more gopls instances forwarding the LSP to a daemon,
|
||||
// URLs will contain debug addresses for each server in the serving path, in
|
||||
// serving order. The daemon debug address will be the last entry in the
|
||||
// slice. If any intermediate gopls instance fails to start debugging, no
|
||||
// error will be returned but the debug URL for that server in the URLs slice
|
||||
// will be empty.
|
||||
"URLs": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.start_profile`: **Start capturing a profile of gopls' execution**
|
||||
|
||||
Start a new pprof profile. Before using the resulting file, profiling must
|
||||
be stopped with a corresponding call to StopProfile.
|
||||
|
||||
This command is intended for internal use only, by the gopls benchmark
|
||||
runner.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
struct{}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
struct{}
|
||||
```
|
||||
|
||||
## `gopls.stop_profile`: **Stop an ongoing profile**
|
||||
|
||||
This command is intended for internal use only, by the gopls benchmark
|
||||
runner.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
struct{}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
// File is the profile file name.
|
||||
"File": string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.test`: **Run test(s) (legacy)**
|
||||
|
||||
Runs `go test` for a specific set of test or benchmark functions.
|
||||
|
||||
This command is asynchronous; wait for the 'end' progress notification.
|
||||
|
||||
This command is an alias for RunTests; the only difference
|
||||
is the form of the parameters.
|
||||
|
||||
TODO(adonovan): eliminate it.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
string,
|
||||
[]string,
|
||||
[]string
|
||||
```
|
||||
|
||||
## `gopls.tidy`: **Run go mod tidy**
|
||||
|
||||
Runs `go mod tidy` for a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URIs.
|
||||
"URIs": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.toggle_gc_details`: **Toggle gc_details**
|
||||
|
||||
Toggle the calculation of gc annotations.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URI.
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.update_go_sum`: **Update go.sum**
|
||||
|
||||
Updates the go.sum file for a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URIs.
|
||||
"URIs": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.upgrade_dependency`: **Upgrade a dependency**
|
||||
|
||||
Upgrades a dependency in the go.mod file for a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The go.mod file URI.
|
||||
"URI": string,
|
||||
// Additional args to pass to the go command.
|
||||
"GoCmdArgs": []string,
|
||||
// Whether to add a require directive.
|
||||
"AddRequire": bool,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.vendor`: **Run go mod vendor**
|
||||
|
||||
Runs `go mod vendor` for a module.
|
||||
|
||||
Args:
|
||||
|
||||
```
|
||||
{
|
||||
// The file URI.
|
||||
"URI": string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.views`: **List current Views on the server.**
|
||||
|
||||
This command is intended for use by gopls tests only.
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
[]{
|
||||
"ID": string,
|
||||
"Type": string,
|
||||
"Root": string,
|
||||
"Folder": string,
|
||||
"EnvOverlay": []string,
|
||||
}
|
||||
```
|
||||
|
||||
## `gopls.workspace_stats`: **Fetch workspace statistics**
|
||||
|
||||
Query statistics about workspace builds, modules, packages, and files.
|
||||
|
||||
This command is intended for internal use only, by the gopls stats
|
||||
command.
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
{
|
||||
"Files": {
|
||||
"Total": int,
|
||||
"Largest": int,
|
||||
"Errs": int,
|
||||
},
|
||||
"Views": []{
|
||||
"GoCommandVersion": string,
|
||||
"AllPackages": {
|
||||
"Packages": int,
|
||||
"LargestPackage": int,
|
||||
"CompiledGoFiles": int,
|
||||
"Modules": int,
|
||||
},
|
||||
"WorkspacePackages": {
|
||||
"Packages": int,
|
||||
"LargestPackage": int,
|
||||
"CompiledGoFiles": int,
|
||||
"Modules": int,
|
||||
},
|
||||
"Diagnostics": int,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<!-- END Commands: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
@@ -0,0 +1,167 @@
|
||||
# Documentation for contributors
|
||||
|
||||
This documentation augments the general documentation for contributing to the
|
||||
x/tools repository, described at the [repository root](../../CONTRIBUTING.md).
|
||||
|
||||
Contributions are welcome, but since development is so active, we request that
|
||||
you file an issue and claim it before starting to work on something. Otherwise,
|
||||
it is likely that we might already be working on a fix for your issue.
|
||||
|
||||
## Finding issues
|
||||
|
||||
All `gopls` issues are labeled as such (see the [`gopls` label][issue-gopls]).
|
||||
Issues that are suitable for contributors are additionally tagged with the
|
||||
[`help-wanted` label][issue-wanted].
|
||||
|
||||
Before you begin working on an issue, please leave a comment that you are
|
||||
claiming it.
|
||||
|
||||
## Getting started
|
||||
|
||||
Most of the `gopls` logic is in the `golang.org/x/tools/gopls/internal`
|
||||
directory. See [design/implementation.md] for an overview of the code organization.
|
||||
|
||||
## Build
|
||||
|
||||
To build a version of `gopls` with your changes applied:
|
||||
|
||||
```bash
|
||||
cd /path/to/tools/gopls
|
||||
go install
|
||||
```
|
||||
|
||||
To confirm that you are testing with the correct `gopls` version, check that
|
||||
your `gopls` version looks like this:
|
||||
|
||||
```bash
|
||||
$ gopls version
|
||||
golang.org/x/tools/gopls master
|
||||
golang.org/x/tools/gopls@(devel)
|
||||
```
|
||||
|
||||
## Getting help
|
||||
|
||||
The best way to contact the gopls team directly is via the
|
||||
[#gopls-dev](https://app.slack.com/client/T029RQSE6/CRWSN9NCD) channel on the
|
||||
gophers slack. Please feel free to ask any questions about your contribution or
|
||||
about contributing in general.
|
||||
|
||||
|
||||
## Error handling
|
||||
|
||||
It is important for the user experience that, whenever practical,
|
||||
minor logic errors in a particular feature don't cause the server to
|
||||
crash.
|
||||
|
||||
The representation of a Go program is complex. The import graph of
|
||||
package metadata, the syntax trees of parsed files, and their
|
||||
associated type information together form a huge API surface area.
|
||||
Even when the input is valid, there are many edge cases to consider,
|
||||
and this grows by an order of magnitude when you consider missing
|
||||
imports, parse errors, and type errors.
|
||||
|
||||
What should you do when your logic must handle an error that you
|
||||
believe "can't happen"?
|
||||
|
||||
- If it's possible to return an error, then use the `bug.Errorf`
|
||||
function to return an error to the user, but also record the bug in
|
||||
gopls' cache so that it is less likely to be ignored.
|
||||
|
||||
- If it's safe to proceed, you can call `bug.Reportf` to record the
|
||||
error and continue as normal.
|
||||
|
||||
- If there's no way to proceed, call `bug.Fatalf` to record the error
|
||||
and then stop the program with `log.Fatalf`. You can also use
|
||||
`bug.Panicf` if there's a chance that a recover handler might save
|
||||
the situation.
|
||||
|
||||
- Only if you can prove locally that an error is impossible should you
|
||||
call `log.Fatal`. If the error may happen for some input, however
|
||||
unlikely, then you should use one of the approaches above. Also, if
|
||||
the proof of safety depends on invariants broadly distributed across
|
||||
the code base, then you should instead use `bug.Panicf`.
|
||||
|
||||
Note also that panicking is preferable to `log.Fatal` because it
|
||||
allows VS Code's crash reporting to recognize and capture the stack.
|
||||
|
||||
Bugs reported through `bug.Errorf` and friends are retrieved using the
|
||||
`gopls bug` command, which opens a GitHub Issue template and populates
|
||||
it with a summary of each bug and its frequency.
|
||||
The text of the bug is rather fastidiously printed to stdout to avoid
|
||||
sharing user names and error message strings (which could contain
|
||||
project identifiers) with GitHub.
|
||||
Users are invited to share it if they are willing.
|
||||
|
||||
## Testing
|
||||
|
||||
The normal command you should use to run the tests after a change is:
|
||||
|
||||
```bash
|
||||
gopls$ go test -short ./...
|
||||
```
|
||||
|
||||
(The `-short` flag skips some slow-running ones. The trybot builders
|
||||
run the complete set, on a wide range of platforms.)
|
||||
|
||||
Gopls tests are a mix of two kinds.
|
||||
|
||||
- [Marker tests](../internal/test/marker) express each test scenario
|
||||
in a standalone text file that contains the target .go, go.mod, and
|
||||
go.work files, in which special annotations embedded in comments
|
||||
drive the test. These tests are generally easy to write and fast
|
||||
to iterate, but have limitations on what they can express.
|
||||
|
||||
- [Integration tests](../internal/test/integration) are regular Go
|
||||
`func Test(*testing.T)` functions that make a series of calls to an
|
||||
API for a fake LSP-enabled client editor. The API allows you to open
|
||||
and edit a file, navigate to a definition, invoke other LSP
|
||||
operations, and assert properties about the state.
|
||||
|
||||
Due to the asynchronous nature of the LSP, integration tests make
|
||||
assertions about states that the editor must achieve eventually,
|
||||
even when the program goes wrong quickly, it may take a while before
|
||||
the error is reported as a failure to achieve the desired state
|
||||
within several minutes. We recommend that you set
|
||||
`GOPLS_INTEGRATION_TEST_TIMEOUT=10s` to reduce the timeout for
|
||||
integration tests when debugging.
|
||||
|
||||
When they fail, the integration tests print the log of the LSP
|
||||
session between client and server. Though verbose, they are very
|
||||
helpful for debugging once you know how to read them.
|
||||
|
||||
Don't hesitate to [reach out](#getting-help) to the gopls team if you
|
||||
need help.
|
||||
|
||||
### CI
|
||||
|
||||
When you mail your CL and you or a fellow contributor assigns the
|
||||
`Run-TryBot=1` label in Gerrit, the
|
||||
[TryBots](https://golang.org/doc/contribute.html#trybots) will run tests in
|
||||
both the `golang.org/x/tools` and `golang.org/x/tools/gopls` modules, as
|
||||
described above.
|
||||
|
||||
Furthermore, an additional "gopls-CI" pass will be run by _Kokoro_, which is a
|
||||
Jenkins-like Google infrastructure for running Dockerized tests. This allows us
|
||||
to run gopls tests in various environments that would be difficult to add to
|
||||
the TryBots. Notably, Kokoro runs tests on
|
||||
[older Go versions](../README.md#supported-go-versions) that are no longer supported
|
||||
by the TryBots. Per that that policy, support for these older Go versions is
|
||||
best-effort, and test failures may be skipped rather than fixed.
|
||||
|
||||
Kokoro runs are triggered by the `Run-TryBot=1` label, just like TryBots, but
|
||||
unlike TryBots they do not automatically re-run if the "gopls-CI" result is
|
||||
removed in Gerrit. To force a re-run of the Kokoro CI on a CL containing the
|
||||
`Run-TryBot=1` label, you can reply in Gerrit with the comment "kokoro rerun".
|
||||
|
||||
## Debugging
|
||||
|
||||
The easiest way to debug your change is to run a single `gopls` test with a
|
||||
debugger.
|
||||
|
||||
See also [Troubleshooting](troubleshooting.md#troubleshooting).
|
||||
|
||||
<!--TODO(rstambler): Add more details about the debug server and viewing
|
||||
telemetry.-->
|
||||
|
||||
[issue-gopls]: https://github.com/golang/go/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Agopls "gopls issues"
|
||||
[issue-wanted]: https://github.com/golang/go/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Agopls+label%3A"help+wanted" "help wanted"
|
||||
@@ -0,0 +1,183 @@
|
||||
# Running gopls as a daemon
|
||||
|
||||
**Note: this feature is new. If you encounter bugs, please [file an
|
||||
issue](troubleshooting.md#file-an-issue).**
|
||||
|
||||
If you just want to try this out, skip ahead to the [quickstart](#quickstart).
|
||||
|
||||
## Background: gopls execution modes
|
||||
|
||||
Gopls was originally implemented as an LSP sidecar: a process started by
|
||||
editors or editor plugins, and communicated with using jsonrpc 2.0 over
|
||||
stdin/stdout. By executing as a stateful process, gopls can maintain a
|
||||
significant amount of cache and can eagerly perform analysis on the source code
|
||||
being edited.
|
||||
|
||||
This execution mode does not work as well when there are many separate editor
|
||||
processes or when editor processes are short-lived, as is often the case for
|
||||
users of non-IDE editors such as Vim or Emacs. Having many processes means
|
||||
having many caches, consuming a significant amount of system resources. Using
|
||||
short-lived sessions means paying a start-up cost each time a session is
|
||||
created.
|
||||
|
||||
To support these types of workflows, a new mode of gopls execution is supported
|
||||
wherein a single, persistent, shared gopls "daemon" process is responsible for
|
||||
managing all gopls sessions. In this mode, editors still start a gopls sidecar,
|
||||
but this sidecar merely acts as a thin "forwarder", responsible for forwarding
|
||||
the LSP to the shared gopls instance and recording metrics, logs, and rpc
|
||||
traces.
|
||||
|
||||
## Quickstart
|
||||
|
||||
To use a shared gopls instance you must either manage the daemon process
|
||||
yourself, or let the gopls forwarder processes start the shared daemon as
|
||||
needed.
|
||||
|
||||
### Running with `-remote=auto`
|
||||
|
||||
Automatic management of the daemon is easiest, and can be done by passing the
|
||||
flag `-remote=auto` to the gopls process started by your editor. This will
|
||||
cause this process to auto-start the gopls daemon if needed, connect to it, and
|
||||
forward the LSP. For example, here is a reasonable gopls invocation, that sets
|
||||
some additional flags for easier [debugging](#debugging):
|
||||
|
||||
```bash
|
||||
gopls -remote=auto -logfile=auto -debug=:0 -remote.debug=:0 -rpc.trace
|
||||
```
|
||||
|
||||
Note that the shared gopls process will automatically shut down after one
|
||||
minute with no connected clients.
|
||||
|
||||
### Managing the daemon manually
|
||||
|
||||
To manage the gopls daemon process via external means rather than having the
|
||||
forwarders manage it, you must start a gopls daemon process with the
|
||||
`-listen=<addr>` flag, and then pass `-remote=<addr>` to the gopls processes
|
||||
started by your editor.
|
||||
|
||||
For example, to host the daemon on the TCP port `37374`, do:
|
||||
|
||||
```bash
|
||||
gopls -listen=:37374 -logfile=auto -debug=:0
|
||||
```
|
||||
|
||||
And then from the editor, run
|
||||
|
||||
```bash
|
||||
gopls -remote=:37374 -logfile=auto -debug=:0 -rpc.trace
|
||||
```
|
||||
|
||||
If you are on a POSIX system, you can also use unix domain sockets by prefixing
|
||||
the flag values with `unix;`. For example:
|
||||
|
||||
```bash
|
||||
gopls -listen="unix;/tmp/gopls-daemon-socket" -logfile=auto -debug=:0
|
||||
```
|
||||
|
||||
And connect via:
|
||||
|
||||
```bash
|
||||
gopls -remote="unix;/tmp/gopls-daemon-socket" -logfile=auto -debug=:0 -rpc.trace
|
||||
```
|
||||
|
||||
(Note that these flag values MUST be enclosed in quotes, because ';' is a
|
||||
special shell character. For this reason, this syntax is subject to change in
|
||||
the future.)
|
||||
|
||||
## Debugging
|
||||
|
||||
Debugging a shared gopls session is more complicated than a singleton session,
|
||||
because there are now two gopls processes involved with handling the LSP. Here
|
||||
are some tips:
|
||||
|
||||
### Finding logfiles and debug addresses
|
||||
|
||||
When running in daemon mode, you can use the `gopls inspect sessions` command
|
||||
to find the logfile and debug port for your gopls daemon instance (as well as
|
||||
for all its connected clients). By default, this inspects the default daemon
|
||||
(i.e. `-remote=auto`). To inspect a different daemon, use the `-remote` flag
|
||||
explicitly: `gopls -remote=localhost:12345 inspect sessions`.
|
||||
|
||||
This works whether or not you have enabled `-remote.debug`.
|
||||
|
||||
### Traversing debug pages
|
||||
|
||||
When `-debug=:0` is passed to gopls, it runs a webserver that serves stateful
|
||||
debug pages (see [troubleshooting.md](troubleshooting.md)). You can find the
|
||||
actual port hosting these pages by either using the `gopls inspect sessions`
|
||||
command, or by checking the start of the logfile -- it will be one of the first
|
||||
log messages. For example, if using `-logfile=auto`, find the debug address by
|
||||
checking `head /tmp/gopls-<pid>.log`.
|
||||
|
||||
By default, the gopls daemon is not started with `-debug`. To enable it, set
|
||||
the `-remote.debug` flag on the forwarder instance, so that it invokes gopls
|
||||
with `-debug` when starting the daemon.
|
||||
|
||||
The debug pages of the forwarder process will have a link to the debug pages of
|
||||
the daemon server process. Correspondingly, the debug pages of the daemon
|
||||
process will have a link to each of its clients.
|
||||
|
||||
This can help you find metrics, traces, and log files for all of the various
|
||||
servers and clients.
|
||||
|
||||
### Using logfiles
|
||||
|
||||
The gopls daemon is started with logging disabled by default. To customize
|
||||
this, pass `-remote.logfile` to the gopls forwarder. Using
|
||||
`-remote.logfile=auto`, the daemon will log to a default location (on posix
|
||||
systems: `/tmp/gopls-daemon-<pid>.log`).
|
||||
|
||||
The gopls daemon does not log session-scoped messages: those are instead
|
||||
reflected back to the forwarder so that they can be accessed by the editor.
|
||||
Daemon logs will only contain global messages, for example logs when sessions
|
||||
connect and disconnect.
|
||||
|
||||
It is recommended to start the forwarder gopls process with `-rpc.trace`, so
|
||||
that its logfile will contain rpc trace logs specific to the LSP session.
|
||||
|
||||
## Using multiple shared gopls instances
|
||||
|
||||
There may be environments where it is desirable to have more than one shared
|
||||
gopls instance. If managing the daemon manually, this can be done by simply
|
||||
choosing different `-listen` addresses for each distinct daemon process.
|
||||
|
||||
On POSIX systems, there is also support for automatic management of distinct
|
||||
shared gopls processes: distinct daemons can be selected by passing
|
||||
`-remote="auto;<id>"`. Any gopls forwarder passing the same value for `<id>`
|
||||
will use the same shared daemon.
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Why am I not saving as much memory as I expected when using a shared gopls?**
|
||||
|
||||
A: As described in [implementation.md](design/implementation.md), gopls has a
|
||||
concept of view/session/cache. Each session and view map onto exactly one
|
||||
editor session (because they contain things like edited but unsaved buffers).
|
||||
The cache contains things that are independent of any editor session, and can
|
||||
therefore be shared.
|
||||
|
||||
When, for example, three editor session are sharing a single gopls process,
|
||||
they will share the cache but will each have their own session and view. The
|
||||
memory savings in this mode, when compared to three separate gopls processes,
|
||||
corresponds to the amount of cache overlap across sessions.
|
||||
|
||||
Because this hasn't mattered much in the past, it is likely that there is state
|
||||
that can be moved out of the session/view, and into the cache, thereby
|
||||
increasing the amount of memory savings in the shared mode.
|
||||
|
||||
**Q: How do I customize the daemon instance when using `-remote=auto`?**
|
||||
|
||||
The daemon may be customized using flags of the form `-remote.*` on the
|
||||
forwarder gopls. This causes the forwarder to invoke gopls with these settings
|
||||
when starting the daemon. As of writing, we expose the following configuration:
|
||||
|
||||
* `-remote.logfile`: the location of the daemon logfile
|
||||
* `-remote.debug`: the daemon's debug address
|
||||
* `-remote.listen.timeout`: the amount of time the daemon should wait for new
|
||||
connections while there are no current connections, before shutting down.
|
||||
Must be set to a valid `time.Duration` (e.g. `30s` or `5m`). If `0`, listen
|
||||
indefinitely. Default: `1m`.
|
||||
|
||||
Note that once the daemon is already running, setting these flags will not
|
||||
change its configuration. These flags only matter for the forwarder process
|
||||
that actually starts the daemon.
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 370 KiB |
@@ -0,0 +1,436 @@
|
||||
# `gopls` design documentation
|
||||
|
||||
## _A note from the future_
|
||||
|
||||
What follows below is the original design document for gopls, aggregated from
|
||||
various sources spanning 2018 and 2019. Since then, all of the features listed
|
||||
below have been implemented, along with many others. The first two goals have
|
||||
been achieved: gopls is a full implementation of the LSP, and the default
|
||||
backend for VS Code Go and many other editors. The third goal has only been
|
||||
partially realized: while gopls has gained many features, it is not extensible
|
||||
in the sense used in this document: the only way to extend gopls is to modify
|
||||
gopls. The fourth goal is not achieved: while some notable companies are able
|
||||
to use gopls with Bazel, the experience is subpar, and the Go command is the
|
||||
only officially supported build system.
|
||||
|
||||
On the other hand, two of the explicit non-goals have been reconsidered. One is
|
||||
minor: syntax highlighting is now supported in the LSP by way of semantic
|
||||
tokens. The other is major: as gopls gained popularity, it became apparent that
|
||||
its memory footprint was a problem. The size of developer workspaces was
|
||||
increasing faster than the RAM available in typically development environments
|
||||
(particularly with containerized development). Gopls now uses a hybrid of
|
||||
on-disk indexes and in-memory caches, described in more detail in our
|
||||
[blog post on scalability](https://go.dev/blog/gopls-scalability).
|
||||
|
||||
Notably, in anticipating difficulties this doc turned out to be prescient.
|
||||
Gopls has indeed struggled against the core standary library packages upon
|
||||
which it is built, and its user experience is still limited by the LSP.
|
||||
Nevertheless, sticking with the standard library and LSP was the right
|
||||
approach, as despite our small team these decisions have helped gopls keep up
|
||||
with the evolving Go language (i.e. generics), and to integrate with many new
|
||||
text editors.
|
||||
|
||||
Gopls development continues, more than four years later, with a focus on
|
||||
simplicity, reliability, and extensibility. The new, opt-in
|
||||
[Go telemetry](https://github.com/golang/tools/releases/tag/gopls%2Fv0.14.0)
|
||||
will help us attain a higher standard of stability in our releases than we've
|
||||
been able to achieve through Github issues alone. Furthermore, telemetry will
|
||||
allow us to focus on high-priority features, and deprecate historical
|
||||
workarounds that burden the codebase. With greater velocity, we look forward
|
||||
to working with the community on improved refactoring, static analysis, and
|
||||
whatever else the future brings.
|
||||
|
||||
- _Rob Findley (rfindley@google.com), 2023_
|
||||
|
||||
## Goals
|
||||
|
||||
* `gopls` should **become the default editor backend** for the major editors used by Go programmers, fully supported by the Go team.
|
||||
* `gopls` will be a **full implementation of LSP**, as described in the [LSP specification], to standardize as many of its features as possible.
|
||||
* `gopls` will be **clean and extensible** so that it can encompass additional features in the future, allowing Go tooling to become best in class once more.
|
||||
* `gopls` will **support alternate build systems and file layouts**, allowing Go development to be simpler and more powerful in any environment.
|
||||
|
||||
## Context
|
||||
|
||||
While Go has a number of excellent and useful command-line tools that enhance the developer experience, it has become clear that integrating these tools with IDEs can pose challenges.
|
||||
|
||||
Support of these tools has relied on the goodwill of community members, and they have been put under a large burden of support at times as the language, toolchain and environments change. As a result many tools have ceased to work, have had support problems, or become confusing with forks and replacements, or provided an experience that is not as good as it could be.
|
||||
See the section below on [existing solutions](#existing-solutions) for more problems and details.
|
||||
|
||||
This is fine for tools used occasionally, but for core IDE features, this is not acceptable.
|
||||
Autocompletion, jump to definition, formatting, and other such features should always work, as they are key for Go development.
|
||||
|
||||
The Go team will create an editor backend that works in any build system.
|
||||
It will also be able to improve upon the latency of Go tools, since each tool will no longer have to individually run the type-checker on each invocation, instead there will be a long-running process and data can be shared between the definitions, completions, diagnostics, and other features.
|
||||
|
||||
By taking ownership of these tools and packaging them together in the form of gopls, the Go team will ensure that the Go development experience isn’t unnecessarily complicated for Go users.
|
||||
Having one editor backend will simplify the lives of Go developers, the Go team, and the maintainers of Go editor plugins.
|
||||
|
||||
See Rebecca's excellent GopherCon keynote [talk] and [slides] for some more context.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
* Command line speed
|
||||
|
||||
Although gopls will have a command line mode, it will be optimized for long running and not command responsiveness, as such it may not be the right tool for things like CI systems.
|
||||
For such cases there will have to be an alternate tool using the same underlying libraries for consistency.
|
||||
|
||||
* Low memory environments
|
||||
|
||||
In order to do a good job of processing large projects with very low latencies gopls will be holding a lot of information in memory.
|
||||
It is presumed that developers are normally working on systems with significant RAM and this will not be a problem.
|
||||
In general this is upheld by the large memory usage of existing IDE solutions (like IntelliJ)
|
||||
|
||||
* Syntax highlighting
|
||||
|
||||
At the moment there is no editor that delegates this functionality to a separate binary, and no standard way of doing it.
|
||||
|
||||
## Existing solutions
|
||||
|
||||
Every year the Go team conducts a survey, asking developers about their experiences with the language.
|
||||
|
||||
One question that is asked is “How do you feel about your editor?”.
|
||||
|
||||
The responses told a very negative story. Some categorized quotes:
|
||||
|
||||
* Setup
|
||||
* "Hard to install and configure"
|
||||
* "Inadequate documentation"
|
||||
* Performance
|
||||
* "Performance is very poor"
|
||||
* "Pretty slow in large projects"
|
||||
* Reliability
|
||||
* "Features work one day, but not the next"
|
||||
* "Tooling is not updated with new language features"
|
||||
|
||||
Each editor has its own plugin that shells out to a variety of tools, many of which break with new Go releases or because they are no longer maintained.
|
||||
|
||||
The individual tools each have to do the work to understand the code and all its transitive dependencies.
|
||||
|
||||
Each feature is a different tool, with a different set of patterns for its command line, a different way to accept input and parse output, a different way of specifying source code locations.
|
||||
To support its existing feature set, VSCode installed 24 different command line tools, many of which have options or forks to configure. When looking at the set of tools that needed to be migrated to modules, across all the editors, there were 63 separate tools.
|
||||
|
||||
All these tools need to understand the code, and they use the same standard libraries to do it. Those libraries are optimized for these kinds of tools, but even so processing that much code takes a lot of time time. Almost none of the tools are capable of returning results within 100ms.
|
||||
As developers type in their editor, multiple of these features need to activate, which means they are not just paying the cost once, but many times. The overall effect is an editing experience that feels sluggish, and features that are either not enabled or sometimes produce results that appear so slowly they are no longer useful when they arrive. This is a problem that increases with the size of the code base, which means it is getting worse over time, and is especially bad for the kinds of large code bases companies are dealing with as they use Go for more major tasks.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Complete feature set
|
||||
|
||||
For gopls to be considered a success it has to implement the full feature set discussed [below](#Features).
|
||||
This is the set of features that users need in order to feel as productive as they were with the tooling it is replacing. It does not include every feature of previous implementations, there are some features that are almost never used that should be dropped (like guru's pointer analysis) and some other features that do not easily fit and will have to be worked around (replacing the save hook/linter).
|
||||
|
||||
### Equivalent or better experience
|
||||
|
||||
For all of those features, the user experience must match or exceed the current one available in all editors.
|
||||
This is an easy statement to make, but a hard one to validate or measure. Many of the possible measures fail to capture the experience.
|
||||
|
||||
For instance, if an attempt was made to measure the latency of a jump to definition call, the results would be fairly consistent from the old godef tool. From the gopls implementation there may be a much larger range of latencies, with the best being orders of magnitude faster, and the worse slightly worse, because gopls attempts to do far more work, but manages to cache it across calls.
|
||||
|
||||
Or for a completion call, it might be slower but produce a better first match such that users accept it more often, resulting in an overall better experience.
|
||||
|
||||
For the most part this has to rely on user reports. If users are refusing to switch because the experience is not better, it is clearly not done, if they are switching but most people are complaining, there are probably enough areas that are better to make the switch compelling but other areas which are worse. If most people are switching and either staying silent or being positive, it is probably done. When writing tools, the user is all that matters.
|
||||
|
||||
### Solid community of contributors
|
||||
|
||||
The scope and scale of the problem gopls is trying to solve is untenable for the core Go team, it is going to require a strong community to make it all happen.
|
||||
|
||||
This implies the code must be easy to contribute to, and easy for many developers to work on in parallel. The functionality needs to be well decoupled, and have a thorough testing story.
|
||||
|
||||
### Latencies that fall within user tolerance
|
||||
|
||||
There has been a lot of research on acceptable latencies for user actions.
|
||||
<!-- TODO: research links -->
|
||||
The main result that affects gopls is that feedback in direct response to continuous user actions needs to be under 100ms to be imperceptible, and anything above 200ms aggravates the user.
|
||||
This means in general the aim has to be <100ms for anything that happens as the developer types.
|
||||
There will always be cases where gopls fails to meet this deadline, and there needs to be ways to make the user experience okay in those cases, but in general the point of this deadline is to inform the basic architecture design, any solution that cannot theoretically meet this goal in the long term is the wrong answer.
|
||||
|
||||
### Easy to configure
|
||||
|
||||
Developers are very particular, and have very differing desires in their coding experience. gopls is going to have to support a significant amount of flexibility, in order to meet those desires.
|
||||
The default settings however with no configuration at all must be the one that is best experience for most users, and where possible the features must be flexible without configuration so that the client can easily make the choices about treatment without changing its communication with gopls.
|
||||
|
||||
## Difficulties
|
||||
|
||||
### Volume of data
|
||||
|
||||
<!-- TODO: project sizes -->
|
||||
* Small:
|
||||
* Medium:
|
||||
* Large:
|
||||
* Corporate mono-repo: Much much bigger
|
||||
|
||||
Parsing and type checking large amounts of code is quite expensive, and the converted forms use a lot of space. As gopls has to keep updating this information while the developer types, it needs to manage how it caches the converted forms very carefully to balance memory use vs speed.
|
||||
|
||||
### Cache invalidation
|
||||
|
||||
The basic unit of operation for the type checking is the package, but the basic unit of operation for an editor is the file.
|
||||
gopls needs to be able to map files to packages efficiently, so that when files change it knows which packages need to be updated (along with any other packages that transitively depended on them).
|
||||
This is made especially difficult by the fact that changing the content of a file can modify which packages it is considered part of (either by changing the package declaration or the build tags), a file can be in more than one package, and changes can be made to files without using the editor, in which case it will not notify us of the changes.
|
||||
|
||||
### Inappropriate core functionality
|
||||
|
||||
The base libraries for Go (things like [go/token], [go/ast] and [go/types]) are all designed for compiler-like applications.
|
||||
They tend to worry more about throughput than memory use, they have structures that are intended to grow and then be thrown away at program exit, and they are not designed to keep going in the presence of errors in the source they are handling.
|
||||
They also have no abilities to do incremental changes.
|
||||
|
||||
Making a long running service work well with those libraries is a very large challenge, but writing new libraries would be far more work, and cause a significant long term cost as both sets of libraries would have to be maintained. Right now it is more important to get a working tool into the hands of users. In the long term this decision may have to be revisited, new low level libraries may be the only way to keep pushing the capabilities forwards.
|
||||
|
||||
### Build system capabilities
|
||||
|
||||
gopls is supposed to be build system agnostic, but it must use the build system to discover how files map to packages. When it tries to do so, even when the functionality is the same, the costs (in time, CPU and memory) are very different, and can significantly impact the user experience. Designing how gopls interacts with the build system to try to minimize or hide these differences is hard.
|
||||
|
||||
### Build tags
|
||||
|
||||
The build tag system in Go is quite powerful, and has many use cases. Source files can exclude themselves using powerful boolean logic on the set of active tags.
|
||||
It is however designed for specifying the set of active tags on the command line, and the libraries are all designed to cope with only one valid combination at a time. There is also no way to work out the set of valid combinations.
|
||||
|
||||
Type checking a file requires knowledge of all the other files in the same package, and that set of files is modified by the build tags. The set of exported identifiers of a package is also affected by which files are in the package, and thus its build tags.
|
||||
|
||||
This means that even for files or packages that have no build tag controls it is not possible to produce correct results without knowing the set of build tags to consider.
|
||||
This makes it very hard to produce useful results when viewing a file.
|
||||
|
||||
### Features not supported by LSP
|
||||
|
||||
There are some things it would be good to be able to do that do not fit easily into the existing LSP protocol.
|
||||
For instance, displaying control flow information, automatic struct tags, complex refactoring...
|
||||
|
||||
Each feature will have to be considered carefully, and either propose a change to LSP, or add a way to have gopls specific extensions to the protocol that are still easy to use in all the editor plugins.
|
||||
|
||||
To avoid these at the start, only core LSP features will be implemented, as they are sufficient to meet the baseline requirements anyway, but the potential features need to be kept in mind in the core architecture.
|
||||
|
||||
### Distribution
|
||||
|
||||
Making sure that users are using the right version of gopls is going to be a problem. Each editor plugin is probably going to install the tools in its own way, some will choose to install it system wide, some will keep their own copy.
|
||||
|
||||
Because it is a brand new tool, it will be changing rapidly. If users are not informed they are on an old version they will be experiencing problems that have already been fixed, which is worse for them, and then probably reporting them, which wastes time for the gopls team. There needs to be a mechanism for gopls to check if is up to date, and a recommended way to install an up to date version.
|
||||
|
||||
### Debugging user problems
|
||||
|
||||
gopls is essentially a very stateful long running server on the developer's machine. Its basic operation is affected by many things, from the users environment to the contents of the local build cache. The data it is operating on is often a confidential code base that cannot be shared.
|
||||
All of these things make it hard for users to report a bug usefully, or create a minimal reproduction.
|
||||
|
||||
There needs to be easy ways for users to report what information they can, and ways to attempt to reproduce problems without their entire state. This is also needed to produce regression tests.
|
||||
|
||||
## Basic design decisions
|
||||
|
||||
There are some fundamental architecture decisions that affect much of the rest of the design of the tool, making fundamental trade offs that impact the user experience.
|
||||
|
||||
### Process lifetime: *managed by the editor*
|
||||
|
||||
Processing a large code base to fully type check and then analyze it within the latency requirements is not feasible, and is one of the primary problems with the existing solutions. This remains true even if the computed information was cached on disk, as running analyzers and type checkers ends up requiring the full AST of all files in the dependency graph.
|
||||
It is theoretically possible to do better, but only with a major re-write of the existing parsing and type checking libraries, something that is not feasible at this time.
|
||||
|
||||
This implies that gopls should be a long running process, that is able to cache and pre-calculate results in memory so that when a request arrives it can produce the answer much faster.
|
||||
|
||||
It could run as a daemon on the user's machine, but there are a lot of issues with managing a daemon. It may well be the right choice in the long term, and it should be allowed for in the fundamental architecture design, but to start with it will instead have a process that lasts as long as the editor that starts it, and that can easily be restarted.
|
||||
|
||||
### Caching: *in memory*
|
||||
|
||||
Persistent disk caches are very expensive to maintain, and require solving a lot of extra problems.
|
||||
Although building the information required is expensive compared to the latencies required of the requests, it is fairly minor compared to the startup times of an editor, so it is expected that rebuilding the information when gopls is restarted will be acceptable.
|
||||
|
||||
The advantage gained from this is that gopls becomes stateless across restarts which means if it has issues or gets its state confused, a simple restart will often fix the problem.
|
||||
It also means that when users report problems, the entire state of the on disk cache is not needed to diagnose and reproduce the issue.
|
||||
|
||||
### Communication: *stdin/stdout JSON*
|
||||
|
||||
The LSP specification defines the JSON messages that are normally used, but it does not define how those message should be sent, and there are implementations of the LSP that do not use JSON (for instance, Protocol buffers are an option).
|
||||
|
||||
The constraints on gopls are that it must be easy to integrate into *every editor* on *all operating systems*, and that it should not have large external dependencies.
|
||||
|
||||
JSON is part of the Go standard library, and is also the native language of LSP, so it makes the most sense. By far the best supported communication mechanism is the standard input and output of a process, and the common client implementations all have ways of using [JSON rpc 2] in this mode. There were no complete and low dependency implementations of this protocol in Go, but it is a fairly small protocol on top of the JSON library that can be implemented with a moderate effort, and would be a generally useful library to have anyway.
|
||||
|
||||
In the future it is expected to run in separated client server mode, so writing it in a way that could use sockets instead of stdin/stdout from the start was the best way to make sure it remained possible. It was also a huge debugging aid to be able to run the gopls server by hand and watch/debug it outside the editor.
|
||||
|
||||
### Running other tools: *no*
|
||||
|
||||
<!--- TODO: subprocess discuss --->
|
||||
|
||||
## Features
|
||||
|
||||
<!--TODO(rstambler): Generate a file that lists all of the supported features.-->
|
||||
|
||||
There is a set of features that gopls needs to expose to be a comprehensive IDE solution.
|
||||
The following is the minimum set of features, along with their existing solutions and how they should map to the LSP.
|
||||
|
||||
### Introspection
|
||||
|
||||
Introspection features tell developers information about their code while they work. They do not make or suggest changes.
|
||||
|
||||
---
|
||||
Diagnostics | Static analysis results of the code, including compilation and lint errors
|
||||
----------- | ---
|
||||
Requires | Full go/analysis run, which needs full AST, type and SSA information
|
||||
LSP | [`textDocument/publishDiagnostics`]
|
||||
Previous | `go build`, `go vet`, `golint`, [errcheck], [staticcheck] <!-- TODO: and all the rest -->
|
||||
| | This is one of the most important IDE features, allowing fast turn around without having to run compilers and checkers in the shell. Often used to power problem lists, gutter markers and squiggle underlines in the IDE. <br/> There is some complicated design work to do in order to let users customize the set of checks being run, preferably without having to recompile the main LSP binary.
|
||||
|
||||
---
|
||||
Hover | Information about the code under the cursor.
|
||||
-------- | ---
|
||||
Requires | AST and type information for the file and all dependencies
|
||||
LSP | [`textDocument/hover`]
|
||||
Previous | [godoc], [gogetdoc]
|
||||
| | Used when reading code to display information known to the compiler but not always obvious from the code. For instance it may return the types of identifiers, or the documentation.
|
||||
|
||||
---
|
||||
Signature help | Function parameter information and documentation
|
||||
-------------- | ---
|
||||
Requires | AST and type information for the file and all dependencies
|
||||
LSP | [`textDocument/signatureHelp`]
|
||||
Previous | [gogetdoc]
|
||||
| | As a function call is being typed into code, it is helpful to know the parameters of that call to enable the developer to call it correctly.
|
||||
|
||||
### Navigation
|
||||
|
||||
Navigation features are designed to make it easier for a developer to find their way round a code base.
|
||||
|
||||
---
|
||||
Definition | Select an identifier, and jump to the code where that identifier was defined.
|
||||
---------- | ---
|
||||
Requires | Full type information for file and all dependencies
|
||||
LSP | [`textDocument/declaration`]
|
||||
| | [`textDocument/definition`]
|
||||
| | [`textDocument/typeDefinition`]
|
||||
Previous | [godef] |
|
||||
| | Asking the editor to open the place where a symbol was defined is one of the most commonly used code navigation tools inside an IDE when available. It is especially valuable when exploring an unfamiliar code base.<br/>Due to a limitation of the compiler output, it is not possible to use the binary data for this task (specifically it does not know column information) and thus it must parse from source.
|
||||
|
||||
---
|
||||
Implementation | Reports the types that implement an interface
|
||||
-------------- | ---
|
||||
Requires | Full workspace type knowledge
|
||||
LSP | [`textDocument/implementation`]
|
||||
Previous | [impl]
|
||||
| | This feature is hard to scale up to large code bases, and is going to take thought to get right. It may be feasible to implemented a more limited form in the meantime.
|
||||
|
||||
---
|
||||
Document symbols | Provides the set of top level symbols in the current file.
|
||||
---------------- | ---
|
||||
Requires | AST of the current file only
|
||||
LSP | [`textDocument/documentSymbol`]
|
||||
Previous | [go-outline], [go-symbols]
|
||||
| | Used to drive things like outline mode.
|
||||
|
||||
---
|
||||
References | Find all references to the symbol under the cursor.
|
||||
---------- | ---
|
||||
Requires | AST and type information for the **reverse** transitive closure
|
||||
LSP | [`textDocument/references`]
|
||||
Previous | [guru]
|
||||
| | This requires knowledge of every package that could possible depend on any packages the current file is part of. In the past this has been implemented either by global knowledge, which does not scale, or by specifying a "scope" which confused users to the point where they just did not use the tools. gopls is probably going to need a more powerful solution in the long term, but to start with automatically limiting the scope may produce acceptable results. This would probably be the module if known, or some sensible parent directory otherwise.
|
||||
|
||||
---
|
||||
Folding | Report logical hierarchies of blocks
|
||||
-------- | ---
|
||||
Requires | AST of the current file only
|
||||
LSP | [`textDocument/foldingRange`]
|
||||
Previous | [go-outline]
|
||||
| | This is normally used to provide expand and collapse behavior in editors.
|
||||
|
||||
---
|
||||
Selection | Report regions of logical selection around the cursor
|
||||
--------- | ---
|
||||
Requires | AST of the current file only
|
||||
LSP | [`textDocument/selectionRange`]
|
||||
Previous | [guru]
|
||||
| | Used in editor features like expand selection.
|
||||
|
||||
|
||||
### Edit assistance
|
||||
|
||||
These features suggest or apply edits to the code for the user, including refactoring features, for which there are many potential use cases.
|
||||
Refactoring is one of the places where Go tools could potentially be very strong, but have not been so far, and thus there is huge potential for improvements in the developer experience.
|
||||
There is not yet a clear understanding of the kinds of refactoring people need or how they should express them however, and there are weaknesses in the LSP protocol around this.
|
||||
This means it may be much more of a research project.
|
||||
|
||||
|
||||
---
|
||||
Format | Fix the formatting of the file
|
||||
-------- | ---
|
||||
Requires | AST of current file
|
||||
LSP | [`textDocument/formatting`]
|
||||
| | [`textDocument/rangeFormatting`]
|
||||
| | [`textDocument/onTypeFormatting`]
|
||||
Previous | [gofmt], [goimports], [goreturns]
|
||||
| | It will use the standard format package. <br/> Current limitations are that it does not work on malformed code. It may need some very careful changes to the formatter to allow for formatting an invalid AST or changes to force the AST to a valid mode. These changes would improve range and file mode as well, but are basically vital to onTypeFormatting
|
||||
|
||||
---
|
||||
Imports | Rewrite the imports block automatically to match the symbols used.
|
||||
-------- | ---
|
||||
Requires | AST of the current file and full symbol knowledge for all candidate packages.
|
||||
LSP | [`textDocument/codeAction`]
|
||||
Previous | [goimports], [goreturns]
|
||||
| | This needs knowledge of packages that are not yet in use, and the ability to find those packages by name. <br/> It also needs exported symbol information for all the packages it discovers. <br/> It should be implemented using the standard imports package, but there may need to be exposed a more fine grained API than just a file rewrite for some of the interactions.
|
||||
|
||||
---
|
||||
Autocompletion | Makes suggestions to complete the entity currently being typed.
|
||||
-------------- | ---
|
||||
Requires | AST and type information for the file and all dependencies<br/> Also full exported symbol knowledge for all packages.
|
||||
LSP | [`textDocument/completion`]
|
||||
| | [`completionItem/resolve`]
|
||||
Previous | [gocode]
|
||||
| | Autocomplete is one of the most complicated features, and the more it knows the better its suggestions can be. For instance it can autocomplete into packages that are not yet being imported if it has their public symbols. It can make better suggestions of options if it knows what kind of program you are writing. It can suggest better arguments if it knows how you normally call a function. It can suggest entire patterns of code if it knows they are common. Unlike many other features, which have a specific task, and once it is doing that task the feature is done, autocomplete will never be finished. Balancing and improving both the candidates and how they are ranked will be a research problem for a long time to come.
|
||||
|
||||
---
|
||||
Rename | Rename an identifier
|
||||
-------- | ---
|
||||
Requires | AST and type information for the **reverse** transitive closure
|
||||
LSP | [`textDocument/rename`]
|
||||
| | [`textDocument/prepareRename`]
|
||||
Previous | [gorename]
|
||||
| | This uses the same information that find references does, with all the same problems and limitations. It is slightly worse because the changes it suggests make it intolerant of incorrect results. It is also dangerous using it to change the public API of a package.
|
||||
|
||||
---
|
||||
Suggested fixes | Suggestions that can be manually or automatically accepted to change the code
|
||||
--------------- | ---
|
||||
Requires | Full go/analysis run, which needs full AST, type and SSA information
|
||||
LSP | [`textDocument/codeAction`]
|
||||
Previous | N/A
|
||||
| | This is a brand new feature powered by the new go/analysis engine, and it should allow a huge amount of automated refactoring.
|
||||
|
||||
[LSP specification]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/
|
||||
[talk]: TODO
|
||||
[slides]: https://github.com/gophercon/2019-talks/blob/master/RebeccaStambler-GoPleaseStopBreakingMyEditor/slides.pdf "Go, please stop breaking my editor!"
|
||||
[JSON rpc 2]: https://www.jsonrpc.org/specification
|
||||
|
||||
[errcheck]: https://github.com/kisielk/errcheck
|
||||
[go-outline]: https://github.com/lukehoban/go-outline
|
||||
[go-symbols]: https://github.com/acroca/go-symbols
|
||||
[gocode]: https://github.com/stamblerre/gocode
|
||||
[godef]: https://github.com/rogpeppe/godef
|
||||
[godoc]: https://golang.org/cmd/godoc
|
||||
[gofmt]: https://golang.org/cmd/gofmt
|
||||
[gogetdoc]: https://github.com/zmb3/gogetdoc
|
||||
[goimports]: https://pkg.go.dev/golang.org/x/tools/cmd/goimports
|
||||
[gorename]: https://pkg.go.dev/golang.org/x/tools/cmd/gorename
|
||||
[goreturns]: https://github.com/sqs/goreturns
|
||||
[gotags]: https://github.com/jstemmer/gotags
|
||||
[guru]: https://pkg.go.dev/golang.org/x/tools/cmd/guru
|
||||
[impl]: https://github.com/josharian/impl
|
||||
[staticcheck]: https://staticcheck.io/docs/
|
||||
[go/types]: https://golang.org/pkg/go/types/
|
||||
[go/ast]: https://golang.org/pkg/go/ast/
|
||||
[go/token]: https://golang.org/pkg/go/token/
|
||||
|
||||
[`completionItem/resolve`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#completionItem_resolve
|
||||
[`textDocument/codeAction`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_codeAction
|
||||
[`textDocument/completion`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_completion
|
||||
[`textDocument/declaration`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_declaration
|
||||
[`textDocument/definition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_definition
|
||||
[`textDocument/documentLink`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentLink
|
||||
[`textDocument/documentSymbol`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentSymbol
|
||||
[`textDocument/foldingRange`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_foldingRange
|
||||
[`textDocument/formatting`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_formatting
|
||||
[`textDocument/highlight`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_highlight
|
||||
[`textDocument/hover`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_hover
|
||||
[`textDocument/implementation`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_implementation
|
||||
[`textDocument/onTypeFormatting`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_onTypeFormatting
|
||||
[`textDocument/prepareRename`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_prepareRename
|
||||
[`textDocument/publishDiagnostics`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_publishDiagnostics
|
||||
[`textDocument/rangeFormatting`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_rangeFormatting
|
||||
[`textDocument/references`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_references
|
||||
[`textDocument/rename`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_rename
|
||||
[`textDocument/selectionRange`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_selectionRange
|
||||
[`textDocument/signatureHelp`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_signatureHelp
|
||||
[`textDocument/typeDefinition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_typeDefinition
|
||||
[`workspace/didChangeWatchedFiles`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#workspace_didChangeWatchedFiles
|
||||
@@ -0,0 +1,172 @@
|
||||
|
||||
# Gopls architecture
|
||||
|
||||
Last major update: Jan 16 2024
|
||||
|
||||
This doc presents a high-level overview of the structure of gopls to
|
||||
help new contributors find their way. It is not intended to be a
|
||||
complete description of the implementation, nor even of any key
|
||||
components; for that, the package documentation (linked below) and
|
||||
other comments within the code are a better guide.
|
||||
|
||||
The diagram below shows selected components of the gopls module and
|
||||
their relationship to each other according to the Go import graph.
|
||||
Tests and test infrastructure are not shown, nor are utility packages,
|
||||
nor packages from the [x/tools] module. For brevity, packages are
|
||||
referred to by their last segment, which is usually unambiguous.
|
||||
|
||||
The height of each blob corresponds loosely to its technical depth.
|
||||
Some blocks are wide and shallow, such as [protocol], which declares
|
||||
Go types for the entire LSP protocol. Others are deep, such as [cache]
|
||||
and [golang], as they contain a lot of dense logic and algorithms.
|
||||
|
||||
<!-- Source: https://docs.google.com/drawings/d/1CK6YSLt7G3svRoZf7skJI-lxRol2VI90YOxHcYS0DP4 -->
|
||||

|
||||
|
||||
Starting from the bottom, we'll describe the various components.
|
||||
|
||||
The lowest layer defines the request and response types of the
|
||||
Language Server Protocol:
|
||||
|
||||
- The [protocol] package defines the standard protocol; it is mostly
|
||||
generated mechanically from the schema definition provided by
|
||||
Microsoft.
|
||||
The most important type is DocumentURI, which represents a `file:`
|
||||
URL that identifies a client editor document. It also provides
|
||||
`Mapper`, which maps between the different coordinate systems used
|
||||
for source positions: UTF-8, UTF-16, and token.Pos.
|
||||
|
||||
- The [command] package defines Gopls's non-standard commands, which
|
||||
are all invoked through the `workspace/executeCommand` extension
|
||||
mechanism. These commands are typically returned by the server as
|
||||
continuations of Code Actions or Code Lenses; most clients do not
|
||||
construct calls to them directly.
|
||||
|
||||
The next layer defines a number of important and very widely used data structures:
|
||||
|
||||
- The [file] package defines the primary abstractions of a client
|
||||
file: its `Identity` (URI and content hash), and its `Handle` (which
|
||||
additionally provides the version and content of a particular
|
||||
snapshot of the file.
|
||||
|
||||
- The [parsego] package defines `File`, the parsed form of a Go source
|
||||
file, including its content, syntax tree, and coordinary mappings
|
||||
(Mapper and token.File). The package performs various kinds of tree
|
||||
repair to work around error-recovery shortcomings of the Go parser.
|
||||
|
||||
- The [metadata] package defines `Package`, an abstraction of the
|
||||
metadata of a Go package, similar to the output of `go list -json`.
|
||||
Metadata is produced from [go/packages], which takes
|
||||
care of invoking `go list`. (Users report that it works to some extent
|
||||
with a GOPACKAGESDRIVER for Bazel, though we maintain no tests for this
|
||||
scenario.)
|
||||
|
||||
The package also provides `Graph`, the complete import graph for a
|
||||
workspace; each graph node is a `Package`.
|
||||
|
||||
The [settings] layer defines the data structure (effectively a large
|
||||
tree) for gopls configuration options, along with its JSON encoding.
|
||||
|
||||
The [cache] layer is the largest and most complex component of gopls.
|
||||
It is concerned with state management, dependency analysis, and invalidation:
|
||||
the `Session` of communication with the client;
|
||||
the `Folder`s that the client has opened;
|
||||
the `View` of a particular workspace tree with particular build
|
||||
options;
|
||||
the `Snapshot` of the state of all files in the workspace after a
|
||||
particular edit operation;
|
||||
the contents of all files, whether saved to disk (`DiskFile`) or
|
||||
edited and unsaved (`Overlay`);
|
||||
the `Cache` of in-memory memoized computations,
|
||||
such as parsing go.mod files or build the symbol index;
|
||||
and the `Package`, which holds the results of type checking a package
|
||||
from Go syntax.
|
||||
|
||||
The cache layer depends on various auxiliary packages, including:
|
||||
|
||||
- The [filecache] package, which manages gopls' persistent, transactional,
|
||||
file-based key/value store.
|
||||
|
||||
- The [xrefs], [methodsets], and [typerefs] packages define algorithms
|
||||
for constructing indexes of information derived from type-checking,
|
||||
and for encoding and decoding these serializable indexes in the file
|
||||
cache.
|
||||
|
||||
Together these packages enable the fast restart, reduced memory
|
||||
consumption, and synergy across processes that were delivered by the
|
||||
v0.12 redesign and described in ["Scaling gopls for the growing Go
|
||||
ecosystem"](https://go.dev/blog/gopls-scalability).
|
||||
|
||||
The cache also defines gopls's [go/analysis] driver, which runs
|
||||
modular analysis (similar to `go vet`) across the workspace.
|
||||
Gopls also includes a number of analysis passes that are not part of vet.
|
||||
|
||||
The next layer defines four packages, each for handling files in a
|
||||
particular language:
|
||||
[mod] for go.mod files;
|
||||
[work] for go.work files;
|
||||
[template] for files in `text/template` syntax; and
|
||||
[golang], for files in Go itself.
|
||||
This package, by far the largest, provides the main features of gopls:
|
||||
navigation, analysis, and refactoring of Go code.
|
||||
As most users imagine it, this package _is_ gopls.
|
||||
|
||||
The [server] package defines the LSP service implementation, with one
|
||||
handler method per LSP request type. Each handler switches on the type
|
||||
of the file and dispatches to one of the four language-specific
|
||||
packages.
|
||||
|
||||
The [lsprpc] package connects the service interface to our [JSON RPC](jsonrpc2)
|
||||
server.
|
||||
|
||||
Bear in mind that the diagram is a dependency graph, a "static"
|
||||
viewpoint of the program's structure. A more dynamic viewpoint would
|
||||
order the packages based on the sequence in which they are encountered
|
||||
during processing of a particular request; in such a view, the bottom
|
||||
layer would represent the "wire" (protocol and command), the next
|
||||
layer up would hold the RPC-related packages (lsprpc and server), and
|
||||
features (e.g. golang, mod, work, template) would be at the top.
|
||||
|
||||
<!--
|
||||
A dynamic view would be an interesting topic for another article.
|
||||
This slide deck [requires Google network]
|
||||
The Life of a (gopls) Query (Oct 2021)
|
||||
https://docs.google.com/presentation/d/1c8XJaIldzii-F3YvEOPWHK_MQJ_o8ua5Bct1yDa3ZlU
|
||||
provides useful (if somewhat out of date) information.
|
||||
-->
|
||||
|
||||
The [cmd] package defines the command-line interface of the `gopls`
|
||||
command, around which gopls's main package is just a trivial wrapper.
|
||||
It is usually run without arguments, causing it to start a server and
|
||||
listen indefinitely.
|
||||
It also provides a number of subcommands that start a server, make a
|
||||
single request to it, and exit, providing traditional batch-command
|
||||
access to server functionality. These subcommands are primarily
|
||||
provided as a debugging aid (but see
|
||||
[#63693](https://github.com/golang/go/issues/63693)).
|
||||
|
||||
[cache]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/cache
|
||||
[cmd]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/cmd
|
||||
[command]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/protocol/command
|
||||
[debug]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/debug
|
||||
[file]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/file
|
||||
[filecache]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/filecache
|
||||
[go/analysis]: https://pkg.go.dev/golang.org/x/tools@master/go/analysis
|
||||
[go/packages]: https://pkg.go.dev/golang.org/x/tools@master/go/packages
|
||||
[gopls]: https://pkg.go.dev/golang.org/x/tools/gopls@master
|
||||
[jsonrpc2]: https://pkg.go.dev/golang.org/x/tools@master/internal/jsonrpc2
|
||||
[lsprpc]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/lsprpc
|
||||
[memoize]: https://github.com/golang/tools/tree/master/internal/memoize
|
||||
[metadata]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/cache/metadata
|
||||
[methodsets]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/cache/methodsets
|
||||
[mod]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/mod
|
||||
[parsego]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/cache/parsego
|
||||
[protocol]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/protocol
|
||||
[server]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/server
|
||||
[settings]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/settings
|
||||
[golang]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/golang
|
||||
[template]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/template
|
||||
[typerefs]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/cache/typerefs
|
||||
[work]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/work
|
||||
[x/tools]: https://github.com/golang/tools@master
|
||||
[xrefs]: https://pkg.go.dev/golang.org/x/tools/gopls@master/internal/cache/xrefs
|
||||
@@ -0,0 +1,89 @@
|
||||
# Documentation for plugin authors
|
||||
|
||||
If you are integrating `gopls` into an editor by writing an editor plugin, there are quite a few semantics of the communication between the editor and `gopls` that are not specified by the [LSP specification].
|
||||
|
||||
We attempt to document those details along with any other information that has been helpful to other plugin authors here.
|
||||
|
||||
If you are implementing a plugin yourself and have questions this page does not answer, please reach out to us to ask, and then also contribute your findings back to this page.
|
||||
|
||||
## Supported features
|
||||
|
||||
For the most part you should look at the [list](status.md#supported-features) in the current status document to know if gopls supports a feature.
|
||||
For a truly authoritative answer you should check the [result][InitializeResult] of the [initialize] request, where gopls enumerates its support in the [ServerCapabilities].
|
||||
|
||||
|
||||
## Positions and ranges
|
||||
|
||||
Many LSP requests pass position or range information. This is described in the [LSP specification][lsp-text-documents]:
|
||||
|
||||
> A position inside a document (see Position definition below) is expressed as a zero-based line and character offset. The offsets are based on a UTF-16 string representation. So a string of the form a𐐀b the character offset of the character a is 0, the character offset of 𐐀 is 1 and the character offset of b is 3 since 𐐀 is represented using two code units in UTF-16.
|
||||
|
||||
This means that integrators will need to calculate UTF-16 based column offsets.
|
||||
Use `protocol.Mapper` for all the conversions.
|
||||
|
||||
## Edits
|
||||
|
||||
In order to deliver changes from gopls to the editor, the LSP supports arrays of [`TextEdit`][lsp-textedit]s in responses.
|
||||
The spec specifies exactly how these should be applied:
|
||||
|
||||
> All text edits ranges refer to positions in the original document. Text edits ranges must never overlap, that means no part of the original document must be manipulated by more than one edit. However, it is possible that multiple edits have the same start position: multiple inserts, or any number of inserts followed by a single remove or replace edit. If multiple inserts have the same position, the order in the array defines the order in which the inserted strings appear in the resulting text.
|
||||
|
||||
All `[]TextEdit` are sorted such that applying the array of deltas received in reverse order achieves the desired result that holds with the spec.
|
||||
|
||||
## Errors
|
||||
|
||||
Various error codes are described in the [LSP specification][lsp-response]. We are still determining what it means for a method to return an error; are errors only for low-level LSP/transport issues or can other conditions cause errors to be returned? See some of this discussion on [#31526].
|
||||
|
||||
The method chosen is currently influenced by the exact treatment in the currently popular editor integrations. It may well change, and ideally would become more coherent across requests.
|
||||
|
||||
* [`textDocument/codeAction`]: Return error if there was an error computing code actions.
|
||||
* [`textDocument/completion`]: Log errors, return empty result list.
|
||||
* [`textDocument/definition`]: Return error if there was an error computing the definition for the position.
|
||||
* [`textDocument/typeDefinition`]: Return error if there was an error computing the type definition for the position.
|
||||
* [`textDocument/formatting`]: Return error if there was an error formatting the file.
|
||||
* [`textDocument/highlight`]: Log errors, return empty result.
|
||||
* [`textDocument/hover`]: Return empty result.
|
||||
* [`textDocument/documentLink`]: Log errors, return nil result.
|
||||
* [`textDocument/publishDiagnostics`]: Log errors if there were any while computing diagnostics.
|
||||
* [`textDocument/references`]: Log errors, return empty result.
|
||||
* [`textDocument/rename`]: Return error if there was an error computing renames.
|
||||
* [`textDocument/signatureHelp`]: Log errors, return nil result.
|
||||
* [`textDocument/documentSymbols`]: Return error if there was an error computing document symbols.
|
||||
|
||||
## Watching files
|
||||
|
||||
It is fairly normal for files that affect `gopls` to be modified outside of the editor it is associated with.
|
||||
|
||||
For instance, files that are needed to do correct type checking are modified by switching branches in git, or updated by a code generator.
|
||||
|
||||
Monitoring files inside gopls directly has a lot of awkward problems, but the [LSP specification] has methods that allow gopls to request that the client notify it of file system changes, specifically [`workspace/didChangeWatchedFiles`].
|
||||
This is currently being added to gopls by a community member, and tracked in [#31553]
|
||||
|
||||
[InitializeResult]: https://pkg.go.dev/golang.org/x/tools/gopls/internal/protocol#InitializeResult
|
||||
[ServerCapabilities]: https://pkg.go.dev/golang.org/x/tools/gopls/internal/protocol#ServerCapabilities
|
||||
[`golang.org/x/tools/gopls/internal/protocol`]: https://pkg.go.dev/golang.org/x/tools/internal/protocol#NewPoint
|
||||
|
||||
[LSP specification]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/
|
||||
[lsp-response]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#response-message
|
||||
[initialize]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#initialize
|
||||
[lsp-text-documents]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#text-documents
|
||||
[lsp-textedit]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textedit
|
||||
|
||||
[`textDocument/codeAction`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_codeAction
|
||||
[`textDocument/completion`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_completion
|
||||
[`textDocument/definition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_definition
|
||||
[`textDocument/typeDefinition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_typeDefinition
|
||||
[`textDocument/formatting`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_formatting
|
||||
[`textDocument/highlight`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_highlight
|
||||
[`textDocument/hover`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_hover
|
||||
[`textDocument/documentLink`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentLink
|
||||
[`textDocument/publishDiagnostics`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_publishDiagnostics
|
||||
[`textDocument/references`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_references
|
||||
[`textDocument/rename`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_rename
|
||||
[`textDocument/signatureHelp`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_signatureHelp
|
||||
[`textDocument/documentSymbols`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentSymbols
|
||||
[`workspace/didChangeWatchedFiles`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#workspace_didChangeWatchedFiles
|
||||
|
||||
[#31080]: https://github.com/golang/go/issues/31080
|
||||
[#31553]: https://github.com/golang/go/issues/31553
|
||||
[#31526]: https://github.com/golang/go/issues/31526
|
||||
@@ -0,0 +1,185 @@
|
||||
# Emacs
|
||||
|
||||
## Installing `gopls`
|
||||
|
||||
To use `gopls` with Emacs, you must first
|
||||
[install the `gopls` binary](../README.md#installation) and ensure that the directory
|
||||
containing the resulting binary (either `$(go env GOBIN)` or `$(go env
|
||||
GOPATH)/bin`) is in your `PATH`.
|
||||
|
||||
## Choosing an Emacs LSP client
|
||||
|
||||
To use `gopls` with Emacs, you will need to choose and install an Emacs LSP
|
||||
client package. Two popular client packages are [LSP Mode] and [Eglot].
|
||||
|
||||
LSP Mode takes a batteries-included approach, with many integrations enabled
|
||||
“out of the box” and several additional behaviors provided by `lsp-mode` itself.
|
||||
|
||||
Eglot takes a minimally-intrusive approach, focusing on smooth integration with
|
||||
other established packages. It provides a few of its own `eglot-` commands but
|
||||
no additional keybindings by default.
|
||||
|
||||
Once you have selected which client you want to use, install it per the packages
|
||||
instructions: see [Eglot 1-2-3](https://github.com/joaotavora/eglot#1-2-3) or
|
||||
[LSP Mode Installation](https://emacs-lsp.github.io/lsp-mode/page/installation/).
|
||||
|
||||
## Common configuration
|
||||
|
||||
Both Eglot and LSP Mode can integrate with popular packages in the Emacs
|
||||
ecosystem:
|
||||
|
||||
* The built-in [`xref`] package provides cross-references.
|
||||
* The built-in [Flymake] package provides an on-the-fly diagnostic overlay.
|
||||
* [Company] mode displays code completion candidates (with a richer UI than
|
||||
the built-in [`completion-at-point`]).
|
||||
|
||||
Eglot provides documentation using the built-in [ElDoc] minor mode, while LSP
|
||||
Mode by default provides documentation using its own [`lsp-ui`] mode.
|
||||
|
||||
Eglot by default locates the project root using the [`project`] package. In LSP
|
||||
Mode, this behavior can be configured using the `lsp-auto-guess-root` setting.
|
||||
|
||||
## Configuring LSP Mode
|
||||
|
||||
### Loading LSP Mode in `.emacs`
|
||||
|
||||
```elisp
|
||||
(require 'lsp-mode)
|
||||
(add-hook 'go-mode-hook #'lsp-deferred)
|
||||
|
||||
;; Set up before-save hooks to format buffer and add/delete imports.
|
||||
;; Make sure you don't have other gofmt/goimports hooks enabled.
|
||||
(defun lsp-go-install-save-hooks ()
|
||||
(add-hook 'before-save-hook #'lsp-format-buffer t t)
|
||||
(add-hook 'before-save-hook #'lsp-organize-imports t t))
|
||||
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)
|
||||
```
|
||||
|
||||
### Configuring `gopls` via LSP Mode
|
||||
|
||||
See [settings] for information about available gopls settings.
|
||||
|
||||
Stable gopls settings have corresponding configuration variables in `lsp-mode`.
|
||||
For example, `(setq lsp-gopls-use-placeholders nil)` will disable placeholders
|
||||
in completion snippets. See [`lsp-go`] for a list of available variables.
|
||||
|
||||
Experimental settings can be configured via `lsp-register-custom-settings`:
|
||||
|
||||
```lisp
|
||||
(lsp-register-custom-settings
|
||||
'(("gopls.completeUnimported" t t)
|
||||
("gopls.staticcheck" t t)))
|
||||
```
|
||||
|
||||
Note that after changing settings you must restart gopls using e.g. `M-x
|
||||
lsp-restart-workspace`.
|
||||
|
||||
## Configuring Eglot
|
||||
|
||||
### Configuring `project` for Go modules in `.emacs`
|
||||
|
||||
Eglot uses the built-in `project` package to identify the LSP workspace for a
|
||||
newly-opened buffer. The `project` package does not natively know about `GOPATH`
|
||||
or Go modules. Fortunately, you can give it a custom hook to tell it to look for
|
||||
the nearest parent `go.mod` file (that is, the root of the Go module) as the
|
||||
project root.
|
||||
|
||||
```elisp
|
||||
(require 'project)
|
||||
|
||||
(defun project-find-go-module (dir)
|
||||
(when-let ((root (locate-dominating-file dir "go.mod")))
|
||||
(cons 'go-module root)))
|
||||
|
||||
(cl-defmethod project-root ((project (head go-module)))
|
||||
(cdr project))
|
||||
|
||||
(add-hook 'project-find-functions #'project-find-go-module)
|
||||
```
|
||||
|
||||
### Loading Eglot in `.emacs`
|
||||
|
||||
```elisp
|
||||
;; Optional: load other packages before eglot to enable eglot integrations.
|
||||
(require 'company)
|
||||
(require 'yasnippet)
|
||||
|
||||
(require 'go-mode)
|
||||
(require 'eglot)
|
||||
(add-hook 'go-mode-hook 'eglot-ensure)
|
||||
|
||||
;; Optional: install eglot-format-buffer as a save hook.
|
||||
;; The depth of -10 places this before eglot's willSave notification,
|
||||
;; so that that notification reports the actual contents that will be saved.
|
||||
(defun eglot-format-buffer-before-save ()
|
||||
(add-hook 'before-save-hook #'eglot-format-buffer -10 t))
|
||||
(add-hook 'go-mode-hook #'eglot-format-buffer-before-save)
|
||||
```
|
||||
|
||||
### Configuring `gopls` via Eglot
|
||||
|
||||
See [settings] for information about available gopls settings.
|
||||
|
||||
LSP server settings are controlled by the `eglot-workspace-configuration`
|
||||
variable, which can be set either globally in `.emacs` or in a `.dir-locals.el` file in the project root.
|
||||
|
||||
`.emacs`:
|
||||
```elisp
|
||||
(setq-default eglot-workspace-configuration
|
||||
'((:gopls .
|
||||
((staticcheck . t)
|
||||
(matcher . "CaseSensitive")))))
|
||||
```
|
||||
|
||||
`.dir-locals.el`:
|
||||
```elisp
|
||||
((nil (eglot-workspace-configuration . ((gopls . ((staticcheck . t)
|
||||
(matcher . "CaseSensitive")))))))
|
||||
```
|
||||
|
||||
### Organizing imports with Eglot
|
||||
|
||||
`gopls` provides the import-organizing functionality of `goimports` as an LSP
|
||||
code action, which you can invoke as needed by running `M-x eglot-code-actions`
|
||||
(or a key of your choice bound to the `eglot-code-actions` function) and
|
||||
selecting `Organize Imports` at the prompt.
|
||||
|
||||
To automatically organize imports before saving, add a hook:
|
||||
|
||||
```elisp
|
||||
(add-hook 'before-save-hook
|
||||
(lambda ()
|
||||
(call-interactively 'eglot-code-action-organize-imports))
|
||||
nil t)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Common errors:
|
||||
|
||||
* When prompted by Emacs for your project folder, if you are using modules you
|
||||
must select the module's root folder (i.e. the directory with the "go.mod").
|
||||
If you are using GOPATH, select your $GOPATH as your folder.
|
||||
* Emacs must have your environment set properly (PATH, GOPATH, etc). You can
|
||||
run `M-x getenv <RET> PATH <RET>` to see if your PATH is set in Emacs. If
|
||||
not, you can try starting Emacs from your terminal, using [this
|
||||
package][exec-path-from-shell], or moving your shell config from `.bashrc`
|
||||
into `.profile` and logging out and back in.
|
||||
* Make sure only one LSP client mode is installed. (For example, if using
|
||||
`lsp-mode`, ensure that you are not _also_ enabling `eglot`.)
|
||||
* Look for errors in the `*lsp-log*` buffer or run `M-x eglot-events-buffer`.
|
||||
* Ask for help in the `#emacs` channel on the [Gophers slack].
|
||||
|
||||
[LSP Mode]: https://emacs-lsp.github.io/lsp-mode/
|
||||
[Eglot]: https://github.com/joaotavora/eglot/blob/master/README.md
|
||||
[`xref`]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html
|
||||
[Flymake]: https://www.gnu.org/software/emacs/manual/html_node/flymake/Using-Flymake.html#Using-Flymake
|
||||
[Company]: https://company-mode.github.io/
|
||||
[`completion-at-point`]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion-in-Buffers.html
|
||||
[ElDoc]: https://elpa.gnu.org/packages/eldoc.html
|
||||
[`lsp-ui`]: https://emacs-lsp.github.io/lsp-ui/
|
||||
[`lsp-go`]: https://github.com/emacs-lsp/lsp-mode/blob/master/clients/lsp-go.el
|
||||
[`use-package`]: https://github.com/jwiegley/use-package
|
||||
[`exec-path-from-shell`]: https://github.com/purcell/exec-path-from-shell
|
||||
[settings]: settings.md
|
||||
[Gophers slack]: https://invite.slack.golangbridge.org/
|
||||
@@ -0,0 +1,55 @@
|
||||
# Features
|
||||
|
||||
This document describes some of the features supported by `gopls`. It is
|
||||
currently under construction, so, for a comprehensive list, see the
|
||||
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
|
||||
|
||||
## Special features
|
||||
|
||||
Here, only special features outside of the LSP are described.
|
||||
|
||||
### Symbol Queries
|
||||
|
||||
Gopls supports some extended syntax for `workspace/symbol` requests, when using
|
||||
the `fuzzy` symbol matcher (the default). Inspired by the popular fuzzy matcher
|
||||
[FZF](https://github.com/junegunn/fzf), the following special characters are
|
||||
supported within symbol queries:
|
||||
|
||||
| Character | Usage | Match |
|
||||
| --------- | --------- | ------------ |
|
||||
| `'` | `'abc` | exact |
|
||||
| `^` | `^printf` | exact prefix |
|
||||
| `$` | `printf$` | exact suffix |
|
||||
|
||||
## Template Files
|
||||
|
||||
Gopls provides some support for Go template files, that is, files that
|
||||
are parsed by `text/template` or `html/template`.
|
||||
Gopls recognizes template files based on their file extension, which may be
|
||||
configured by the
|
||||
[`templateExtensions`](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#templateextensions) setting.
|
||||
Making this list empty turns off template support.
|
||||
|
||||
In template files, template support works inside
|
||||
the default `{{` delimiters. (Go template parsing
|
||||
allows the user to specify other delimiters, but
|
||||
gopls does not know how to do that.)
|
||||
|
||||
Gopls template support includes the following features:
|
||||
+ **Diagnostics**: if template parsing returns an error,
|
||||
it is presented as a diagnostic. (Missing functions do not produce errors.)
|
||||
+ **Syntax Highlighting**: syntax highlighting is provided for template files.
|
||||
+ **Definitions**: gopls provides jump-to-definition inside templates, though it does not understand scoping (all templates are considered to be in one global scope).
|
||||
+ **References**: gopls provides find-references, with the same scoping limitation as definitions.
|
||||
+ **Completions**: gopls will attempt to suggest completions inside templates.
|
||||
|
||||
### Configuring your editor
|
||||
|
||||
In addition to configuring `templateExtensions`, you may need to configure your
|
||||
editor or LSP client to activate `gopls` for template files. For example, in
|
||||
`VS Code` you will need to configure both
|
||||
[`files.associations`](https://code.visualstudio.com/docs/languages/identifiers)
|
||||
and `build.templateExtensions` (the gopls setting).
|
||||
|
||||
<!--TODO(rstambler): Automatically generate a list of supported features.-->
|
||||
|
||||
@@ -0,0 +1,917 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
// The generate command updates the following files of documentation:
|
||||
//
|
||||
// gopls/doc/settings.md -- from linking gopls/internal/settings.DefaultOptions
|
||||
// gopls/doc/commands.md -- from loading gopls/internal/protocol/command
|
||||
// gopls/doc/analyzers.md -- from linking gopls/internal/settings.DefaultAnalyzers
|
||||
// gopls/doc/inlayHints.md -- from linking gopls/internal/golang.AllInlayHints
|
||||
// gopls/internal/doc/api.json -- all of the above in a single value, for 'gopls api-json'
|
||||
//
|
||||
// Run it with this command:
|
||||
//
|
||||
// $ cd gopls/doc && go generate
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/tools/gopls/internal/cache"
|
||||
"golang.org/x/tools/gopls/internal/doc"
|
||||
"golang.org/x/tools/gopls/internal/golang"
|
||||
"golang.org/x/tools/gopls/internal/mod"
|
||||
"golang.org/x/tools/gopls/internal/protocol/command/commandmeta"
|
||||
"golang.org/x/tools/gopls/internal/settings"
|
||||
"golang.org/x/tools/gopls/internal/util/maps"
|
||||
"golang.org/x/tools/gopls/internal/util/safetoken"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if _, err := doMain(true); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Generation failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// doMain regenerates the output files. On success:
|
||||
// - if write, it updates them;
|
||||
// - if !write, it reports whether they would change.
|
||||
func doMain(write bool) (bool, error) {
|
||||
// TODO(adonovan): when we can rely on go1.23,
|
||||
// switch to gotypesalias=1 behavior.
|
||||
//
|
||||
// (Since this program is run by 'go run',
|
||||
// the gopls/go.mod file's go 1.19 directive doesn't
|
||||
// have its usual effect of setting gotypesalias=0.)
|
||||
os.Setenv("GODEBUG", "gotypesalias=0")
|
||||
|
||||
api, err := loadAPI()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
goplsDir, err := pkgDir("golang.org/x/tools/gopls")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// TODO(adonovan): consider using HTML, not Markdown, for the
|
||||
// generated reference documents. It's not more difficult, the
|
||||
// layout is easier to read, and we can use go/doc-comment
|
||||
// rendering logic.
|
||||
|
||||
for _, f := range []struct {
|
||||
name string // relative to gopls
|
||||
rewrite rewriter
|
||||
}{
|
||||
{"internal/doc/api.json", rewriteAPI},
|
||||
{"doc/settings.md", rewriteSettings},
|
||||
{"doc/codelenses.md", rewriteCodeLenses},
|
||||
{"doc/commands.md", rewriteCommands},
|
||||
{"doc/analyzers.md", rewriteAnalyzers},
|
||||
{"doc/inlayHints.md", rewriteInlayHints},
|
||||
} {
|
||||
file := filepath.Join(goplsDir, f.name)
|
||||
old, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
new, err := f.rewrite(old, api)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("rewriting %q: %v", file, err)
|
||||
}
|
||||
|
||||
if write {
|
||||
if err := os.WriteFile(file, new, 0); err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else if !bytes.Equal(old, new) {
|
||||
return false, nil // files would change
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// A rewriter is a function that transforms the content of a file.
|
||||
type rewriter = func([]byte, *doc.API) ([]byte, error)
|
||||
|
||||
// pkgDir returns the directory corresponding to the import path pkgPath.
|
||||
func pkgDir(pkgPath string) (string, error) {
|
||||
cmd := exec.Command("go", "list", "-f", "{{.Dir}}", pkgPath)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
if ee, _ := err.(*exec.ExitError); ee != nil && len(ee.Stderr) > 0 {
|
||||
return "", fmt.Errorf("%v: %w\n%s", cmd, err, ee.Stderr)
|
||||
}
|
||||
return "", fmt.Errorf("%v: %w", cmd, err)
|
||||
}
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
// loadAPI computes the JSON-encodable value that describes gopls'
|
||||
// interfaces, by a combination of static and dynamic analysis.
|
||||
func loadAPI() (*doc.API, error) {
|
||||
pkgs, err := packages.Load(
|
||||
&packages.Config{
|
||||
Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax | packages.NeedDeps,
|
||||
},
|
||||
"golang.org/x/tools/gopls/internal/settings",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
settingsPkg := pkgs[0]
|
||||
|
||||
defaults := settings.DefaultOptions()
|
||||
api := &doc.API{
|
||||
Options: map[string][]*doc.Option{},
|
||||
Analyzers: loadAnalyzers(settings.DefaultAnalyzers), // no staticcheck analyzers
|
||||
}
|
||||
|
||||
api.Commands, err = loadCommands()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
api.Lenses, err = loadLenses(settingsPkg, defaults.Codelenses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
api.Hints = loadHints(golang.AllInlayHints)
|
||||
for _, category := range []reflect.Value{
|
||||
reflect.ValueOf(defaults.UserOptions),
|
||||
} {
|
||||
// Find the type information and ast.File corresponding to the category.
|
||||
optsType := settingsPkg.Types.Scope().Lookup(category.Type().Name())
|
||||
if optsType == nil {
|
||||
return nil, fmt.Errorf("could not find %v in scope %v", category.Type().Name(), settingsPkg.Types.Scope())
|
||||
}
|
||||
opts, err := loadOptions(category, optsType, settingsPkg, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
catName := strings.TrimSuffix(category.Type().Name(), "Options")
|
||||
api.Options[catName] = opts
|
||||
|
||||
// Hardcode the expected values for the "analyses" and "hints" settings,
|
||||
// since their map keys are strings, not enums.
|
||||
for _, opt := range opts {
|
||||
switch opt.Name {
|
||||
case "analyses":
|
||||
for _, a := range api.Analyzers {
|
||||
opt.EnumKeys.Keys = append(opt.EnumKeys.Keys, doc.EnumKey{
|
||||
Name: fmt.Sprintf("%q", a.Name),
|
||||
Doc: a.Doc,
|
||||
Default: strconv.FormatBool(a.Default),
|
||||
})
|
||||
}
|
||||
case "hints":
|
||||
// TODO(adonovan): simplify InlayHints to use an enum,
|
||||
// following CodeLensSource.
|
||||
for _, a := range api.Hints {
|
||||
opt.EnumKeys.Keys = append(opt.EnumKeys.Keys, doc.EnumKey{
|
||||
Name: fmt.Sprintf("%q", a.Name),
|
||||
Doc: a.Doc,
|
||||
Default: strconv.FormatBool(a.Default),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return api, nil
|
||||
}
|
||||
|
||||
// loadOptions computes a single category of settings by a combination
|
||||
// of static analysis and reflection over gopls internal types.
|
||||
func loadOptions(category reflect.Value, optsType types.Object, pkg *packages.Package, hierarchy string) ([]*doc.Option, error) {
|
||||
file, err := fileForPos(pkg, optsType.Pos())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enums, err := loadEnums(pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var opts []*doc.Option
|
||||
optsStruct := optsType.Type().Underlying().(*types.Struct)
|
||||
for i := 0; i < optsStruct.NumFields(); i++ {
|
||||
// The types field gives us the type.
|
||||
typesField := optsStruct.Field(i)
|
||||
|
||||
// If the field name ends with "Options", assume it is a struct with
|
||||
// additional options and process it recursively.
|
||||
if h := strings.TrimSuffix(typesField.Name(), "Options"); h != typesField.Name() {
|
||||
// Keep track of the parent structs.
|
||||
if hierarchy != "" {
|
||||
h = hierarchy + "." + h
|
||||
}
|
||||
options, err := loadOptions(category, typesField, pkg, strings.ToLower(h))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, options...)
|
||||
continue
|
||||
}
|
||||
path, _ := astutil.PathEnclosingInterval(file, typesField.Pos(), typesField.Pos())
|
||||
if len(path) < 2 {
|
||||
return nil, fmt.Errorf("could not find AST node for field %v", typesField)
|
||||
}
|
||||
// The AST field gives us the doc.
|
||||
astField, ok := path[1].(*ast.Field)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected AST path %v", path)
|
||||
}
|
||||
|
||||
// The reflect field gives us the default value.
|
||||
reflectField := category.FieldByName(typesField.Name())
|
||||
if !reflectField.IsValid() {
|
||||
return nil, fmt.Errorf("could not find reflect field for %v", typesField.Name())
|
||||
}
|
||||
|
||||
def, err := formatDefault(reflectField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Derive the doc-and-api.json type from the Go field type.
|
||||
//
|
||||
// In principle, we should use JSON nomenclature here
|
||||
// (number, array, object, etc; see #68057), but in
|
||||
// practice we use the Go type string ([]T, map[K]V,
|
||||
// etc) with only one tweak: enumeration types are
|
||||
// replaced by "enum", including when they appear as
|
||||
// map keys.
|
||||
//
|
||||
// Notable edge cases:
|
||||
// - any (e.g. in linksInHover) is really a sum of false | true | "internal".
|
||||
// - time.Duration is really a string with a particular syntax.
|
||||
typ := typesField.Type().String()
|
||||
if _, ok := enums[typesField.Type()]; ok {
|
||||
typ = "enum"
|
||||
}
|
||||
name := lowerFirst(typesField.Name())
|
||||
|
||||
// enum-keyed maps
|
||||
var enumKeys doc.EnumKeys
|
||||
if m, ok := typesField.Type().Underlying().(*types.Map); ok {
|
||||
values, ok := enums[m.Key()]
|
||||
if ok {
|
||||
// Update type name: "map[CodeLensSource]T" -> "map[enum]T"
|
||||
// hack: assumes key substring is unique!
|
||||
typ = strings.Replace(typ, m.Key().String(), "enum", 1)
|
||||
}
|
||||
|
||||
// Edge case: "analyses" is a string (not enum) keyed map,
|
||||
// but its EnumKeys.ValueType was historically populated.
|
||||
// (But not "env"; not sure why.)
|
||||
if ok || name == "analyses" {
|
||||
enumKeys.ValueType = m.Elem().String()
|
||||
|
||||
// For map[enum]T fields, gather the set of valid
|
||||
// EnumKeys (from type information). If T=bool, also
|
||||
// record the default value (from reflection).
|
||||
keys, err := collectEnumKeys(m, reflectField, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enumKeys.Keys = keys
|
||||
}
|
||||
}
|
||||
|
||||
// Get the status of the field by checking its struct tags.
|
||||
reflectStructField, ok := category.Type().FieldByName(typesField.Name())
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no struct field for %s", typesField.Name())
|
||||
}
|
||||
status := reflectStructField.Tag.Get("status")
|
||||
|
||||
opts = append(opts, &doc.Option{
|
||||
Name: name,
|
||||
Type: typ,
|
||||
Doc: lowerFirst(astField.Doc.Text()),
|
||||
Default: def,
|
||||
EnumKeys: enumKeys,
|
||||
EnumValues: enums[typesField.Type()],
|
||||
Status: status,
|
||||
Hierarchy: hierarchy,
|
||||
})
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// loadEnums returns a description of gopls' settings enum types based on static analysis.
|
||||
func loadEnums(pkg *packages.Package) (map[types.Type][]doc.EnumValue, error) {
|
||||
enums := map[types.Type][]doc.EnumValue{}
|
||||
for _, name := range pkg.Types.Scope().Names() {
|
||||
obj := pkg.Types.Scope().Lookup(name)
|
||||
cnst, ok := obj.(*types.Const)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
f, err := fileForPos(pkg, cnst.Pos())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("finding file for %q: %v", cnst.Name(), err)
|
||||
}
|
||||
path, _ := astutil.PathEnclosingInterval(f, cnst.Pos(), cnst.Pos())
|
||||
spec := path[1].(*ast.ValueSpec)
|
||||
value := cnst.Val().ExactString()
|
||||
docstring := valueDoc(cnst.Name(), value, spec.Doc.Text())
|
||||
v := doc.EnumValue{
|
||||
Value: value,
|
||||
Doc: docstring,
|
||||
}
|
||||
enums[obj.Type()] = append(enums[obj.Type()], v)
|
||||
}
|
||||
return enums, nil
|
||||
}
|
||||
|
||||
func collectEnumKeys(m *types.Map, reflectField reflect.Value, enumValues []doc.EnumValue) ([]doc.EnumKey, error) {
|
||||
// We can get default values for enum -> bool maps.
|
||||
var isEnumBoolMap bool
|
||||
if basic, ok := m.Elem().Underlying().(*types.Basic); ok && basic.Kind() == types.Bool {
|
||||
isEnumBoolMap = true
|
||||
}
|
||||
var keys []doc.EnumKey
|
||||
for _, v := range enumValues {
|
||||
var def string
|
||||
if isEnumBoolMap {
|
||||
var err error
|
||||
def, err = formatDefaultFromEnumBoolMap(reflectField, v.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
keys = append(keys, doc.EnumKey{
|
||||
Name: v.Value,
|
||||
Doc: v.Doc,
|
||||
Default: def,
|
||||
})
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func formatDefaultFromEnumBoolMap(reflectMap reflect.Value, enumKey string) (string, error) {
|
||||
if reflectMap.Kind() != reflect.Map {
|
||||
return "", nil
|
||||
}
|
||||
name := enumKey
|
||||
if unquoted, err := strconv.Unquote(name); err == nil {
|
||||
name = unquoted
|
||||
}
|
||||
for _, e := range reflectMap.MapKeys() {
|
||||
if e.String() == name {
|
||||
value := reflectMap.MapIndex(e)
|
||||
if value.Type().Kind() == reflect.Bool {
|
||||
return formatDefault(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Assume that if the value isn't mentioned in the map, it defaults to
|
||||
// the default value, false.
|
||||
return formatDefault(reflect.ValueOf(false))
|
||||
}
|
||||
|
||||
// formatDefault formats the default value into a JSON-like string.
|
||||
// VS Code exposes settings as JSON, so showing them as JSON is reasonable.
|
||||
// TODO(rstambler): Reconsider this approach, as the VS Code Go generator now
|
||||
// marshals to JSON.
|
||||
func formatDefault(reflectField reflect.Value) (string, error) {
|
||||
def := reflectField.Interface()
|
||||
|
||||
// Durations marshal as nanoseconds, but we want the stringy versions,
|
||||
// e.g. "100ms".
|
||||
if t, ok := def.(time.Duration); ok {
|
||||
def = t.String()
|
||||
}
|
||||
defBytes, err := json.Marshal(def)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Nil values format as "null" so print them as hardcoded empty values.
|
||||
switch reflectField.Type().Kind() {
|
||||
case reflect.Map:
|
||||
if reflectField.IsNil() {
|
||||
defBytes = []byte("{}")
|
||||
}
|
||||
case reflect.Slice:
|
||||
if reflectField.IsNil() {
|
||||
defBytes = []byte("[]")
|
||||
}
|
||||
}
|
||||
return string(defBytes), err
|
||||
}
|
||||
|
||||
// valueDoc transforms a docstring documenting an constant identifier to a
|
||||
// docstring documenting its value.
|
||||
//
|
||||
// If doc is of the form "Foo is a bar", it returns '`"fooValue"` is a bar'. If
|
||||
// doc is non-standard ("this value is a bar"), it returns '`"fooValue"`: this
|
||||
// value is a bar'.
|
||||
func valueDoc(name, value, doc string) string {
|
||||
if doc == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.HasPrefix(doc, name) {
|
||||
// docstring in standard form. Replace the subject with value.
|
||||
return fmt.Sprintf("`%s`%s", value, doc[len(name):])
|
||||
}
|
||||
return fmt.Sprintf("`%s`: %s", value, doc)
|
||||
}
|
||||
|
||||
func loadCommands() ([]*doc.Command, error) {
|
||||
var commands []*doc.Command
|
||||
|
||||
cmds, err := commandmeta.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Parse the objects it contains.
|
||||
for _, cmd := range cmds {
|
||||
cmdjson := &doc.Command{
|
||||
Command: cmd.Name,
|
||||
Title: cmd.Title,
|
||||
Doc: cmd.Doc,
|
||||
ArgDoc: argsDoc(cmd.Args),
|
||||
}
|
||||
if cmd.Result != nil {
|
||||
cmdjson.ResultDoc = typeDoc(cmd.Result, 0)
|
||||
}
|
||||
commands = append(commands, cmdjson)
|
||||
}
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func argsDoc(args []*commandmeta.Field) string {
|
||||
var b strings.Builder
|
||||
for i, arg := range args {
|
||||
b.WriteString(typeDoc(arg, 0))
|
||||
if i != len(args)-1 {
|
||||
b.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func typeDoc(arg *commandmeta.Field, level int) string {
|
||||
// Max level to expand struct fields.
|
||||
const maxLevel = 3
|
||||
if len(arg.Fields) > 0 {
|
||||
if level < maxLevel {
|
||||
return arg.FieldMod + structDoc(arg.Fields, level)
|
||||
}
|
||||
return "{ ... }"
|
||||
}
|
||||
under := arg.Type.Underlying()
|
||||
switch u := under.(type) {
|
||||
case *types.Slice:
|
||||
return fmt.Sprintf("[]%s", u.Elem().Underlying().String())
|
||||
}
|
||||
// TODO(adonovan): use (*types.Package).Name qualifier.
|
||||
return types.TypeString(under, nil)
|
||||
}
|
||||
|
||||
// TODO(adonovan): this format is strange; it's not Go, nor JSON, nor LSP. Rethink.
|
||||
func structDoc(fields []*commandmeta.Field, level int) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("{\n")
|
||||
indent := strings.Repeat("\t", level)
|
||||
for _, fld := range fields {
|
||||
if fld.Doc != "" && level == 0 {
|
||||
doclines := strings.Split(fld.Doc, "\n")
|
||||
for _, line := range doclines {
|
||||
text := ""
|
||||
if line != "" {
|
||||
text = " " + line
|
||||
}
|
||||
fmt.Fprintf(&b, "%s\t//%s\n", indent, text)
|
||||
}
|
||||
}
|
||||
tag := strings.Split(fld.JSONTag, ",")[0]
|
||||
if tag == "" {
|
||||
tag = fld.Name
|
||||
}
|
||||
fmt.Fprintf(&b, "%s\t%q: %s,\n", indent, tag, typeDoc(fld, level+1))
|
||||
}
|
||||
fmt.Fprintf(&b, "%s}", indent)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// loadLenses combines the syntactic comments from the settings
|
||||
// package with the default values from settings.DefaultOptions(), and
|
||||
// returns a list of Code Lens descriptors.
|
||||
func loadLenses(settingsPkg *packages.Package, defaults map[settings.CodeLensSource]bool) ([]*doc.Lens, error) {
|
||||
// Find the CodeLensSource enums among the files of the protocol package.
|
||||
// Map each enum value to its doc comment.
|
||||
enumDoc := make(map[string]string)
|
||||
for _, f := range settingsPkg.Syntax {
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.CONST {
|
||||
for _, spec := range decl.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
posn := safetoken.StartPosition(settingsPkg.Fset, spec.Pos())
|
||||
if id, ok := spec.Type.(*ast.Ident); ok && id.Name == "CodeLensSource" {
|
||||
if len(spec.Names) != 1 || len(spec.Values) != 1 {
|
||||
return nil, fmt.Errorf("%s: declare one CodeLensSource per line", posn)
|
||||
}
|
||||
lit, ok := spec.Values[0].(*ast.BasicLit)
|
||||
if !ok && lit.Kind != token.STRING {
|
||||
return nil, fmt.Errorf("%s: CodeLensSource value is not a string literal", posn)
|
||||
}
|
||||
value, _ := strconv.Unquote(lit.Value) // ignore error: AST is well-formed
|
||||
if spec.Doc == nil {
|
||||
return nil, fmt.Errorf("%s: %s lacks doc comment", posn, spec.Names[0].Name)
|
||||
}
|
||||
enumDoc[value] = spec.Doc.Text()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(enumDoc) == 0 {
|
||||
return nil, fmt.Errorf("failed to extract any CodeLensSource declarations")
|
||||
}
|
||||
|
||||
// Build list of Lens descriptors.
|
||||
var lenses []*doc.Lens
|
||||
addAll := func(sources map[settings.CodeLensSource]cache.CodeLensSourceFunc, fileType string) error {
|
||||
slice := maps.Keys(sources)
|
||||
sort.Slice(slice, func(i, j int) bool { return slice[i] < slice[j] })
|
||||
for _, source := range slice {
|
||||
docText, ok := enumDoc[string(source)]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing CodeLensSource declaration for %s", source)
|
||||
}
|
||||
title, docText, _ := strings.Cut(docText, "\n") // first line is title
|
||||
lenses = append(lenses, &doc.Lens{
|
||||
FileType: fileType,
|
||||
Lens: string(source),
|
||||
Title: title,
|
||||
Doc: docText,
|
||||
Default: defaults[source],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
addAll(golang.CodeLensSources(), "Go")
|
||||
addAll(mod.CodeLensSources(), "go.mod")
|
||||
return lenses, nil
|
||||
}
|
||||
|
||||
func loadAnalyzers(m map[string]*settings.Analyzer) []*doc.Analyzer {
|
||||
var sorted []string
|
||||
for _, a := range m {
|
||||
sorted = append(sorted, a.Analyzer().Name)
|
||||
}
|
||||
sort.Strings(sorted)
|
||||
var json []*doc.Analyzer
|
||||
for _, name := range sorted {
|
||||
a := m[name]
|
||||
json = append(json, &doc.Analyzer{
|
||||
Name: a.Analyzer().Name,
|
||||
Doc: a.Analyzer().Doc,
|
||||
URL: a.Analyzer().URL,
|
||||
Default: a.EnabledByDefault(),
|
||||
})
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
func loadHints(m map[string]*golang.Hint) []*doc.Hint {
|
||||
var sorted []string
|
||||
for _, h := range m {
|
||||
sorted = append(sorted, h.Name)
|
||||
}
|
||||
sort.Strings(sorted)
|
||||
var json []*doc.Hint
|
||||
for _, name := range sorted {
|
||||
h := m[name]
|
||||
json = append(json, &doc.Hint{
|
||||
Name: h.Name,
|
||||
Doc: h.Doc,
|
||||
})
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
func lowerFirst(x string) string {
|
||||
if x == "" {
|
||||
return x
|
||||
}
|
||||
return strings.ToLower(x[:1]) + x[1:]
|
||||
}
|
||||
|
||||
func upperFirst(x string) string {
|
||||
if x == "" {
|
||||
return x
|
||||
}
|
||||
return strings.ToUpper(x[:1]) + x[1:]
|
||||
}
|
||||
|
||||
func fileForPos(pkg *packages.Package, pos token.Pos) (*ast.File, error) {
|
||||
fset := pkg.Fset
|
||||
for _, f := range pkg.Syntax {
|
||||
if safetoken.StartPosition(fset, f.Pos()).Filename == safetoken.StartPosition(fset, pos).Filename {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no file for pos %v", pos)
|
||||
}
|
||||
|
||||
func rewriteAPI(_ []byte, api *doc.API) ([]byte, error) {
|
||||
return json.MarshalIndent(api, "", "\t")
|
||||
}
|
||||
|
||||
type optionsGroup struct {
|
||||
title string // dotted path (e.g. "ui.documentation")
|
||||
final string // final segment of title (e.g. "documentation")
|
||||
level int
|
||||
options []*doc.Option
|
||||
}
|
||||
|
||||
func rewriteSettings(prevContent []byte, api *doc.API) ([]byte, error) {
|
||||
content := prevContent
|
||||
for category, opts := range api.Options {
|
||||
groups := collectGroups(opts)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
// First, print a table of contents (ToC).
|
||||
fmt.Fprintln(&buf)
|
||||
for _, h := range groups {
|
||||
title := h.final
|
||||
if title != "" {
|
||||
fmt.Fprintf(&buf, "%s* [%s](#%s)\n",
|
||||
strings.Repeat(" ", h.level),
|
||||
capitalize(title),
|
||||
strings.ToLower(title))
|
||||
}
|
||||
}
|
||||
|
||||
// Section titles are h2, options are h3.
|
||||
// This is independent of the option hierarchy.
|
||||
// (Nested options should not be smaller!)
|
||||
fmt.Fprintln(&buf)
|
||||
for _, h := range groups {
|
||||
title := h.final
|
||||
if title != "" {
|
||||
// Emit HTML anchor as GitHub markdown doesn't support
|
||||
// "# Heading {#anchor}" syntax.
|
||||
fmt.Fprintf(&buf, "<a id='%s'></a>\n", strings.ToLower(title))
|
||||
|
||||
fmt.Fprintf(&buf, "## %s\n\n", capitalize(title))
|
||||
}
|
||||
for _, opt := range h.options {
|
||||
// Emit HTML anchor as GitHub markdown doesn't support
|
||||
// "# Heading {#anchor}" syntax.
|
||||
//
|
||||
// (Each option name is the camelCased name of a field of
|
||||
// settings.UserOptions or one of its FooOptions subfields.)
|
||||
fmt.Fprintf(&buf, "<a id='%s'></a>\n", opt.Name)
|
||||
|
||||
// heading
|
||||
//
|
||||
// TODO(adonovan): We should display not the Go type (e.g.
|
||||
// `time.Duration`, `map[Enum]bool`) for each setting,
|
||||
// but its JSON type, since that's the actual interface.
|
||||
// We need a better way to derive accurate JSON type descriptions
|
||||
// from Go types. eg. "a string parsed as if by
|
||||
// `time.Duration.Parse`". (`time.Duration` is an integer, not
|
||||
// a string!)
|
||||
//
|
||||
// We do not display the undocumented dotted-path alias
|
||||
// (h.title + "." + opt.Name) used by VS Code only.
|
||||
fmt.Fprintf(&buf, "### `%s` *%v*\n\n", opt.Name, opt.Type)
|
||||
|
||||
// status
|
||||
switch opt.Status {
|
||||
case "":
|
||||
case "advanced":
|
||||
fmt.Fprint(&buf, "**This is an advanced setting and should not be configured by most `gopls` users.**\n\n")
|
||||
case "debug":
|
||||
fmt.Fprint(&buf, "**This setting is for debugging purposes only.**\n\n")
|
||||
case "experimental":
|
||||
fmt.Fprint(&buf, "**This setting is experimental and may be deleted.**\n\n")
|
||||
default:
|
||||
fmt.Fprintf(&buf, "**Status: %s.**\n\n", opt.Status)
|
||||
}
|
||||
|
||||
// doc comment
|
||||
buf.WriteString(opt.Doc)
|
||||
|
||||
// enums
|
||||
write := func(name, doc string) {
|
||||
if doc != "" {
|
||||
unbroken := parBreakRE.ReplaceAllString(doc, "\\\n")
|
||||
fmt.Fprintf(&buf, "* %s\n", strings.TrimSpace(unbroken))
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "* `%s`\n", name)
|
||||
}
|
||||
}
|
||||
if len(opt.EnumValues) > 0 && opt.Type == "enum" {
|
||||
// enum as top-level type constructor
|
||||
buf.WriteString("\nMust be one of:\n\n")
|
||||
for _, val := range opt.EnumValues {
|
||||
write(val.Value, val.Doc)
|
||||
}
|
||||
} else if len(opt.EnumKeys.Keys) > 0 && shouldShowEnumKeysInSettings(opt.Name) {
|
||||
// enum as map key (currently just "annotations")
|
||||
buf.WriteString("\nEach enum must be one of:\n\n")
|
||||
for _, val := range opt.EnumKeys.Keys {
|
||||
write(val.Name, val.Doc)
|
||||
}
|
||||
}
|
||||
|
||||
// default value
|
||||
fmt.Fprintf(&buf, "\nDefault: `%v`.\n\n", opt.Default)
|
||||
}
|
||||
}
|
||||
newContent, err := replaceSection(content, category, buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content = newContent
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
var parBreakRE = regexp.MustCompile("\n{2,}")
|
||||
|
||||
func shouldShowEnumKeysInSettings(name string) bool {
|
||||
// These fields have too many possible options,
|
||||
// or too voluminous documentation, to render as enums.
|
||||
// Instead they each get their own page in the manual.
|
||||
return !(name == "analyses" || name == "codelenses" || name == "hints")
|
||||
}
|
||||
|
||||
func collectGroups(opts []*doc.Option) []optionsGroup {
|
||||
optsByHierarchy := map[string][]*doc.Option{}
|
||||
for _, opt := range opts {
|
||||
optsByHierarchy[opt.Hierarchy] = append(optsByHierarchy[opt.Hierarchy], opt)
|
||||
}
|
||||
|
||||
// As a hack, assume that uncategorized items are less important to
|
||||
// users and force the empty string to the end of the list.
|
||||
var containsEmpty bool
|
||||
var sorted []string
|
||||
for h := range optsByHierarchy {
|
||||
if h == "" {
|
||||
containsEmpty = true
|
||||
continue
|
||||
}
|
||||
sorted = append(sorted, h)
|
||||
}
|
||||
sort.Strings(sorted)
|
||||
if containsEmpty {
|
||||
sorted = append(sorted, "")
|
||||
}
|
||||
var groups []optionsGroup
|
||||
baseLevel := 0
|
||||
for _, h := range sorted {
|
||||
split := strings.SplitAfter(h, ".")
|
||||
last := split[len(split)-1]
|
||||
// Hack to capitalize all of UI.
|
||||
if last == "ui" {
|
||||
last = "UI"
|
||||
}
|
||||
// A hierarchy may look like "ui.formatting". If "ui" has no
|
||||
// options of its own, it may not be added to the map, but it
|
||||
// still needs a heading.
|
||||
components := strings.Split(h, ".")
|
||||
for i := 1; i < len(components); i++ {
|
||||
parent := strings.Join(components[0:i], ".")
|
||||
if _, ok := optsByHierarchy[parent]; !ok {
|
||||
groups = append(groups, optionsGroup{
|
||||
title: parent,
|
||||
final: last,
|
||||
level: baseLevel + i,
|
||||
})
|
||||
}
|
||||
}
|
||||
groups = append(groups, optionsGroup{
|
||||
title: h,
|
||||
final: last,
|
||||
level: baseLevel + strings.Count(h, "."),
|
||||
options: optsByHierarchy[h],
|
||||
})
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
func capitalize(s string) string {
|
||||
return string(unicode.ToUpper(rune(s[0]))) + s[1:]
|
||||
}
|
||||
|
||||
func rewriteCodeLenses(prevContent []byte, api *doc.API) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, lens := range api.Lenses {
|
||||
fmt.Fprintf(&buf, "## `%s`: %s\n\n", lens.Lens, lens.Title)
|
||||
fmt.Fprintf(&buf, "%s\n\n", lens.Doc)
|
||||
fmt.Fprintf(&buf, "Default: %v\n\n", onOff(lens.Default))
|
||||
fmt.Fprintf(&buf, "File type: %s\n\n", lens.FileType)
|
||||
}
|
||||
return replaceSection(prevContent, "Lenses", buf.Bytes())
|
||||
}
|
||||
|
||||
func rewriteCommands(prevContent []byte, api *doc.API) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, command := range api.Commands {
|
||||
fmt.Fprintf(&buf, "## `%s`: **%s**\n\n%v\n\n", command.Command, command.Title, command.Doc)
|
||||
if command.ArgDoc != "" {
|
||||
fmt.Fprintf(&buf, "Args:\n\n```\n%s\n```\n\n", command.ArgDoc)
|
||||
}
|
||||
if command.ResultDoc != "" {
|
||||
fmt.Fprintf(&buf, "Result:\n\n```\n%s\n```\n\n", command.ResultDoc)
|
||||
}
|
||||
}
|
||||
return replaceSection(prevContent, "Commands", buf.Bytes())
|
||||
}
|
||||
|
||||
func rewriteAnalyzers(prevContent []byte, api *doc.API) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, analyzer := range api.Analyzers {
|
||||
fmt.Fprintf(&buf, "<a id='%s'></a>\n", analyzer.Name)
|
||||
title, doc, _ := strings.Cut(analyzer.Doc, "\n")
|
||||
title = strings.TrimPrefix(title, analyzer.Name+": ")
|
||||
fmt.Fprintf(&buf, "## `%s`: %s\n\n", analyzer.Name, title)
|
||||
fmt.Fprintf(&buf, "%s\n\n", doc)
|
||||
fmt.Fprintf(&buf, "Default: %s.", onOff(analyzer.Default))
|
||||
if !analyzer.Default {
|
||||
fmt.Fprintf(&buf, " Enable by setting `\"analyses\": {\"%s\": true}`.", analyzer.Name)
|
||||
}
|
||||
fmt.Fprintf(&buf, "\n\n")
|
||||
if analyzer.URL != "" {
|
||||
// TODO(adonovan): currently the URL provides the same information
|
||||
// as 'doc' above, though that may change due to
|
||||
// https://github.com/golang/go/issues/61315#issuecomment-1841350181.
|
||||
// In that case, update this to something like "Complete documentation".
|
||||
fmt.Fprintf(&buf, "Package documentation: [%s](%s)\n\n",
|
||||
analyzer.Name, analyzer.URL)
|
||||
}
|
||||
|
||||
}
|
||||
return replaceSection(prevContent, "Analyzers", buf.Bytes())
|
||||
}
|
||||
|
||||
func rewriteInlayHints(prevContent []byte, api *doc.API) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, hint := range api.Hints {
|
||||
fmt.Fprintf(&buf, "## **%v**\n\n", hint.Name)
|
||||
fmt.Fprintf(&buf, "%s\n\n", hint.Doc)
|
||||
switch hint.Default {
|
||||
case true:
|
||||
fmt.Fprintf(&buf, "**Enabled by default.**\n\n")
|
||||
case false:
|
||||
fmt.Fprintf(&buf, "**Disabled by default. Enable it by setting `\"hints\": {\"%s\": true}`.**\n\n", hint.Name)
|
||||
}
|
||||
}
|
||||
return replaceSection(prevContent, "Hints", buf.Bytes())
|
||||
}
|
||||
|
||||
// replaceSection replaces the portion of a file delimited by comments of the form:
|
||||
//
|
||||
// <!-- BEGIN sectionName -->
|
||||
// <!-- END section Name -->
|
||||
func replaceSection(content []byte, sectionName string, replacement []byte) ([]byte, error) {
|
||||
re := regexp.MustCompile(fmt.Sprintf(`(?s)<!-- BEGIN %v.* -->\n(.*?)<!-- END %v.* -->`, sectionName, sectionName))
|
||||
idx := re.FindSubmatchIndex(content)
|
||||
if idx == nil {
|
||||
return nil, fmt.Errorf("could not find section %q", sectionName)
|
||||
}
|
||||
result := append([]byte(nil), content[:idx[2]]...)
|
||||
result = append(result, replacement...)
|
||||
result = append(result, content[idx[3]:]...)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type onOff bool
|
||||
|
||||
func (o onOff) String() string {
|
||||
if o {
|
||||
return "on"
|
||||
} else {
|
||||
return "off"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2020 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 main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
)
|
||||
|
||||
func TestGenerated(t *testing.T) {
|
||||
testenv.NeedsGoPackages(t)
|
||||
// This test fails on Kokoro, for unknown reasons, so must be run only on TryBots.
|
||||
// In any case, it suffices to run this test on any builder.
|
||||
testenv.NeedsGo1Point(t, 21)
|
||||
|
||||
testenv.NeedsLocalXTools(t)
|
||||
|
||||
ok, err := doMain(false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !ok {
|
||||
t.Error("documentation needs updating. Run: cd gopls && go generate ./...")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# Helix
|
||||
|
||||
Configuring `gopls` to work with Helix is rather straightforward. Install `gopls`, and then add it to the `PATH` variable. If it is in the `PATH` variable, Helix will be able to detect it automatically.
|
||||
|
||||
The documentation explaining how to install the default language servers for Helix can be found [here](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers)
|
||||
|
||||
## Installing `gopls`
|
||||
|
||||
The first step is to install `gopls` on your machine.
|
||||
You can follow installation instructions [here](https://github.com/golang/tools/tree/master/gopls#installation).
|
||||
|
||||
## Setting your path to include `gopls`
|
||||
|
||||
Set your `PATH` environment variable to point to `gopls`.
|
||||
If you used `go install` to download `gopls`, it should be in `$GOPATH/bin`.
|
||||
If you don't have `GOPATH` set, you can use `go env GOPATH` to find it.
|
||||
|
||||
## Additional information
|
||||
|
||||
You can find more information about how to set up the LSP formatter [here](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers#autoformatting).
|
||||
|
||||
It is possible to use `hx --health go` to see that the language server is properly set up.
|
||||
|
||||
### Configuration
|
||||
|
||||
The settings for `gopls` can be configured in the `languages.toml` file.
|
||||
The official Helix documentation for this can be found [here](https://docs.helix-editor.com/languages.html)
|
||||
|
||||
Configuration pertaining to `gopls` should be in the table `language-server.gopls`.
|
||||
|
||||
#### How to set flags
|
||||
|
||||
To set flags, add them to the `args` array in the `language-server.gopls` section of the `languages.toml` file.
|
||||
|
||||
#### How to set LSP configuration
|
||||
|
||||
Configuration options can be set in the `language-server.gopls.config` section of the `languages.toml` file, or in the `config` key of the `language-server.gopls` section of the `languages.toml` file.
|
||||
|
||||
#### A minimal config example
|
||||
|
||||
In the `~/.config/helix/languages.toml` file, the following snippet would set up `gopls` with a logfile located at `/tmp/gopls.log` and enable staticcheck.
|
||||
|
||||
```toml
|
||||
[language-server.gopls]
|
||||
command = "gopls"
|
||||
args = ["-logfile=/tmp/gopls.log", "serve"]
|
||||
[language-server.gopls.config]
|
||||
"ui.diagnostic.staticcheck" = true
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Hints
|
||||
|
||||
This document describes the inlay hints that `gopls` uses inside the editor.
|
||||
|
||||
<!-- BEGIN Hints: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
## **assignVariableTypes**
|
||||
|
||||
Enable/disable inlay hints for variable types in assign statements:
|
||||
```go
|
||||
i/* int*/, j/* int*/ := 0, len(r)-1
|
||||
```
|
||||
|
||||
**Disabled by default. Enable it by setting `"hints": {"assignVariableTypes": true}`.**
|
||||
|
||||
## **compositeLiteralFields**
|
||||
|
||||
Enable/disable inlay hints for composite literal field names:
|
||||
```go
|
||||
{/*in: */"Hello, world", /*want: */"dlrow ,olleH"}
|
||||
```
|
||||
|
||||
**Disabled by default. Enable it by setting `"hints": {"compositeLiteralFields": true}`.**
|
||||
|
||||
## **compositeLiteralTypes**
|
||||
|
||||
Enable/disable inlay hints for composite literal types:
|
||||
```go
|
||||
for _, c := range []struct {
|
||||
in, want string
|
||||
}{
|
||||
/*struct{ in string; want string }*/{"Hello, world", "dlrow ,olleH"},
|
||||
}
|
||||
```
|
||||
|
||||
**Disabled by default. Enable it by setting `"hints": {"compositeLiteralTypes": true}`.**
|
||||
|
||||
## **constantValues**
|
||||
|
||||
Enable/disable inlay hints for constant values:
|
||||
```go
|
||||
const (
|
||||
KindNone Kind = iota/* = 0*/
|
||||
KindPrint/* = 1*/
|
||||
KindPrintf/* = 2*/
|
||||
KindErrorf/* = 3*/
|
||||
)
|
||||
```
|
||||
|
||||
**Disabled by default. Enable it by setting `"hints": {"constantValues": true}`.**
|
||||
|
||||
## **functionTypeParameters**
|
||||
|
||||
Enable/disable inlay hints for implicit type parameters on generic functions:
|
||||
```go
|
||||
myFoo/*[int, string]*/(1, "hello")
|
||||
```
|
||||
|
||||
**Disabled by default. Enable it by setting `"hints": {"functionTypeParameters": true}`.**
|
||||
|
||||
## **parameterNames**
|
||||
|
||||
Enable/disable inlay hints for parameter names:
|
||||
```go
|
||||
parseInt(/* str: */ "123", /* radix: */ 8)
|
||||
```
|
||||
|
||||
**Disabled by default. Enable it by setting `"hints": {"parameterNames": true}`.**
|
||||
|
||||
## **rangeVariableTypes**
|
||||
|
||||
Enable/disable inlay hints for variable types in range statements:
|
||||
```go
|
||||
for k/* int*/, v/* string*/ := range []string{} {
|
||||
fmt.Println(k, v)
|
||||
}
|
||||
```
|
||||
|
||||
**Disabled by default. Enable it by setting `"hints": {"rangeVariableTypes": true}`.**
|
||||
|
||||
<!-- END Hints: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,161 @@
|
||||
|
||||
Gopls v0.14 supports a new refactoring operation:
|
||||
inlining of function calls.
|
||||
|
||||
You can find it in VS Code by selecting a static call to a function or
|
||||
method f and choosing the `Refactor...` command followed by `Inline
|
||||
call to f`.
|
||||
Other editors and LSP clients have their own idiomatic command for it;
|
||||
for example, in Emacs with Eglot it is
|
||||
[`M-x eglot-code-action-inline`](https://joaotavora.github.io/eglot/#index-M_002dx-eglot_002dcode_002daction_002dinline)
|
||||
and in Vim with coc.nvim it is `coc-rename`.
|
||||
|
||||
<!-- source code used for images:
|
||||
|
||||
func six() int {
|
||||
return sum(1, 2, 3)
|
||||
}
|
||||
|
||||
func sum(values ...int) int {
|
||||
total := 0
|
||||
for _, v := range values {
|
||||
total += v
|
||||
}
|
||||
return total
|
||||
}
|
||||
-->
|
||||

|
||||

|
||||
|
||||
Inlining replaces the call expression by a copy of the function body,
|
||||
with parameters replaced by arguments.
|
||||
Inlining is useful for a number of reasons.
|
||||
Perhaps you want to eliminate a call to a deprecated
|
||||
function such as `ioutil.ReadFile` by replacing it with a call to the
|
||||
newer `os.ReadFile`; inlining will do that for you.
|
||||
Or perhaps you want to copy and modify an existing function in some
|
||||
way; inlining can provide a starting point.
|
||||
The inlining logic also provides a building block for
|
||||
other refactorings to come, such as "change signature".
|
||||
|
||||
Not every call can be inlined.
|
||||
Of course, the tool needs to know which function is being called, so
|
||||
you can't inline a dynamic call through a function value or interface
|
||||
method; but static calls to methods are fine.
|
||||
Nor can you inline a call if the callee is declared in another package
|
||||
and refers to non-exported parts of that package, or to [internal
|
||||
packages](https://go.dev/doc/go1.4#internalpackages) that are
|
||||
inaccessible to the caller.
|
||||
|
||||
When inlining is possible, it's critical that the tool preserve
|
||||
the original behavior of the program.
|
||||
We don't want refactoring to break the build, or, worse, to introduce
|
||||
subtle latent bugs.
|
||||
This is especially important when inlining tools are used to perform
|
||||
automated clean-ups in large code bases.
|
||||
We must be able to trust the tool.
|
||||
Our inliner is very careful not to make guesses or unsound
|
||||
assumptions about the behavior of the code.
|
||||
However, that does mean it sometimes produces a change that differs
|
||||
from what someone with expert knowledge of the same code might have
|
||||
written by hand.
|
||||
|
||||
In the most difficult cases, especially with complex control flow, it
|
||||
may not be safe to eliminate the function call at all.
|
||||
For example, the behavior of a `defer` statement is intimately tied to
|
||||
its enclosing function call, and `defer` is the only control
|
||||
construct that can be used to handle panics, so it cannot be reduced
|
||||
into simpler constructs.
|
||||
So, for example, given a function f defined as:
|
||||
|
||||
```go
|
||||
func f(s string) {
|
||||
defer fmt.Println("goodbye")
|
||||
fmt.Println(s)
|
||||
}
|
||||
```
|
||||
a call `f("hello")` will be inlined to:
|
||||
```go
|
||||
func() {
|
||||
defer fmt.Println("goodbye")
|
||||
fmt.Println("hello")
|
||||
}()
|
||||
```
|
||||
Although the parameter was eliminated, the function call remains.
|
||||
|
||||
An inliner is a bit like an optimizing compiler.
|
||||
A compiler is considered "correct" if it doesn't change the meaning of
|
||||
the program in translation from source language to target language.
|
||||
An _optimizing_ compiler exploits the particulars of the input to
|
||||
generate better code, where "better" usually means more efficient.
|
||||
As users report inputs that cause the compiler to emit suboptimal
|
||||
code, the compiler is improved to recognize more cases, or more rules,
|
||||
and more exceptions to rules---but this process has no end.
|
||||
Inlining is similar, except that "better" code means tidier code.
|
||||
The most conservative translation provides a simple but (hopefully!)
|
||||
correct foundation, on top of which endless rules, and exceptions to
|
||||
rules, can embellish and improve the quality of the output.
|
||||
|
||||
The following section lists some of the technical
|
||||
challenges involved in sound inlining:
|
||||
|
||||
- **Effects:** When replacing a parameter by its argument expression,
|
||||
we must be careful not to change the effects of the call. For
|
||||
example, if we call a function `func twice(x int) int { return x + x }`
|
||||
with `twice(g())`, we do not want to see `g() + g()`, which would
|
||||
cause g's effects to occur twice, and potentially each call might
|
||||
return a different value. All effects must occur the same number of
|
||||
times, and in the same order. This requires analyzing both the
|
||||
arguments and the callee function to determine whether they are
|
||||
"pure", whether they read variables, or whether (and when) they
|
||||
update them too. The inliner will introduce a declaration such as
|
||||
`var x int = g()` when it cannot prove that it is safe to substitute
|
||||
the argument throughout.
|
||||
|
||||
- **Constants:** If inlining always replaced a parameter by its argument
|
||||
when the value is constant, some programs would no longer build
|
||||
because checks previously done at run time would happen at compile time.
|
||||
For example `func index(s string, i int) byte { return s[i] }`
|
||||
is a valid function, but if inlining were to replace the call `index("abc", 3)`
|
||||
by the expression `"abc"[3]`, the compiler will report that the
|
||||
index `3` is out of bounds for the string `"abc"`.
|
||||
The inliner will prevent substitution of parameters by problematic
|
||||
constant arguments, again introducing a `var` declaration instead.
|
||||
|
||||
- **Referential integrity:** When a parameter variable is replaced by
|
||||
its argument expression, we must ensure that any names in the
|
||||
argument expression continue to refer to the same thing---not to a
|
||||
different declaration in the callee function body that happens to
|
||||
use the same name! The inliner must replace local references such as
|
||||
`Printf` by qualified references such as `fmt.Printf`, and add an
|
||||
import of package `fmt` as needed.
|
||||
|
||||
- **Implicit conversions:** When passing an argument to a function, it
|
||||
is implicitly converted to the parameter type.
|
||||
If we eliminate the parameter variable, we don't want to
|
||||
lose the conversion as it may be important.
|
||||
For example, in `func f(x any) { y := x; fmt.Printf("%T", &y) }` the
|
||||
type of variable y is `any`, so the program prints `"*interface{}"`.
|
||||
But if inlining the call `f(1)` were to produce the statement `y :=
|
||||
1`, then the type of y would have changed to `int`, which could
|
||||
cause a compile error or, as in this case, a bug, as the program
|
||||
now prints `"*int"`. When the inliner substitutes a parameter variable
|
||||
by its argument value, it may need to introduce explicit conversions
|
||||
of each value to the original parameter type, such as `y := any(1)`.
|
||||
|
||||
- **Last reference:** When an argument expression has no effects
|
||||
and its corresponding parameter is never used, the expression
|
||||
may be eliminated. However, if the expression contains the last
|
||||
reference to a local variable at the caller, this may cause a compile
|
||||
error because the variable is now unused! So the inliner must be
|
||||
cautious about eliminating references to local variables.
|
||||
|
||||
This is just a taste of the problem domain. If you're curious, the
|
||||
documentation for [golang.org/x/tools/internal/refactor/inline](https://pkg.go.dev/golang.org/x/tools/internal/refactor/inline) has
|
||||
more detail. All of this is to say, it's a complex problem, and we aim
|
||||
for correctness first of all. We've already implemented a number of
|
||||
important "tidiness optimizations" and we expect more to follow.
|
||||
|
||||
Please give the inliner a try, and if you find any bugs (where the
|
||||
transformation is incorrect), please do report them. We'd also like to
|
||||
hear what "optimizations" you'd like to see next.
|
||||
@@ -0,0 +1,10 @@
|
||||
This directory contains the draft release notes for each upcoming release.
|
||||
|
||||
Be sure to update the file for the forthcoming release in the same CL
|
||||
that you add new features or fix noteworthy bugs.
|
||||
|
||||
See https://github.com/golang/tools/releases for all past releases.
|
||||
|
||||
Tip: when reviewing edits to markdown files in Gerrit, to see the
|
||||
rendered form, click the "Open in Code Search" link (magnifying glass
|
||||
in blue square) then click "View in > gitiles" (shortcut: `v g`).
|
||||
@@ -0,0 +1,270 @@
|
||||
# gopls/v0.16.0
|
||||
|
||||
<!-- TODO: update this instruction once v0.16.0 is released -->
|
||||
```
|
||||
go install golang.org/x/tools/gopls@v0.16.0-pre.1
|
||||
```
|
||||
|
||||
This release includes several features and bug fixes, and is the first
|
||||
version of gopls to support Go 1.23. To install it, run:
|
||||
|
||||
## New support policy; end of support for Go 1.19 and Go 1.20
|
||||
|
||||
**TL;DR: We are narrowing gopls' support window, but this is unlikely to
|
||||
affect you as long as you use at least Go 1.21 to build gopls. This doesn't
|
||||
affect gopls' support for the code you are writing.**
|
||||
|
||||
This is the last release of gopls that may be built with Go 1.19 or Go 1.20,
|
||||
and also the last to support integrating with go command versions 1.19 and
|
||||
1.20. If built or used with either of these Go versions, it will display
|
||||
a message advising the user to upgrade.
|
||||
|
||||
When using gopls, there are three versions to be aware of:
|
||||
1. The _gopls build go version_: the version of Go used to build gopls.
|
||||
2. The _go command version_: the version of the go list command executed by
|
||||
gopls to load information about your workspace.
|
||||
3. The _language version_: the version in the go directive of the current
|
||||
file's enclosing go.mod file, which determines the file's Go language
|
||||
semantics.
|
||||
|
||||
This gopls release, v0.16.0, is the final release to support Go 1.19 and Go
|
||||
1.20 as the _gopls build go version_ or _go command version_. There is no
|
||||
change to gopls' support for all _language versions_--in fact this support has
|
||||
somewhat improved with the addition of the `stdversion` analyzer (see below).
|
||||
|
||||
Starting with gopls@v0.17.0, which will be released after Go 1.23.0 is released
|
||||
in August, gopls will only support the latest version of Go as the
|
||||
_gopls build go version_.
|
||||
However, thanks to the [forward compatibility](https://go.dev/blog/toolchain)
|
||||
added to Go 1.21, any necessary toolchain upgrade should be handled
|
||||
automatically for users of Go 1.21 or later, just like any other dependency.
|
||||
Additionally, we are reducing our _go command version_ support window from
|
||||
4 versions to 3. Note that this means if you have at least Go 1.21 installed on
|
||||
your system, you should still be able to `go install` and use gopls@v0.17.0.
|
||||
|
||||
We have no plans to ever change our _language version_ support: we expect that
|
||||
gopls will always support developing programs that target _any_ Go version.
|
||||
|
||||
By focusing on building gopls with the latest Go version, we can significantly
|
||||
reduce our maintenance burden and help improve the stability of future gopls
|
||||
releases. See the newly updated
|
||||
[support policy](https://github.com/golang/tools/tree/master/gopls#support-policy)
|
||||
for details. Please comment on [issue #65917](https://go.dev/issue/65917) if
|
||||
you have concerns about this change.
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
- The experimental "allowImplicitNetworkAccess" setting is deprecated (but not
|
||||
yet removed). Please comment on https://go.dev/issue/66861 if you use this
|
||||
setting and would be impacted by its removal.
|
||||
|
||||
## New features
|
||||
|
||||
### Go 1.23 support
|
||||
|
||||
This version of gopls is the first to support new Go 1.23 language features,
|
||||
including
|
||||
[range-over-func](https://go.dev/wiki/RangefuncExperiment) iterators
|
||||
and support for the
|
||||
[`godebug` directive](https://go.dev/ref/mod#go-mod-file-godebug)
|
||||
in go.mod files.
|
||||
|
||||
### Integrated documentation viewer
|
||||
|
||||
Gopls now offers a "Browse package documentation" code action that opens
|
||||
a local web page displaying the generated documentation for the
|
||||
current Go package in a form similar to https://pkg.go.dev.
|
||||
The page will be initially scrolled to the documentation for the
|
||||
declaration containing the cursor.
|
||||
Use this feature to preview the marked-up documentation as you prepare API
|
||||
changes, or to read the documentation for locally edited packages,
|
||||
even ones that have not yet been saved. Reload the page after an edit
|
||||
to see updated documentation.
|
||||
|
||||
TODO: demo in VS Code.
|
||||
|
||||
Clicking on the source-code link associated with a declaration will
|
||||
cause your editor to navigate to the declaration.
|
||||
|
||||
TODO: demo of source linking.
|
||||
|
||||
Editor support:
|
||||
|
||||
- VS Code: use the `Source action > Browse package documentation` menu item.
|
||||
Note: source links navigate the editor but don't yet raise the window yet.
|
||||
Please upvote https://github.com/microsoft/vscode/issues/208093 and
|
||||
https://github.com/microsoft/vscode/issues/207634 (temporarily closed).
|
||||
|
||||
- Emacs: requires eglot v1.17. You may find this `go-doc` function a
|
||||
useful shortcut:
|
||||
|
||||
```lisp
|
||||
(eglot--code-action eglot-code-action-doc "source.doc")
|
||||
|
||||
(defalias 'go-doc #'eglot-code-action-doc
|
||||
"Browse documentation for the current Go package.")
|
||||
```
|
||||
|
||||
- TODO: test in vim, neovim, sublime, helix.
|
||||
|
||||
The `linksInHover` setting now supports a new value, `"gopls"`,
|
||||
that causes documentation links in the the Markdown output
|
||||
of the Hover operation to link to gopls' internal doc viewer.
|
||||
|
||||
|
||||
### Browse free symbols
|
||||
|
||||
Gopls offers another web-based code action, "Browse free symbols",
|
||||
which displays the free symbols referenced by the selected code.
|
||||
|
||||
A symbol is "free" if it is referenced within the selection but
|
||||
declared outside of it. The free symbols that are variables are, in
|
||||
effect, the set of parameters that would be needed if the block were
|
||||
extracted into its own function in the same package.
|
||||
|
||||
Even when you don't intend to extract a block into a new function,
|
||||
this information can help you to tell at a glance what names a block
|
||||
of code depends on.
|
||||
|
||||
Each dotted path of identifiers (such as `file.Name.Pos`) is reported
|
||||
as a separate item, so that you can see which parts of a complex
|
||||
type are actually needed.
|
||||
|
||||
The free symbols of the body of a function may reveal that
|
||||
only a small part (a single field of a struct, say) of one of the
|
||||
function's parameters is used, allowing you to simplify and generalize
|
||||
the function by choosing a different type for that parameter.
|
||||
|
||||
- TODO screenshot
|
||||
|
||||
- VS Code: use the `Source action > Browse free symbols` menu item.
|
||||
|
||||
- Emacs: requires eglot v1.17. You may find this `go-doc` function a
|
||||
useful shortcut:
|
||||
|
||||
```lisp
|
||||
(eglot--code-action eglot-code-action-freesymbols "source.freesymbols")
|
||||
|
||||
(defalias 'go-freesymbols #'eglot-code-action-freesymbols
|
||||
"Browse free symbols referred to by the current selection.")
|
||||
```
|
||||
TODO(dominikh/go-mode.el#436): add both of these to go-mode.el.
|
||||
|
||||
### Browse assembly
|
||||
|
||||
Gopls offers a third web-based code action, "Browse assembly for f",
|
||||
which displays an assembly listing of the function declaration
|
||||
enclosing the selected code, plus any nested functions (function
|
||||
literals, deferred calls).
|
||||
It invokes the compiler to generate the report.
|
||||
Reloading the page will update the report.
|
||||
|
||||
The machine architecture is determined by the "view" or build
|
||||
configuration that gopls selects for the current file.
|
||||
This is usually the same as your machine's GOARCH unless you are
|
||||
working in a file with `go:build` tags for a different architecture.
|
||||
|
||||
- TODO screenshot
|
||||
|
||||
Gopls cannot yet display assembly for generic functions: generic
|
||||
functions are not fully compiled until they are instantiated, but any
|
||||
function declaration enclosing the selection cannot be an instantiated
|
||||
generic function.
|
||||
<!-- Clearly the ideal UX for generic functions is to use the function
|
||||
symbol under the cursor, e.g. Vector[string], rather than the
|
||||
enclosing function; but computing the name of the linker symbol
|
||||
remains a challenge. -->
|
||||
|
||||
### `unusedwrite` analyzer
|
||||
|
||||
The new
|
||||
[unusedwrite](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite)
|
||||
analyzer reports assignments, often to fields of structs, that have no
|
||||
effect because, for example, the struct is never used again:
|
||||
|
||||
```go
|
||||
func scheme(host string) string {
|
||||
u := &url.URL{
|
||||
Host: host, // "unused write to field Host" (no need to construct a URL)
|
||||
Scheme: "https:",
|
||||
}
|
||||
return u.Scheme
|
||||
}
|
||||
```
|
||||
|
||||
This is at best an indication that the code is unnecessarily complex
|
||||
(for instance, some dead code could be removed), but often indicates a
|
||||
bug, as in this example:
|
||||
|
||||
```go
|
||||
type S struct { x int }
|
||||
|
||||
func (s S) set(x int) {
|
||||
s.x = x // "unused write to field x" (s should be a *S pointer)
|
||||
}
|
||||
```
|
||||
|
||||
### `stdversion` analyzer
|
||||
|
||||
The new
|
||||
[`stdversion`](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stdversion)
|
||||
analyzer warns about the use of too-new standard library symbols based on the
|
||||
version of the `go` directive in your `go.mod` file. This improves our support
|
||||
for older _language versions_ (see above), even when gopls is built with
|
||||
a recent Go version.
|
||||
|
||||
TODO: add a screenshot demonstrating the new diagnostics
|
||||
|
||||
### Two more vet analyzers
|
||||
|
||||
The [framepointer](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/framepointer)
|
||||
and [sigchanyzer](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/sigchanyzer)
|
||||
analyzers have long been part of go vet's suite,
|
||||
but had been overlooked in previous versions of gopls.
|
||||
|
||||
Henceforth, gopls will always include any analyzers run by vet.
|
||||
|
||||
### Hover shows size/offset info, and struct tags
|
||||
|
||||
TODO: consolidate release notes related to Hover improvements.
|
||||
|
||||
Hovering over the identifier that declares a type or struct field now
|
||||
displays the size information for the type, and the offset information
|
||||
for the field. In addition, it reports the percentage of wasted space
|
||||
due to suboptimal ordering of struct fields, if this figure is 20% or
|
||||
higher. This information may be helpful when making space
|
||||
optimizations to your data structures, or when reading assembly code.
|
||||
|
||||
TODO: example hover image.
|
||||
|
||||
Hovering over a field with struct tags now also includes those tags.
|
||||
|
||||
TODO: example hover image
|
||||
|
||||
### Hover and definition on doc links
|
||||
|
||||
Go 1.19 added support for [links in doc
|
||||
comments](https://go.dev/doc/comment#links), allowing the
|
||||
documentation for one symbol to reference another:
|
||||
|
||||
TODO: turn the code below into a VS Code screenshot of hover.
|
||||
|
||||
```go
|
||||
// Logf logs a message formatted in the manner of [fmt.Printf].
|
||||
func Logf(format string, args ...any)
|
||||
```
|
||||
|
||||
Gopls's Hover and Definition operations now treat these links just
|
||||
like identifiers, so hovering over one will display information about
|
||||
the symbol, and "Go to definition" will navigate to its declaration.
|
||||
Thanks to @rogeryk for contributing this feature.
|
||||
|
||||
|
||||
## Bugs fixed
|
||||
|
||||
## Thank you to our contributors!
|
||||
|
||||
@guodongli-google for the `unusedwrite` analyzer.
|
||||
TODO: they're a xoogler; is there a more current GH account?
|
||||
|
||||
@rogeryk
|
||||
@@ -0,0 +1,25 @@
|
||||
# Gopls release policy
|
||||
|
||||
Gopls releases follow [semver](http://semver.org), with major changes and new
|
||||
features introduced only in new minor versions (i.e. versions of the form
|
||||
`v*.N.0` for some N). Subsequent patch releases contain only cherry-picked
|
||||
fixes or superficial updates.
|
||||
|
||||
In order to align with the
|
||||
[Go release timeline](https://github.com/golang/go/wiki/Go-Release-Cycle#timeline),
|
||||
we aim to release a new minor version of Gopls approximately every three
|
||||
months, with patch releases approximately every month, according to the
|
||||
following table:
|
||||
|
||||
| Month | Version(s) |
|
||||
| ---- | ------- |
|
||||
| Jan | `v*.<N+0>.0` |
|
||||
| Jan-Mar | `v*.<N+0>.*` |
|
||||
| Apr | `v*.<N+1>.0` |
|
||||
| Apr-Jun | `v*.<N+1>.*` |
|
||||
| Jul | `v*.<N+2>.0` |
|
||||
| Jul-Sep | `v*.<N+2>.*` |
|
||||
| Oct | `v*.<N+3>.0` |
|
||||
| Oct-Dec | `v*.<N+3>.*` |
|
||||
|
||||
For more background on this policy, see https://go.dev/issue/55267.
|
||||
@@ -0,0 +1,121 @@
|
||||
# Semantic Tokens
|
||||
|
||||
The [LSP](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_semanticTokens)
|
||||
specifies semantic tokens as a way of telling clients about language-specific
|
||||
properties of pieces of code in a file being edited.
|
||||
|
||||
The client asks for a set of semantic tokens and modifiers. This note describe which ones
|
||||
gopls will return, and under what circumstances. Gopls has no control over how the client
|
||||
converts semantic tokens into colors (or some other visible indication). In vscode it
|
||||
is possible to modify the color a theme uses by setting the `editor.semanticTokenColorCustomizations`
|
||||
object. We provide a little [guidance](#Colors) later.
|
||||
|
||||
There are 22 semantic tokens, with 10 possible modifiers. The protocol allows each semantic
|
||||
token to be used with any of the 1024 subsets of possible modifiers, but most combinations
|
||||
don't make intuitive sense (although `async documentation` has a certain appeal).
|
||||
|
||||
The 22 semantic tokens are `namespace`, `type`, `class`, `enum`, `interface`,
|
||||
`struct`, `typeParameter`, `parameter`, `variable`, `property`, `enumMember`,
|
||||
`event`, `function`, `method`, `macro`, `keyword`, `modifier`, `comment`,
|
||||
`string`, `number`, `regexp`, `operator`.
|
||||
|
||||
The 10 modifiers are `declaration`, `definition`, `readonly`, `static`,
|
||||
`deprecated`, `abstract`, `async`, `modification`, `documentation`, `defaultLibrary`.
|
||||
|
||||
The authoritative lists are in the [specification](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#semanticTokenTypes)
|
||||
|
||||
For the implementation to work correctly the client and server have to agree on the ordering
|
||||
of the tokens and of the modifiers. Gopls, therefore, will only send tokens and modifiers
|
||||
that the client has asked for. This document says what gopls would send if the client
|
||||
asked for everything. By default, vscode asks for everything.
|
||||
|
||||
Gopls sends 11 token types for `.go` files and 1 for `.*tmpl` files.
|
||||
Nothing is sent for any other kind of file.
|
||||
This all could change. (When Go has generics, gopls will return `typeParameter`.)
|
||||
|
||||
For `.*tmpl` files gopls sends `macro`, and no modifiers, for each `{{`...`}}` scope.
|
||||
|
||||
## Semantic tokens for Go files
|
||||
|
||||
There are two contrasting guiding principles that might be used to decide what to mark
|
||||
with semantic tokens. All clients already do some kind of syntax marking. E.g., vscode
|
||||
uses a TextMate grammar. The minimal principle would send semantic tokens only for those
|
||||
language features that cannot be reliably found without parsing Go and looking at types.
|
||||
The maximal principle would attempt to convey as much as possible about the Go code,
|
||||
using all available parsing and type information.
|
||||
|
||||
There is much to be said for returning minimal information, but the minimal principle is
|
||||
not well-specified. Gopls has no way of knowing what the clients know about the Go program
|
||||
being edited. Even in vscode the TextMate grammars can be more or less elaborate
|
||||
and change over time. (Nonetheless, a minimal implementation would not return `keyword`,
|
||||
`number`, `comment`, or `string`.)
|
||||
|
||||
The maximal position isn't particularly well-specified either. To chose one example, a
|
||||
format string might have formatting codes (`%[4]-3.6f`), escape sequences (`\U00010604`), and regular
|
||||
characters. Should these all be distinguished? One could even imagine distinguishing
|
||||
different runes by their Unicode language assignment, or some other Unicode property, such as
|
||||
being [confusable](http://www.unicode.org/Public/security/10.0.0/confusables.txt).
|
||||
|
||||
Gopls does not come close to either of these principles. Semantic tokens are returned for
|
||||
identifiers, keywords, operators, comments, and literals. (Semantic tokens do not
|
||||
cover the file. They are not returned for
|
||||
white space or punctuation, and there is no semantic token for labels.)
|
||||
The following describes more precisely what gopls
|
||||
does, with a few notes on possible alternative choices.
|
||||
The references to *object* refer to the
|
||||
```types.Object``` returned by the type checker. The references to *nodes* refer to the
|
||||
```ast.Node``` from the parser.
|
||||
|
||||
1. __`keyword`__ All Go [keywords](https://golang.org/ref/spec#Keywords) are marked `keyword`.
|
||||
1. __`namespace`__ All package names are marked `namespace`. In an import, if there is an
|
||||
alias, it would be marked. Otherwise the last component of the import path is marked.
|
||||
1. __`type`__ Objects of type ```types.TypeName``` are marked `type`.
|
||||
If they are also ```types.Basic```
|
||||
the modifier is `defaultLibrary`. (And in ```type B struct{C}```, ```B``` has modifier `definition`.)
|
||||
1. __`parameter`__ The formal arguments in ```ast.FuncDecl``` and ```ast.FuncType``` nodes are marked `parameter`.
|
||||
1. __`variable`__ Identifiers in the
|
||||
scope of ```const``` are modified with `readonly`. ```nil``` is usually a `variable` modified with both
|
||||
`readonly` and `defaultLibrary`. (```nil``` is a predefined identifier; the user can redefine it,
|
||||
in which case it would just be a variable, or whatever.) Identifiers of type ```types.Variable``` are,
|
||||
not surprisingly, marked `variable`. Identifiers being defined (node ```ast.GenDecl```) are modified
|
||||
by `definition` and, if appropriate, `readonly`. Receivers (in method declarations) are
|
||||
`variable`.
|
||||
1. __`method`__ Methods are marked at their definition (```func (x foo) bar() {}```) or declaration
|
||||
in an ```interface```. Methods are not marked where they are used.
|
||||
In ```x.bar()```, ```x``` will be marked
|
||||
either as a `namespace` if it is a package name, or as a `variable` if it is an interface value,
|
||||
so distinguishing ```bar``` seemed superfluous.
|
||||
1. __`function`__ Bultins (```types.Builtin```) are modified with `defaultLibrary`
|
||||
(e.g., ```make```, ```len```, ```copy```). Identifiers whose
|
||||
object is ```types.Func``` or whose node is ```ast.FuncDecl``` are `function`.
|
||||
1. __`comment`__ Comments and struct tags. (Perhaps struct tags should be `property`?)
|
||||
1. __`string`__ Strings. Could add modifiers for e.g., escapes or format codes.
|
||||
1. __`number`__ Numbers. Should the ```i``` in ```23i``` be handled specially?
|
||||
1. __`operator`__ Assignment operators, binary operators, ellipses (```...```), increment/decrement
|
||||
operators, sends (```<-```), and unary operators.
|
||||
|
||||
Gopls will send the modifier `deprecated` if it finds a comment
|
||||
```// deprecated``` in the godoc.
|
||||
|
||||
The unused tokens for Go code are `class`, `enum`, `interface`,
|
||||
`struct`, `typeParameter`, `property`, `enumMember`,
|
||||
`event`, `macro`, `modifier`,
|
||||
`regexp`
|
||||
|
||||
## Colors
|
||||
|
||||
These comments are about vscode.
|
||||
|
||||
The documentation has a [helpful](https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#custom-textmate-scope-mappings)
|
||||
description of which semantic tokens correspond to scopes in TextMate grammars. Themes seem
|
||||
to use the TextMate scopes to decide on colors.
|
||||
|
||||
Some examples of color customizations are [here](https://medium.com/@danromans/how-to-customize-semantic-token-colorization-with-visual-studio-code-ac3eab96141b).
|
||||
|
||||
## Note
|
||||
|
||||
While a file is being edited it may temporarily contain either
|
||||
parsing errors or type errors. In this case gopls cannot determine some (or maybe any)
|
||||
of the semantic tokens. To avoid weird flickering it is the responsibility
|
||||
of clients to maintain the semantic token information
|
||||
in the unedited part of the file, and they do.
|
||||
@@ -0,0 +1,556 @@
|
||||
# Settings
|
||||
|
||||
This document describes gopls' configuration settings.
|
||||
|
||||
Gopls settings are defined by an JSON object whose valid fields are
|
||||
described below. These fields are gopls-specific, and generic LSP
|
||||
clients have no knowledge of them.
|
||||
|
||||
Different clients present configuration settings in their user
|
||||
interfaces in a wide variety of ways.
|
||||
For example, some expect the user to edit the raw JSON object while
|
||||
others use a data structure in the editor's configuration language;
|
||||
still others (such as VS Code) have a graphical configuration system.
|
||||
Be sure to consult the documentation for how to express configuration
|
||||
settings in your client.
|
||||
Some clients also permit settings to be configured differently for
|
||||
each workspace folder.
|
||||
|
||||
Any settings that are experimental or for debugging purposes are
|
||||
marked as such. To enable all experimental features, use
|
||||
**allExperiments: `true`**. You will still be able to independently
|
||||
override specific experimental features.
|
||||
|
||||
<!--
|
||||
All settings are uniquely identified by name such as `semanticTokens`
|
||||
or `templateExtensions`.
|
||||
However, for convenience of VS Code, each setting also has an
|
||||
undocumented alias whose form is a dotted path such as
|
||||
`ui.semanticTokens` or `build.templateExtensions`.
|
||||
However, only the final segment is actually significant, so
|
||||
`build.templateExtensions` is equivalent to `templateExtensions`.
|
||||
All clients but VS Code should use the short form.
|
||||
-->
|
||||
|
||||
<!-- This portion is generated by doc/generate from the ../internal/settings package. -->
|
||||
<!-- BEGIN User: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
|
||||
* [Build](#build)
|
||||
* [Formatting](#formatting)
|
||||
* [UI](#ui)
|
||||
* [Completion](#completion)
|
||||
* [Diagnostic](#diagnostic)
|
||||
* [Documentation](#documentation)
|
||||
* [Inlayhint](#inlayhint)
|
||||
* [Navigation](#navigation)
|
||||
|
||||
<a id='build'></a>
|
||||
## Build
|
||||
|
||||
<a id='buildFlags'></a>
|
||||
### `buildFlags` *[]string*
|
||||
|
||||
buildFlags is the set of flags passed on to the build system when invoked.
|
||||
It is applied to queries like `go list`, which is used when discovering files.
|
||||
The most common use is to set `-tags`.
|
||||
|
||||
Default: `[]`.
|
||||
|
||||
<a id='env'></a>
|
||||
### `env` *map[string]string*
|
||||
|
||||
env adds environment variables to external commands run by `gopls`, most notably `go list`.
|
||||
|
||||
Default: `{}`.
|
||||
|
||||
<a id='directoryFilters'></a>
|
||||
### `directoryFilters` *[]string*
|
||||
|
||||
directoryFilters can be used to exclude unwanted directories from the
|
||||
workspace. By default, all directories are included. Filters are an
|
||||
operator, `+` to include and `-` to exclude, followed by a path prefix
|
||||
relative to the workspace folder. They are evaluated in order, and
|
||||
the last filter that applies to a path controls whether it is included.
|
||||
The path prefix can be empty, so an initial `-` excludes everything.
|
||||
|
||||
DirectoryFilters also supports the `**` operator to match 0 or more directories.
|
||||
|
||||
Examples:
|
||||
|
||||
Exclude node_modules at current depth: `-node_modules`
|
||||
|
||||
Exclude node_modules at any depth: `-**/node_modules`
|
||||
|
||||
Include only project_a: `-` (exclude everything), `+project_a`
|
||||
|
||||
Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules`
|
||||
|
||||
Default: `["-**/node_modules"]`.
|
||||
|
||||
<a id='templateExtensions'></a>
|
||||
### `templateExtensions` *[]string*
|
||||
|
||||
templateExtensions gives the extensions of file names that are treateed
|
||||
as template files. (The extension
|
||||
is the part of the file name after the final dot.)
|
||||
|
||||
Default: `[]`.
|
||||
|
||||
<a id='memoryMode'></a>
|
||||
### `memoryMode` *string*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
obsolete, no effect
|
||||
|
||||
Default: `""`.
|
||||
|
||||
<a id='expandWorkspaceToModule'></a>
|
||||
### `expandWorkspaceToModule` *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
expandWorkspaceToModule determines which packages are considered
|
||||
"workspace packages" when the workspace is using modules.
|
||||
|
||||
Workspace packages affect the scope of workspace-wide operations. Notably,
|
||||
gopls diagnoses all packages considered to be part of the workspace after
|
||||
every keystroke, so by setting "ExpandWorkspaceToModule" to false, and
|
||||
opening a nested workspace directory, you can reduce the amount of work
|
||||
gopls has to do to keep your workspace up to date.
|
||||
|
||||
Default: `true`.
|
||||
|
||||
<a id='allowImplicitNetworkAccess'></a>
|
||||
### `allowImplicitNetworkAccess` *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
allowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module
|
||||
downloads rather than requiring user action. This option will eventually
|
||||
be removed.
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<a id='standaloneTags'></a>
|
||||
### `standaloneTags` *[]string*
|
||||
|
||||
standaloneTags specifies a set of build constraints that identify
|
||||
individual Go source files that make up the entire main package of an
|
||||
executable.
|
||||
|
||||
A common example of standalone main files is the convention of using the
|
||||
directive `//go:build ignore` to denote files that are not intended to be
|
||||
included in any package, for example because they are invoked directly by
|
||||
the developer using `go run`.
|
||||
|
||||
Gopls considers a file to be a standalone main file if and only if it has
|
||||
package name "main" and has a build directive of the exact form
|
||||
"//go:build tag" or "// +build tag", where tag is among the list of tags
|
||||
configured by this setting. Notably, if the build constraint is more
|
||||
complicated than a simple tag (such as the composite constraint
|
||||
`//go:build tag && go1.18`), the file is not considered to be a standalone
|
||||
main file.
|
||||
|
||||
This setting is only supported when gopls is built with Go 1.16 or later.
|
||||
|
||||
Default: `["ignore"]`.
|
||||
|
||||
<a id='formatting'></a>
|
||||
## Formatting
|
||||
|
||||
<a id='local'></a>
|
||||
### `local` *string*
|
||||
|
||||
local is the equivalent of the `goimports -local` flag, which puts
|
||||
imports beginning with this string after third-party packages. It should
|
||||
be the prefix of the import path whose imports should be grouped
|
||||
separately.
|
||||
|
||||
Default: `""`.
|
||||
|
||||
<a id='gofumpt'></a>
|
||||
### `gofumpt` *bool*
|
||||
|
||||
gofumpt indicates if we should run gofumpt formatting.
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<a id='ui'></a>
|
||||
## UI
|
||||
|
||||
<a id='codelenses'></a>
|
||||
### `codelenses` *map[enum]bool*
|
||||
|
||||
codelenses overrides the enabled/disabled state of each of gopls'
|
||||
sources of [Code Lenses](codelenses.md).
|
||||
|
||||
Example Usage:
|
||||
|
||||
```json5
|
||||
"gopls": {
|
||||
...
|
||||
"codelenses": {
|
||||
"generate": false, // Don't show the `go generate` lens.
|
||||
"gc_details": true // Show a code lens toggling the display of gc's choices.
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Default: `{"gc_details":false,"generate":true,"regenerate_cgo":true,"run_govulncheck":false,"tidy":true,"upgrade_dependency":true,"vendor":true}`.
|
||||
|
||||
<a id='semanticTokens'></a>
|
||||
### `semanticTokens` *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
semanticTokens controls whether the LSP server will send
|
||||
semantic tokens to the client.
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<a id='noSemanticString'></a>
|
||||
### `noSemanticString` *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
noSemanticString turns off the sending of the semantic token 'string'
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<a id='noSemanticNumber'></a>
|
||||
### `noSemanticNumber` *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
noSemanticNumber turns off the sending of the semantic token 'number'
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<a id='completion'></a>
|
||||
## Completion
|
||||
|
||||
<a id='usePlaceholders'></a>
|
||||
### `usePlaceholders` *bool*
|
||||
|
||||
placeholders enables placeholders for function parameters or struct
|
||||
fields in completion responses.
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<a id='completionBudget'></a>
|
||||
### `completionBudget` *time.Duration*
|
||||
|
||||
**This setting is for debugging purposes only.**
|
||||
|
||||
completionBudget is the soft latency goal for completion requests. Most
|
||||
requests finish in a couple milliseconds, but in some cases deep
|
||||
completions can take much longer. As we use up our budget we
|
||||
dynamically reduce the search scope to ensure we return timely
|
||||
results. Zero means unlimited.
|
||||
|
||||
Default: `"100ms"`.
|
||||
|
||||
<a id='matcher'></a>
|
||||
### `matcher` *enum*
|
||||
|
||||
**This is an advanced setting and should not be configured by most `gopls` users.**
|
||||
|
||||
matcher sets the algorithm that is used when calculating completion
|
||||
candidates.
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"CaseInsensitive"`
|
||||
* `"CaseSensitive"`
|
||||
* `"Fuzzy"`
|
||||
|
||||
Default: `"Fuzzy"`.
|
||||
|
||||
<a id='experimentalPostfixCompletions'></a>
|
||||
### `experimentalPostfixCompletions` *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
experimentalPostfixCompletions enables artificial method snippets
|
||||
such as "someSlice.sort!".
|
||||
|
||||
Default: `true`.
|
||||
|
||||
<a id='completeFunctionCalls'></a>
|
||||
### `completeFunctionCalls` *bool*
|
||||
|
||||
completeFunctionCalls enables function call completion.
|
||||
|
||||
When completing a statement, or when a function return type matches the
|
||||
expected of the expression being completed, completion may suggest call
|
||||
expressions (i.e. may include parentheses).
|
||||
|
||||
Default: `true`.
|
||||
|
||||
<a id='diagnostic'></a>
|
||||
## Diagnostic
|
||||
|
||||
<a id='analyses'></a>
|
||||
### `analyses` *map[string]bool*
|
||||
|
||||
analyses specify analyses that the user would like to enable or disable.
|
||||
A map of the names of analysis passes that should be enabled/disabled.
|
||||
A full list of analyzers that gopls uses can be found in
|
||||
[analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md).
|
||||
|
||||
Example Usage:
|
||||
|
||||
```json5
|
||||
...
|
||||
"analyses": {
|
||||
"unreachable": false, // Disable the unreachable analyzer.
|
||||
"unusedvariable": true // Enable the unusedvariable analyzer.
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
Default: `{}`.
|
||||
|
||||
<a id='staticcheck'></a>
|
||||
### `staticcheck` *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
staticcheck enables additional analyses from staticcheck.io.
|
||||
These analyses are documented on
|
||||
[Staticcheck's website](https://staticcheck.io/docs/checks/).
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<a id='annotations'></a>
|
||||
### `annotations` *map[enum]bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
annotations specifies the various kinds of optimization diagnostics
|
||||
that should be reported by the gc_details command.
|
||||
|
||||
Each enum must be one of:
|
||||
|
||||
* `"bounds"` controls bounds checking diagnostics.
|
||||
* `"escape"` controls diagnostics about escape choices.
|
||||
* `"inline"` controls diagnostics about inlining choices.
|
||||
* `"nil"` controls nil checks.
|
||||
|
||||
Default: `{"bounds":true,"escape":true,"inline":true,"nil":true}`.
|
||||
|
||||
<a id='vulncheck'></a>
|
||||
### `vulncheck` *enum*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
vulncheck enables vulnerability scanning.
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"Imports"`: In Imports mode, `gopls` will report vulnerabilities that affect packages
|
||||
directly and indirectly used by the analyzed main module.
|
||||
* `"Off"`: Disable vulnerability analysis.
|
||||
|
||||
Default: `"Off"`.
|
||||
|
||||
<a id='diagnosticsDelay'></a>
|
||||
### `diagnosticsDelay` *time.Duration*
|
||||
|
||||
**This is an advanced setting and should not be configured by most `gopls` users.**
|
||||
|
||||
diagnosticsDelay controls the amount of time that gopls waits
|
||||
after the most recent file modification before computing deep diagnostics.
|
||||
Simple diagnostics (parsing and type-checking) are always run immediately
|
||||
on recently modified packages.
|
||||
|
||||
This option must be set to a valid duration string, for example `"250ms"`.
|
||||
|
||||
Default: `"1s"`.
|
||||
|
||||
<a id='diagnosticsTrigger'></a>
|
||||
### `diagnosticsTrigger` *enum*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
diagnosticsTrigger controls when to run diagnostics.
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"Edit"`: Trigger diagnostics on file edit and save. (default)
|
||||
* `"Save"`: Trigger diagnostics only on file save. Events like initial workspace load
|
||||
or configuration change will still trigger diagnostics.
|
||||
|
||||
Default: `"Edit"`.
|
||||
|
||||
<a id='analysisProgressReporting'></a>
|
||||
### `analysisProgressReporting` *bool*
|
||||
|
||||
analysisProgressReporting controls whether gopls sends progress
|
||||
notifications when construction of its index of analysis facts is taking a
|
||||
long time. Cancelling these notifications will cancel the indexing task,
|
||||
though it will restart after the next change in the workspace.
|
||||
|
||||
When a package is opened for the first time and heavyweight analyses such as
|
||||
staticcheck are enabled, it can take a while to construct the index of
|
||||
analysis facts for all its dependencies. The index is cached in the
|
||||
filesystem, so subsequent analysis should be faster.
|
||||
|
||||
Default: `true`.
|
||||
|
||||
<a id='documentation'></a>
|
||||
## Documentation
|
||||
|
||||
<a id='hoverKind'></a>
|
||||
### `hoverKind` *enum*
|
||||
|
||||
hoverKind controls the information that appears in the hover text.
|
||||
SingleLine and Structured are intended for use only by authors of editor plugins.
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"FullDocumentation"`
|
||||
* `"NoDocumentation"`
|
||||
* `"SingleLine"`
|
||||
* `"Structured"` is an experimental setting that returns a structured hover format.
|
||||
This format separates the signature from the documentation, so that the client
|
||||
can do more manipulation of these fields.\
|
||||
This should only be used by clients that support this behavior.
|
||||
* `"SynopsisDocumentation"`
|
||||
|
||||
Default: `"FullDocumentation"`.
|
||||
|
||||
<a id='linkTarget'></a>
|
||||
### `linkTarget` *string*
|
||||
|
||||
linkTarget controls where documentation links go.
|
||||
It might be one of:
|
||||
|
||||
* `"godoc.org"`
|
||||
* `"pkg.go.dev"`
|
||||
|
||||
If company chooses to use its own `godoc.org`, its address can be used as well.
|
||||
|
||||
Modules matching the GOPRIVATE environment variable will not have
|
||||
documentation links in hover.
|
||||
|
||||
Default: `"pkg.go.dev"`.
|
||||
|
||||
<a id='linksInHover'></a>
|
||||
### `linksInHover` *any*
|
||||
|
||||
linksInHover controls the presence of documentation links
|
||||
in hover markdown.
|
||||
|
||||
Its legal values are:
|
||||
- `false`, for no links;
|
||||
- `true`, for links to the `linkTarget` domain; or
|
||||
- `"gopls"`, for links to gopls' internal documentation viewer.
|
||||
|
||||
Default: `true`.
|
||||
|
||||
<a id='inlayhint'></a>
|
||||
## Inlayhint
|
||||
|
||||
<a id='hints'></a>
|
||||
### `hints` *map[string]bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
hints specify inlay hints that users want to see. A full list of hints
|
||||
that gopls uses can be found in
|
||||
[inlayHints.md](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md).
|
||||
|
||||
Default: `{}`.
|
||||
|
||||
<a id='navigation'></a>
|
||||
## Navigation
|
||||
|
||||
<a id='importShortcut'></a>
|
||||
### `importShortcut` *enum*
|
||||
|
||||
importShortcut specifies whether import statements should link to
|
||||
documentation or go to definitions.
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"Both"`
|
||||
* `"Definition"`
|
||||
* `"Link"`
|
||||
|
||||
Default: `"Both"`.
|
||||
|
||||
<a id='symbolMatcher'></a>
|
||||
### `symbolMatcher` *enum*
|
||||
|
||||
**This is an advanced setting and should not be configured by most `gopls` users.**
|
||||
|
||||
symbolMatcher sets the algorithm that is used when finding workspace symbols.
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"CaseInsensitive"`
|
||||
* `"CaseSensitive"`
|
||||
* `"FastFuzzy"`
|
||||
* `"Fuzzy"`
|
||||
|
||||
Default: `"FastFuzzy"`.
|
||||
|
||||
<a id='symbolStyle'></a>
|
||||
### `symbolStyle` *enum*
|
||||
|
||||
**This is an advanced setting and should not be configured by most `gopls` users.**
|
||||
|
||||
symbolStyle controls how symbols are qualified in symbol responses.
|
||||
|
||||
Example Usage:
|
||||
|
||||
```json5
|
||||
"gopls": {
|
||||
...
|
||||
"symbolStyle": "Dynamic",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"Dynamic"` uses whichever qualifier results in the highest scoring
|
||||
match for the given symbol query. Here a "qualifier" is any "/" or "."
|
||||
delimited suffix of the fully qualified symbol. i.e. "to/pkg.Foo.Field" or
|
||||
just "Foo.Field".
|
||||
* `"Full"` is fully qualified symbols, i.e.
|
||||
"path/to/pkg.Foo.Field".
|
||||
* `"Package"` is package qualified symbols i.e.
|
||||
"pkg.Foo.Field".
|
||||
|
||||
Default: `"Dynamic"`.
|
||||
|
||||
<a id='symbolScope'></a>
|
||||
### `symbolScope` *enum*
|
||||
|
||||
symbolScope controls which packages are searched for workspace/symbol
|
||||
requests. When the scope is "workspace", gopls searches only workspace
|
||||
packages. When the scope is "all", gopls searches all loaded packages,
|
||||
including dependencies and the standard library.
|
||||
|
||||
Must be one of:
|
||||
|
||||
* `"all"` matches symbols in any loaded package, including
|
||||
dependencies.
|
||||
* `"workspace"` matches symbols in workspace packages only.
|
||||
|
||||
Default: `"all"`.
|
||||
|
||||
<a id='verboseOutput'></a>
|
||||
### `verboseOutput` *bool*
|
||||
|
||||
**This setting is for debugging purposes only.**
|
||||
|
||||
verboseOutput enables additional debug logging.
|
||||
|
||||
Default: `false`.
|
||||
|
||||
<!-- END User: DO NOT MANUALLY EDIT THIS SECTION -->
|
||||
@@ -0,0 +1,81 @@
|
||||
# Sublime Text
|
||||
|
||||
Use the [LSP] package. After installing it using Package Control, do the following:
|
||||
|
||||
* Open the **Command Palette**
|
||||
* Find and run the command **LSP: Enable Language Server Globally**
|
||||
* Select the **gopls** item. Be careful not to select the similarly named *golsp* by mistake.
|
||||
|
||||
Finally, you should familiarise yourself with the LSP package's *Settings* and *Key Bindings*. Find them under the menu item **Preferences > Package Settings > LSP**.
|
||||
|
||||
## Examples
|
||||
Minimal global LSP settings, that assume **gopls** and **go** appear on the PATH seen by Sublime Text:<br>
|
||||
```
|
||||
{
|
||||
"clients": {
|
||||
"gopls": {
|
||||
"enabled": true,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Global LSP settings that supply a specific PATH for finding **gopls** and **go**, as well as some settings for Sublime LSP itself:
|
||||
```
|
||||
{
|
||||
"clients": {
|
||||
"gopls": {
|
||||
"enabled": true,
|
||||
"env": {
|
||||
"PATH": "/path/to/your/go/bin",
|
||||
}
|
||||
}
|
||||
},
|
||||
// Recommended by https://agniva.me/gopls/2021/01/02/setting-up-gopls-sublime.html
|
||||
// except log_stderr mentioned there is no longer recognized.
|
||||
"show_references_in_quick_panel": true,
|
||||
"log_debug": true,
|
||||
// These two are recommended by LSP-json as replacement for deprecated only_show_lsp_completions
|
||||
"inhibit_snippet_completions": true,
|
||||
"inhibit_word_completions": true,
|
||||
}
|
||||
```
|
||||
|
||||
LSP and gopls settings can also be adjusted on a per-project basis to override global settings.
|
||||
```
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "/path/to/a/folder/one"
|
||||
},
|
||||
{
|
||||
// If you happen to be working on Go itself, this can be helpful; go-dev/bin should be on PATH.
|
||||
"path": "/path/to/your/go-dev/src/cmd"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"LSP": {
|
||||
"gopls": {
|
||||
// To use a specific version of gopls with Sublime Text LSP (e.g., to try new features in development)
|
||||
"command": [
|
||||
"/path/to/your/go/bin/gopls"
|
||||
],
|
||||
"env": {
|
||||
"PATH": "/path/to/your/go-dev/bin:/path/to/your/go/bin",
|
||||
"GOPATH": "",
|
||||
},
|
||||
"settings": {
|
||||
"experimentalWorkspaceModule": true
|
||||
}
|
||||
}
|
||||
},
|
||||
// This will apply for all languages in this project that have
|
||||
// LSP servers, not just Go, however cannot enable just for Go.
|
||||
"lsp_format_on_save": true,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usually changes to these settings are recognized after saving the project file, but it may sometimes be necessary to either restart the server(s) (**Tools > LSP > Restart Servers**) or quit and restart Sublime Text itself.
|
||||
|
||||
[LSP]: https://packagecontrol.io/packages/LSP
|
||||
@@ -0,0 +1,48 @@
|
||||
# Troubleshooting
|
||||
|
||||
If you suspect that `gopls` is crashing or not working correctly, please follow the troubleshooting steps below.
|
||||
|
||||
If `gopls` is using too much memory, please follow the steps under [Memory usage](#debug-memory-usage).
|
||||
|
||||
## Steps
|
||||
|
||||
VS Code users should follow [their troubleshooting guide](https://github.com/golang/vscode-go/blob/master/docs/troubleshooting.md), which has more a more specific version of these instructions.
|
||||
|
||||
1. Verify that your project is in good shape by working with it outside of your editor. Running a command like `go build ./...` in the workspace directory will compile everything. For modules, `go mod tidy` is another good check, though it may modify your `go.mod`.
|
||||
1. Check that your editor isn't showing any diagnostics that indicate a problem with your workspace. They may appear as diagnostics on a Go file's package declaration, diagnostics in a go.mod file, or as a status or progress message. Problems in the workspace configuration can cause many different symptoms. See the [workspace setup instructions](workspace.md) for help.
|
||||
1. Make sure `gopls` is up to date by following the [installation instructions](../README.md#installation), then [restarting gopls](#restart-gopls).
|
||||
1. Optionally, [ask for help](#ask-for-help) on Gophers Slack.
|
||||
1. Finally, [report the issue](#file-an-issue) to the `gopls` developers.
|
||||
|
||||
## Restart `gopls`
|
||||
|
||||
`gopls` has no persistent state, so restarting it will fix transient problems. This is good and bad: good, because you can keep working, and bad, because you won't be able to debug the issue until it recurs.
|
||||
|
||||
In most cases, closing all your open editors will guarantee that `gopls` is killed and restarted. If you don't want to do that, there may be an editor command you can use to restart only `gopls`. Note that some `vim` configurations keep the server alive for a while after the editor exits; you may need to explicitly kill `gopls` if you use `vim`.
|
||||
|
||||
## Ask for help
|
||||
|
||||
Gophers Slack has active editor-specific channels like [#emacs](https://gophers.slack.com/archives/C0HKHULEM), [#vim](https://gophers.slack.com/archives/C07GBR52P), and [#vscode](https://gophers.slack.com/archives/C2B4L99RS) that can help debug further. If you're confident the problem is with `gopls`, you can go straight to [#gopls](https://gophers.slack.com/archives/CJZH85XCZ). Invites are [available to everyone](https://invite.slack.golangbridge.org). Come prepared with a short description of the issue, and try to be available to answer questions for a while afterward.
|
||||
|
||||
## File an issue
|
||||
|
||||
We can't diagnose a problem from just a description. When filing an issue, please include as much as possible of the following information:
|
||||
|
||||
1. Your editor and any settings you have configured (for example, your VSCode `settings.json` file).
|
||||
1. A sample program that reproduces the issue, if possible.
|
||||
1. The output of `gopls version` on the command line.
|
||||
1. A complete gopls log file from a session where the issue occurred. It should have a `go env for <workspace folder>` log line near the beginning. It's also helpful to tell us the timestamp the problem occurred, so we can find it the log. See the [instructions](#capture-logs) for information on how to capture gopls logs.
|
||||
|
||||
Your editor may have a command that fills out some of the necessary information, such as `:GoReportGitHubIssue` in `vim-go`. Otherwise, you can use `gopls bug` on the command line. If neither of those work you can start from scratch directly on the [Go issue tracker](https://github.com/golang/go/issues/new?title=x%2Ftools%2Fgopls%3A%20%3Cfill%20this%20in%3E).
|
||||
|
||||
## Capture logs
|
||||
|
||||
You may have to change your editor's configuration to pass a `-logfile` flag to gopls.
|
||||
|
||||
To increase the level of detail in your logs, start `gopls` with the `-rpc.trace` flag. To start a debug server that will allow you to see profiles and memory usage, start `gopls` with `serve --debug=localhost:6060`. You will then be able to view debug information by navigating to `localhost:6060`.
|
||||
|
||||
If you are unsure of how to pass a flag to `gopls` through your editor, please see the [documentation for your editor](../README.md#editors).
|
||||
|
||||
## Debug memory usage
|
||||
|
||||
`gopls` automatically writes out memory debug information when your usage exceeds 1GB. This information can be found in your temporary directory with names like `gopls.1234-5GiB-withnames.zip`. On Windows, your temporary directory will be located at `%TMP%`, and on Unixes, it will be `$TMPDIR`, which is usually `/tmp`. Please [file an issue](#file-an-issue) with this memory debug information attached. If you are uncomfortable sharing the package names of your code, you can share the `-nonames` zip instead, but it's much less useful.
|
||||
@@ -0,0 +1,234 @@
|
||||
# Vim / Neovim
|
||||
|
||||
* [vim-go](#vimgo)
|
||||
* [LanguageClient-neovim](#lcneovim)
|
||||
* [Ale](#ale)
|
||||
* [vim-lsp](#vimlsp)
|
||||
* [vim-lsc](#vimlsc)
|
||||
* [coc.nvim](#cocnvim)
|
||||
* [govim](#govim)
|
||||
* [Neovim v0.5.0+](#neovim)
|
||||
* [Installation](#neovim-install)
|
||||
* [Custom Configuration](#neovim-config)
|
||||
* [Imports](#neovim-imports)
|
||||
* [Omnifunc](#neovim-omnifunc)
|
||||
* [Additional Links](#neovim-links)
|
||||
|
||||
## <a href="#vimgo" id="vimgo">vim-go</a>
|
||||
|
||||
Use [vim-go] ver 1.20+, with the following configuration:
|
||||
|
||||
```vim
|
||||
let g:go_def_mode='gopls'
|
||||
let g:go_info_mode='gopls'
|
||||
```
|
||||
|
||||
## <a href="#lcneovim" id="lcneovim">LanguageClient-neovim</a>
|
||||
|
||||
Use [LanguageClient-neovim], with the following configuration:
|
||||
|
||||
```vim
|
||||
" Launch gopls when Go files are in use
|
||||
let g:LanguageClient_serverCommands = {
|
||||
\ 'go': ['gopls']
|
||||
\ }
|
||||
" Run gofmt on save
|
||||
autocmd BufWritePre *.go :call LanguageClient#textDocument_formatting_sync()
|
||||
```
|
||||
|
||||
## <a href="#ale" id="ale">Ale</a>
|
||||
|
||||
Use [ale]:
|
||||
|
||||
```vim
|
||||
let g:ale_linters = {
|
||||
\ 'go': ['gopls'],
|
||||
\}
|
||||
```
|
||||
|
||||
see [this issue][ale-issue-2179]
|
||||
|
||||
## <a href="#vimlsp" id="vimlsp">vim-lsp</a>
|
||||
|
||||
Use [prabirshrestha/vim-lsp], with the following configuration:
|
||||
|
||||
```vim
|
||||
augroup LspGo
|
||||
au!
|
||||
autocmd User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'go-lang',
|
||||
\ 'cmd': {server_info->['gopls']},
|
||||
\ 'whitelist': ['go'],
|
||||
\ })
|
||||
autocmd FileType go setlocal omnifunc=lsp#complete
|
||||
"autocmd FileType go nmap <buffer> gd <plug>(lsp-definition)
|
||||
"autocmd FileType go nmap <buffer> ,n <plug>(lsp-next-error)
|
||||
"autocmd FileType go nmap <buffer> ,p <plug>(lsp-previous-error)
|
||||
augroup END
|
||||
```
|
||||
|
||||
## <a href="#vimlsc" id="vimlsc">vim-lsc</a>
|
||||
|
||||
Use [natebosch/vim-lsc], with the following configuration:
|
||||
|
||||
```vim
|
||||
let g:lsc_server_commands = {
|
||||
\ "go": {
|
||||
\ "command": "gopls serve",
|
||||
\ "log_level": -1,
|
||||
\ "suppress_stderr": v:true,
|
||||
\ },
|
||||
\}
|
||||
```
|
||||
|
||||
The `log_level` and `suppress_stderr` parts are needed to prevent breakage from logging. See
|
||||
issues [#180](https://github.com/natebosch/vim-lsc/issues/180) and
|
||||
[#213](https://github.com/natebosch/vim-lsc/issues/213).
|
||||
|
||||
## <a href="#cocnvim" id="cocnvim">coc.nvim</a>
|
||||
|
||||
Use [coc.nvim], with the following `coc-settings.json` configuration:
|
||||
|
||||
```json
|
||||
"languageserver": {
|
||||
"go": {
|
||||
"command": "gopls",
|
||||
"rootPatterns": ["go.work", "go.mod", ".vim/", ".git/", ".hg/"],
|
||||
"filetypes": ["go"],
|
||||
"initializationOptions": {
|
||||
"usePlaceholders": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you use `go.work` files, you may want to set the
|
||||
`workspace.workspaceFolderCheckCwd` option. This will force coc.nvim to search
|
||||
parent directories for `go.work` files, even if the current open directory has
|
||||
a `go.mod` file. See the
|
||||
[coc.nvim documentation](https://github.com/neoclide/coc.nvim/wiki/Using-workspaceFolders)
|
||||
for more details.
|
||||
|
||||
Other [settings](settings.md) can be added in `initializationOptions` too.
|
||||
|
||||
The `editor.action.organizeImport` code action will auto-format code and add missing imports. To run this automatically on save, add the following line to your `init.vim`:
|
||||
|
||||
```vim
|
||||
autocmd BufWritePre *.go :call CocAction('runCommand', 'editor.action.organizeImport')
|
||||
```
|
||||
|
||||
## <a href="#govim" id="govim">govim</a>
|
||||
|
||||
In vim classic only, use the experimental [`govim`], simply follow the [install steps][govim-install].
|
||||
|
||||
## <a href="#neovim" id="neovim">Neovim v0.5.0+</a>
|
||||
|
||||
To use the new native LSP client in Neovim, make sure you
|
||||
[install][nvim-install] Neovim v.0.5.0+,
|
||||
the `nvim-lspconfig` configuration helper plugin, and check the
|
||||
[`gopls` configuration section][nvim-lspconfig] there.
|
||||
|
||||
### <a href="#neovim-install" id="neovim-install">Installation</a>
|
||||
|
||||
You can use Neovim's native plugin system. On a Unix system, you can do that by
|
||||
cloning the `nvim-lspconfig` repository into the correct directory:
|
||||
|
||||
```sh
|
||||
dir="${HOME}/.local/share/nvim/site/pack/nvim-lspconfig/opt/nvim-lspconfig/"
|
||||
mkdir -p "$dir"
|
||||
cd "$dir"
|
||||
git clone 'https://github.com/neovim/nvim-lspconfig.git' .
|
||||
```
|
||||
|
||||
### <a href="#neovim-config" id="neovim-config">Configuration</a>
|
||||
|
||||
nvim-lspconfig aims to provide reasonable defaults, so your setup can be very
|
||||
brief.
|
||||
|
||||
```lua
|
||||
local lspconfig = require("lspconfig")
|
||||
lspconfig.gopls.setup({})
|
||||
```
|
||||
|
||||
However, you can also configure `gopls` for your preferences. Here's an
|
||||
example that enables `unusedparams`, `staticcheck`, and `gofumpt`.
|
||||
|
||||
```lua
|
||||
local lspconfig = require("lspconfig")
|
||||
lspconfig.gopls.setup({
|
||||
settings = {
|
||||
gopls = {
|
||||
analyses = {
|
||||
unusedparams = true,
|
||||
},
|
||||
staticcheck = true,
|
||||
gofumpt = true,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### <a href="#neovim-imports" id="neovim-imports">Imports and Formatting</a>
|
||||
|
||||
Use the following configuration to have your imports organized on save using
|
||||
the logic of `goimports` and your code formatted.
|
||||
|
||||
```lua
|
||||
autocmd("BufWritePre", {
|
||||
pattern = "*.go",
|
||||
callback = function()
|
||||
local params = vim.lsp.util.make_range_params()
|
||||
params.context = {only = {"source.organizeImports"}}
|
||||
-- buf_request_sync defaults to a 1000ms timeout. Depending on your
|
||||
-- machine and codebase, you may want longer. Add an additional
|
||||
-- argument after params if you find that you have to write the file
|
||||
-- twice for changes to be saved.
|
||||
-- E.g., vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 3000)
|
||||
local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params)
|
||||
for cid, res in pairs(result or {}) do
|
||||
for _, r in pairs(res.result or {}) do
|
||||
if r.edit then
|
||||
local enc = (vim.lsp.get_client_by_id(cid) or {}).offset_encoding or "utf-16"
|
||||
vim.lsp.util.apply_workspace_edit(r.edit, enc)
|
||||
end
|
||||
end
|
||||
end
|
||||
vim.lsp.buf.format({async = false})
|
||||
end
|
||||
})
|
||||
```
|
||||
|
||||
### <a href="#neovim-omnifunc" id="neovim-omnifunc">Omnifunc</a>
|
||||
|
||||
In Neovim v0.8.1 and later if you don't set the option `omnifunc`, it will auto
|
||||
set to `v:lua.vim.lsp.omnifunc`. If you are using an earlier version, you can
|
||||
configure it manually:
|
||||
|
||||
```lua
|
||||
local on_attach = function(client, bufnr)
|
||||
-- Enable completion triggered by <c-x><c-o>
|
||||
vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
|
||||
end
|
||||
require('lspconfig').gopls.setup({
|
||||
on_attach = on_attach
|
||||
})
|
||||
```
|
||||
|
||||
### <a href="#neovim-links" id="neovim-links">Additional Links</a>
|
||||
|
||||
* [Neovim's official LSP documentation][nvim-docs].
|
||||
|
||||
[vim-go]: https://github.com/fatih/vim-go
|
||||
[LanguageClient-neovim]: https://github.com/autozimu/LanguageClient-neovim
|
||||
[ale]: https://github.com/w0rp/ale
|
||||
[ale-issue-2179]: https://github.com/w0rp/ale/issues/2179
|
||||
[prabirshrestha/vim-lsp]: https://github.com/prabirshrestha/vim-lsp/
|
||||
[natebosch/vim-lsc]: https://github.com/natebosch/vim-lsc/
|
||||
[natebosch/vim-lsc#180]: https://github.com/natebosch/vim-lsc/issues/180
|
||||
[coc.nvim]: https://github.com/neoclide/coc.nvim/
|
||||
[`govim`]: https://github.com/myitcv/govim
|
||||
[govim-install]: https://github.com/myitcv/govim/blob/master/README.md#govim---go-development-plugin-for-vim8
|
||||
[nvim-docs]: https://neovim.io/doc/user/lsp.html
|
||||
[nvim-install]: https://github.com/neovim/neovim/wiki/Installing-Neovim
|
||||
[nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#gopls
|
||||
[nvim-lspconfig-imports]: https://github.com/neovim/nvim-lspconfig/issues/115
|
||||
@@ -0,0 +1,139 @@
|
||||
# Setting up your workspace
|
||||
|
||||
In the language server protocol, a "workspace" consists of a folder along with
|
||||
per-folder configuration. Some LSP clients such as VS Code allow configuring
|
||||
workspaces explicitly, while others do so automatically by looking for special
|
||||
files defining a workspace root (such as a `.git` directory or `go.mod` file).
|
||||
|
||||
In order to function, gopls needs a defined scope in which language features
|
||||
like references, rename, and implementation should operate. Put differently,
|
||||
gopls needs to infer from the LSP workspace which `go build` invocations you
|
||||
would use to build your workspace, including the working directory,
|
||||
environment, and build flags.
|
||||
|
||||
In the past, it could be tricky to set up your workspace so that gopls would
|
||||
infer the correct build information. It required opening the correct directory
|
||||
or using a `go.work` file to tell gopls about the modules you're working on,
|
||||
and configuring the correct operating system and architecture in advance.
|
||||
When this didn't work as expected, gopls would often fail in mysterious
|
||||
ways--the dreaded "No packages found" error.
|
||||
|
||||
Starting with gopls v0.15.0, workspace configuration is much simpler, and gopls
|
||||
will typically work when you open a Go file anywhere in your workspace. If it
|
||||
isn't working for you, or if you want to better understand how gopls models
|
||||
your workspace, please read on.
|
||||
|
||||
## Workspace builds
|
||||
|
||||
Starting with gopls v0.15.0, gopls will guess the builds you are working on
|
||||
based on the set of open files. When you open a file in a workspace folder,
|
||||
gopls checks whether the file is contained in a module, `go.work` workspace, or
|
||||
GOPATH directory, and configures the build accordingly. Additionally, if you
|
||||
open a file that is constrained to a different operating system or
|
||||
architecture, for example opening `foo_windows.go` when working on Linux, gopls
|
||||
will create a scope with `GOOS` and `GOARCH` set to a value that matches the
|
||||
file.
|
||||
|
||||
For example, suppose we had a repository with three modules: `moda`, `modb`,
|
||||
and `modc`, and a `go.work` file using modules `moda` and `modb`. If we open
|
||||
the files `moda/a.go`, `modb/b.go`, `moda/a_windows.go`, and `modc/c.go`, gopls
|
||||
will automatically create three builds:
|
||||
|
||||

|
||||
|
||||
This allows gopls to _just work_ when you open a Go file, but it does come with
|
||||
several caveats:
|
||||
|
||||
- It causes gopls to do more work, since it is now tracking three builds
|
||||
instead of one. However, the recent
|
||||
[scalability redesign](https://go.dev/blog/gopls-scalability)
|
||||
allows much of this work to be avoided through efficient caching.
|
||||
- For operations invoked from a given file, such as "References"
|
||||
or "Implementations", gopls executes the operation in
|
||||
_the default build for that file_. For example, finding references to
|
||||
a symbol `S` from `foo_linux.go` will return references from the Linux build,
|
||||
and finding references to the same symbol `S` from `foo_windows.go` will
|
||||
return references from the Windows build. Gopls searches the default build
|
||||
for the file, but it doesn't search all the other possible builds (even
|
||||
though that would be nice) because it is liable to be too expensive.
|
||||
Issues [#65757](https://go.dev/issue/65757) and
|
||||
[#65755](https://go.dev/issue/65755) propose improvements to this behavior.
|
||||
- When selecting a `GOOS/GOARCH` combination to match a build-constrained file,
|
||||
gopls will choose the first matching combination from
|
||||
[this list](https://cs.opensource.google/go/x/tools/+/master:gopls/internal/cache/port.go;l=30;drc=f872b3d6f05822d290bc7bdd29db090fd9d89f5c).
|
||||
In some cases, that may be surprising.
|
||||
- When working in a `GOOS/GOARCH` constrained file that does not match your
|
||||
default toolchain, `CGO_ENABLED=0` is implicitly set, since a C toolchain for
|
||||
that target is unlikely to be available. This means that gopls will not
|
||||
work in files including `import "C"`. Issue
|
||||
[#65758](https://go.dev/issue/65758) may lead to improvements in this
|
||||
behavior.
|
||||
- Gopls is currently unable to guess build flags that include arbitrary
|
||||
user-defined build constraints, such as a file with the build directive
|
||||
`//go:build mytag`. Issue [#65089](https://go.dev/issue/65089) proposes
|
||||
a heuristic by which gopls could handle this automatically.
|
||||
|
||||
Please provide feedback on this behavior by upvoting or commenting the issues
|
||||
mentioned above, or opening a [new issue](https://go.dev/issue/new) for other
|
||||
improvements you'd like to see.
|
||||
|
||||
## When to use a `go.work` file for development
|
||||
|
||||
Starting with Go 1.18, the `go` command has built-in support for multi-module
|
||||
workspaces specified by [`go.work`](https://go.dev/ref/mod#workspaces) files.
|
||||
Gopls will recognize these files if they are present in your workspace.
|
||||
|
||||
Use a `go.work` file when:
|
||||
|
||||
- you want to work on multiple modules simultaneously in a single logical
|
||||
build, for example if you want changes to one module to be reflected in
|
||||
another.
|
||||
- you want to improve gopls' memory usage or performance by reducing the number
|
||||
of builds it must track.
|
||||
- you want gopls to know which modules you are working on in a multi-module
|
||||
workspace, without opening any files. For example, it may be convenient to use
|
||||
`workspace/symbol` queries before any files are open.
|
||||
- you are using gopls v0.14.2 or earlier, and want to work on multiple
|
||||
modules.
|
||||
|
||||
For example, suppose this repo is checked out into the `$WORK/tools` directory,
|
||||
and [`x/mod`](https://pkg.go.dev/golang.org/x/mod) is checked out into
|
||||
`$WORK/mod`, and you are working on a new `x/mod` API for editing `go.mod`
|
||||
files that you want to simultaneously integrate into gopls.
|
||||
|
||||
You can work on both `golang.org/x/tools/gopls` and `golang.org/x/mod`
|
||||
simultaneously by creating a `go.work` file:
|
||||
|
||||
```sh
|
||||
cd $WORK
|
||||
go work init
|
||||
go work use tools/gopls mod
|
||||
```
|
||||
|
||||
then opening the `$WORK` directory in your editor.
|
||||
|
||||
## When to manually configure `GOOS`, `GOARCH`, or `-tags`
|
||||
|
||||
As described in the first section, gopls v0.15.0 and later will try to
|
||||
configure a new build scope automatically when you open a file that doesn't
|
||||
match the system default operating system (`GOOS`) or architecture (`GOARCH`).
|
||||
|
||||
However, per the caveats listed in that section, this automatic behavior comes
|
||||
with limitations. Customize your gopls environment by setting `GOOS` or
|
||||
`GOARCH` in your
|
||||
[`"build.env"`](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#env)
|
||||
or `-tags=...` in your"
|
||||
["build.buildFlags"](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#buildflags)
|
||||
when:
|
||||
|
||||
- You want to modify the default build environment.
|
||||
- Gopls is not guessing the `GOOS/GOARCH` combination you want to use for
|
||||
cross platform development.
|
||||
- You need to work on a file that is constrained by a user-defined build tags,
|
||||
such as the build directive `//go:build mytag`.
|
||||
|
||||
## GOPATH mode
|
||||
|
||||
When opening a directory within a `GOPATH` directory, the workspace scope will
|
||||
be just that directory and all directories contained within it. Note that
|
||||
opening a large GOPATH directory can make gopls very slow to start.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Reference in New Issue
Block a user