1
0
mirror of https://github.com/golang/go synced 2024-11-18 10:54:40 -07:00

internal/lsp: add an importShortcut configuration

This configuration deals with the fact that VS Code has the same
shortcut for go-to-definition and clicking a link. Many users don't like
the behavior of this shortcut triggering both requests, so we allow
users to choose between each behavior.

Fixes golang/go#39065

Change-Id: I760c99c1fade4d157515d3b0fd647daf0c8314eb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242457
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Rebecca Stambler 2020-07-13 23:18:12 -04:00
parent 4cea89713c
commit ddb87c8c46
3 changed files with 64 additions and 25 deletions

View File

@ -130,28 +130,31 @@ func goLinks(ctx context.Context, view source.View, fh source.FileHandle) ([]pro
return true return true
}) })
var links []protocol.DocumentLink var links []protocol.DocumentLink
for _, imp := range imports { // For import specs, provide a link to a documentation website, like
// For import specs, provide a link to a documentation website, like https://pkg.go.dev. // https://pkg.go.dev.
target, err := strconv.Unquote(imp.Path.Value) if view.Options().ImportShortcut.ShowLinks() {
if err != nil { for _, imp := range imports {
continue target, err := strconv.Unquote(imp.Path.Value)
if err != nil {
continue
}
// See golang/go#36998: don't link to modules matching GOPRIVATE.
if view.IsGoPrivatePath(target) {
continue
}
if mod, version, ok := moduleAtVersion(ctx, target, ph); ok && strings.ToLower(view.Options().LinkTarget) == "pkg.go.dev" {
target = strings.Replace(target, mod, mod+"@"+version, 1)
}
// Account for the quotation marks in the positions.
start := imp.Path.Pos() + 1
end := imp.Path.End() - 1
target = fmt.Sprintf("https://%s/%s", view.Options().LinkTarget, target)
l, err := toProtocolLink(view, m, target, start, end, source.Go)
if err != nil {
return nil, err
}
links = append(links, l)
} }
// See golang/go#36998: don't link to modules matching GOPRIVATE.
if view.IsGoPrivatePath(target) {
continue
}
if mod, version, ok := moduleAtVersion(ctx, target, ph); ok && strings.ToLower(view.Options().LinkTarget) == "pkg.go.dev" {
target = strings.Replace(target, mod, mod+"@"+version, 1)
}
// Account for the quotation marks in the positions.
start := imp.Path.Pos() + 1
end := imp.Path.End() - 1
target = fmt.Sprintf("https://%s/%s", view.Options().LinkTarget, target)
l, err := toProtocolLink(view, m, target, start, end, source.Go)
if err != nil {
return nil, err
}
links = append(links, l)
} }
for _, s := range str { for _, s := range str {
l, err := findLinksInString(ctx, view, s.Value, s.Pos(), m, source.Go) l, err := findLinksInString(ctx, view, s.Value, s.Pos(), m, source.Go)

View File

@ -73,9 +73,12 @@ func Identifier(ctx context.Context, snapshot Snapshot, fh FileHandle, pos proto
var ErrNoIdentFound = errors.New("no identifier found") var ErrNoIdentFound = errors.New("no identifier found")
func findIdentifier(ctx context.Context, s Snapshot, pkg Package, file *ast.File, pos token.Pos) (*IdentifierInfo, error) { func findIdentifier(ctx context.Context, s Snapshot, pkg Package, file *ast.File, pos token.Pos) (*IdentifierInfo, error) {
// Handle import specs separately, as there is no formal position for a package declaration. // Handle import specs separately, as there is no formal position for a
if result, err := importSpec(s, pkg, file, pos); result != nil || err != nil { // package declaration.
return result, err if s.View().Options().ImportShortcut.ShowDefinition() {
if result, err := importSpec(s, pkg, file, pos); result != nil || err != nil {
return result, err
}
} }
path := pathEnclosingObjNode(file, pos) path := pathEnclosingObjNode(file, pos)
if path == nil { if path == nil {

View File

@ -206,9 +206,14 @@ type UserOptions struct {
// StaticCheck enables additional analyses from staticcheck.io. // StaticCheck enables additional analyses from staticcheck.io.
StaticCheck bool StaticCheck bool
// LinkTarget is the website used for documentation. // LinkTarget is the website used for documentation. If empty, no link is
// provided.
LinkTarget string LinkTarget string
// ImportShortcut specifies whether import statements should link to
// documentation or go to definitions. The default is both.
ImportShortcut ImportShortcut
// LocalPrefix is used to specify goimports's -local behavior. // LocalPrefix is used to specify goimports's -local behavior.
LocalPrefix string LocalPrefix string
@ -241,6 +246,22 @@ type UserOptions struct {
Gofumpt bool Gofumpt bool
} }
type ImportShortcut int
const (
Both ImportShortcut = iota
Link
Definition
)
func (s ImportShortcut) ShowLinks() bool {
return s == Both || s == Link
}
func (s ImportShortcut) ShowDefinition() bool {
return s == Both || s == Definition
}
type completionOptions struct { type completionOptions struct {
deepCompletion bool deepCompletion bool
unimported bool unimported bool
@ -505,6 +526,18 @@ func (o *Options) set(name string, value interface{}) OptionResult {
case "linkTarget": case "linkTarget":
result.setString(&o.LinkTarget) result.setString(&o.LinkTarget)
case "importShortcut":
var s string
result.setString(&s)
switch s {
case "both":
o.ImportShortcut = Both
case "link":
o.ImportShortcut = Link
case "definition":
o.ImportShortcut = Definition
}
case "analyses": case "analyses":
result.setBoolMap(&o.UserEnabledAnalyses) result.setBoolMap(&o.UserEnabledAnalyses)