internal/lsp: add document link handling for import paths to godoc
Change-Id: Ib2eef50047dfcc64110c264e77d648f959613b88
Reviewed-on: https://go-review.googlesource.com/c/tools/+/173698
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cmd/cmd_test.go b/internal/lsp/cmd/cmd_test.go
index 97eb94f..f91f359 100644
--- a/internal/lsp/cmd/cmd_test.go
+++ b/internal/lsp/cmd/cmd_test.go
@@ -57,6 +57,10 @@
//TODO: add command line signature tests when it works
}
+func (r *runner) Link(t *testing.T, data tests.Links) {
+ //TODO: add command line link tests when it works
+}
+
func captureStdOut(t testing.TB, f func()) string {
r, out, err := os.Pipe()
if err != nil {
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index b3b4840..ade4cf5 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -84,6 +84,7 @@
DocumentSymbolProvider: true,
HoverProvider: true,
DocumentHighlightProvider: true,
+ DocumentLinkProvider: &protocol.DocumentLinkOptions{},
SignatureHelpProvider: &protocol.SignatureHelpOptions{
TriggerCharacters: []string{"(", ","},
},
diff --git a/internal/lsp/link.go b/internal/lsp/link.go
new file mode 100644
index 0000000..33e9188
--- /dev/null
+++ b/internal/lsp/link.go
@@ -0,0 +1,45 @@
+// Copyright 2018 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 lsp
+
+import (
+ "context"
+ "strconv"
+
+ "golang.org/x/tools/internal/lsp/protocol"
+ "golang.org/x/tools/internal/span"
+)
+
+func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
+ uri := span.NewURI(params.TextDocument.URI)
+ view := s.findView(ctx, uri)
+ f, m, err := newColumnMap(ctx, view, uri)
+ if err != nil {
+ return nil, err
+ }
+ // find the import block
+ ast := f.GetAST(ctx)
+ var result []protocol.DocumentLink
+ for _, imp := range ast.Imports {
+ spn, err := span.NewRange(f.GetFileSet(ctx), imp.Pos(), imp.End()).Span()
+ if err != nil {
+ return nil, err
+ }
+ rng, err := m.Range(spn)
+ if err != nil {
+ return nil, err
+ }
+ target, err := strconv.Unquote(imp.Path.Value)
+ if err != nil {
+ continue
+ }
+ target = "https://godoc.org/" + target
+ result = append(result, protocol.DocumentLink{
+ Range: rng,
+ Target: target,
+ })
+ }
+ return result, nil
+}
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index f51dcb0..a1498be 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -536,6 +536,41 @@
return ""
}
+func (r *runner) Link(t *testing.T, data tests.Links) {
+ for uri, wantLinks := range data {
+ m := r.mapper(uri)
+ gotLinks, err := r.server.DocumentLink(context.Background(), &protocol.DocumentLinkParams{
+ TextDocument: protocol.TextDocumentIdentifier{
+ URI: protocol.NewURI(uri),
+ },
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ links := make(map[span.Span]string, len(wantLinks))
+ for _, link := range wantLinks {
+ links[link.Src] = link.Target
+ }
+ for _, link := range gotLinks {
+ spn, err := m.RangeSpan(link.Range)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if target, ok := links[spn]; ok {
+ delete(links, spn)
+ if target != link.Target {
+ t.Errorf("for %v want %v, got %v\n", spn, link.Target, target)
+ }
+ } else {
+ t.Errorf("unexpected link %v:%v\n", spn, link.Target)
+ }
+ }
+ for spn, target := range links {
+ t.Errorf("missing link %v:%v\n", spn, target)
+ }
+ }
+}
+
func (r *runner) mapper(uri span.URI) *protocol.ColumnMapper {
fname, err := uri.Filename()
if err != nil {
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 63533e8..d94bd93 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -210,8 +210,8 @@
return nil, notImplemented("ResolveCodeLens")
}
-func (s *Server) DocumentLink(context.Context, *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
- return nil, nil // ignore
+func (s *Server) DocumentLink(ctx context.Context, params *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
+ return s.documentLink(ctx, params)
}
func (s *Server) ResolveDocumentLink(context.Context, *protocol.DocumentLink) (*protocol.DocumentLink, error) {
diff --git a/internal/lsp/testdata/links/links.go b/internal/lsp/testdata/links/links.go
new file mode 100644
index 0000000..b97da74
--- /dev/null
+++ b/internal/lsp/testdata/links/links.go
@@ -0,0 +1,12 @@
+package links
+
+import (
+ "fmt" //@link(re`".*"`,"https://godoc.org/fmt")
+
+ "golang.org/x/tools/internal/lsp/foo" //@link(re`".*"`,"https://godoc.org/golang.org/x/tools/internal/lsp/foo")
+)
+
+var (
+ _ fmt.Formatter
+ _ foo.StructFoo
+)
diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go
index 4a0363b..69fafca 100644
--- a/internal/lsp/tests/tests.go
+++ b/internal/lsp/tests/tests.go
@@ -36,6 +36,7 @@
ExpectedSymbolsCount = 1
ExpectedSignaturesCount = 19
ExpectedCompletionSnippetCount = 9
+ ExpectedLinksCount = 2
)
const (
@@ -57,6 +58,7 @@
type Symbols map[span.URI][]source.Symbol
type SymbolsChildren map[string][]source.Symbol
type Signatures map[span.Span]source.SignatureInformation
+type Links map[span.URI][]Link
type Data struct {
Config packages.Config
@@ -71,6 +73,7 @@
Symbols Symbols
symbolsChildren SymbolsChildren
Signatures Signatures
+ Links Links
t testing.TB
fragments map[string]string
@@ -85,6 +88,7 @@
Highlight(*testing.T, Highlights)
Symbol(*testing.T, Symbols)
Signature(*testing.T, Signatures)
+ Link(*testing.T, Links)
}
type Definition struct {
@@ -101,6 +105,11 @@
PlaceholderSnippet string
}
+type Link struct {
+ Src span.Span
+ Target string
+}
+
func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
t.Helper()
@@ -114,6 +123,7 @@
Symbols: make(Symbols),
symbolsChildren: make(SymbolsChildren),
Signatures: make(Signatures),
+ Links: make(Links),
t: t,
dir: dir,
@@ -180,6 +190,7 @@
"symbol": data.collectSymbols,
"signature": data.collectSignatures,
"snippet": data.collectCompletionSnippets,
+ "link": data.collectLinks,
}); err != nil {
t.Fatal(err)
}
@@ -264,6 +275,18 @@
}
tests.Signature(t, data.Signatures)
})
+
+ t.Run("Links", func(t *testing.T) {
+ t.Helper()
+ linksCount := 0
+ for _, want := range data.Links {
+ linksCount += len(want)
+ }
+ if linksCount != ExpectedLinksCount {
+ t.Errorf("got %v links expected %v", linksCount, ExpectedLinksCount)
+ }
+ tests.Link(t, data.Links)
+ })
}
func (data *Data) Golden(tag string, target string, update func(golden string) error) []byte {
@@ -379,3 +402,11 @@
PlaceholderSnippet: placeholder,
}
}
+
+func (data *Data) collectLinks(spn span.Span, link string) {
+ uri := spn.URI()
+ data.Links[uri] = append(data.Links[uri], Link{
+ Src: spn,
+ Target: link,
+ })
+}