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

internal/lsp/source: don't link to packages matching GOPRIVATE in hover

Currently, our hover text by default links point to public documentation
sites (e.g. pkg.go.dev). This doesn't make sense for private repos, so
hide the hovertext link when the import path matches GOPRIVATE.

Implementing this was a little messy. To be optimal I had to thread
the value of goprivate through cache.view, and to be correct I had to
duplicate some code from cmd/go internal.

Regtest will follow after https://golang.org/cl/232983 is submitted.

Updates golang/go#36998

Change-Id: I1e556471bf919fea30132d9642426a08fdb7f434
Reviewed-on: https://go-review.googlesource.com/c/tools/+/233524
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Rob Findley 2020-05-12 21:56:33 -04:00 committed by Robert Findley
parent 7d3b6ebf13
commit 8ddc06776e
4 changed files with 74 additions and 9 deletions

View File

@ -13,6 +13,7 @@ import (
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"reflect"
"strings"
@ -108,7 +109,7 @@ type view struct {
goCommand bool
// `go env` variables that need to be tracked.
gopath, gocache string
gopath, gocache, goprivate string
// gocmdRunner guards go command calls from concurrency errors.
gocmdRunner *gocommand.Runner
@ -709,10 +710,11 @@ func isSubdirectory(root, leaf string) bool {
return err == nil && !strings.HasPrefix(rel, "..")
}
// getGoEnv sets the view's build information's GOPATH, GOCACHE, and GOPACKAGESDRIVER values.
// It also returns the view's GOMOD value, which need not be cached.
// getGoEnv sets the view's build information's GOPATH, GOCACHE, GOPRIVATE, and
// GOPACKAGESDRIVER values. It also returns the view's GOMOD value, which need
// not be cached.
func (v *view) getGoEnv(ctx context.Context, env []string) (string, error) {
var gocache, gopath, gopackagesdriver bool
var gocache, gopath, gopackagesdriver, goprivate bool
isGoCommand := func(gopackagesdriver string) bool {
return gopackagesdriver == "" || gopackagesdriver == "off"
}
@ -728,6 +730,9 @@ func (v *view) getGoEnv(ctx context.Context, env []string) (string, error) {
case "GOPATH":
v.gopath = split[1]
gopath = true
case "GOPRIVATE":
v.goprivate = split[1]
goprivate = true
case "GOPACKAGESDRIVER":
v.goCommand = isGoCommand(split[1])
gopackagesdriver = true
@ -762,6 +767,12 @@ func (v *view) getGoEnv(ctx context.Context, env []string) (string, error) {
return "", errors.New("unable to determine GOCACHE")
}
}
if !goprivate {
if goprivate, ok := envMap["GOPRIVATE"]; ok {
v.goprivate = goprivate
}
// No error here: GOPRIVATE is not essential.
}
// The value of GOPACKAGESDRIVER is not returned through the go command.
if !gopackagesdriver {
v.goCommand = isGoCommand(os.Getenv("GOPACKAGESDRIVER"))
@ -770,6 +781,53 @@ func (v *view) getGoEnv(ctx context.Context, env []string) (string, error) {
return gomod, nil
}
return "", nil
}
func (v *view) IsGoPrivatePath(target string) bool {
return globsMatchPath(v.goprivate, target)
}
// Copied from
// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
func globsMatchPath(globs, target string) bool {
for globs != "" {
// Extract next non-empty glob in comma-separated list.
var glob string
if i := strings.Index(globs, ","); i >= 0 {
glob, globs = globs[:i], globs[i+1:]
} else {
glob, globs = globs, ""
}
if glob == "" {
continue
}
// A glob with N+1 path elements (N slashes) needs to be matched
// against the first N+1 path elements of target,
// which end just before the N+1'th slash.
n := strings.Count(glob, "/")
prefix := target
// Walk target, counting slashes, truncating at the N+1'th slash.
for i := 0; i < len(target); i++ {
if target[i] == '/' {
if n == 0 {
prefix = target[:i]
break
}
n--
}
}
if n > 0 {
// Not enough prefix elements.
continue
}
matched, _ := path.Match(glob, prefix)
if matched {
return true
}
}
return false
}
// This function will return the main go.mod file for this folder if it exists and whether the -modfile

View File

@ -58,7 +58,8 @@ func Hover(ctx context.Context, snapshot Snapshot, fh FileHandle, position proto
if err != nil {
return nil, err
}
hover, err := FormatHover(h, snapshot.View().Options())
isPrivate := h.Link != "" && snapshot.View().IsGoPrivatePath(h.Link)
hover, err := FormatHover(h, snapshot.View().Options(), isPrivate)
if err != nil {
return nil, err
}
@ -330,7 +331,7 @@ func formatVar(node ast.Spec, obj types.Object, decl *ast.GenDecl) *HoverInforma
return &HoverInformation{source: obj, comment: decl.Doc}
}
func FormatHover(h *HoverInformation, options Options) (string, error) {
func FormatHover(h *HoverInformation, options Options, isPrivate bool) (string, error) {
signature := h.Signature
if signature != "" && options.PreferredContentFormat == protocol.Markdown {
signature = fmt.Sprintf("```go\n%s\n```", signature)
@ -348,7 +349,10 @@ func FormatHover(h *HoverInformation, options Options) (string, error) {
}
return string(b), nil
}
link := formatLink(h, options)
var link string
if !isPrivate {
link = formatLink(h, options)
}
switch options.HoverKind {
case SynopsisDocumentation:
doc := formatDoc(h.Synopsis, options)
@ -374,7 +378,6 @@ func formatLink(h *HoverInformation, options Options) string {
return plainLink
}
}
func formatDoc(doc string, options Options) string {
if options.PreferredContentFormat == protocol.Markdown {
return CommentToMarkdown(doc)

View File

@ -493,7 +493,7 @@ func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err)
}
hover, err := source.FormatHover(h, r.view.Options())
hover, err := source.FormatHover(h, r.view.Options(), false)
if err != nil {
t.Fatal(err)
}

View File

@ -152,6 +152,10 @@ type View interface {
// user's workspace. In particular, if they are both outside of a module
// and their GOPATH.
ValidBuildConfiguration() bool
// IsGoPrivatePath reports whether target is a private import path, as identified
// by the GOPRIVATE environment variable.
IsGoPrivatePath(path string) bool
}
// Session represents a single connection from a client.