1
0
mirror of https://github.com/golang/go synced 2024-11-19 01:44:40 -07:00
go/internal/lsp/cmd/test/cmdtest.go
Kalman Bekesi b8f202ca5e tools/gopls: add command line support for links
This adds support for calling links from the gopls command line,
e.g.

$ gopls links ~/tmp/foo/main.go

Optional arguments are:
-json, which emits range and uri in JSON
With no arguments, a unique list of links are emitted.

Updates golang/go#32875

Change-Id: I1e7cbf00a636c05ccf21bd544d9a5b7742d5d70b
GitHub-Last-Rev: 7ed1e4612186bce4077d3c73f2407cf6def211d9
GitHub-Pull-Request: golang/tools#181
Reviewed-on: https://go-review.googlesource.com/c/tools/+/203297
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2019-11-01 17:18:15 +00:00

175 lines
5.1 KiB
Go

// Copyright 2019 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 cmdtest contains the test suite for the command line behavior of gopls.
package cmdtest
import (
"bytes"
"context"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"golang.org/x/tools/go/packages/packagestest"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/tests"
"golang.org/x/tools/internal/span"
)
type runner struct {
exporter packagestest.Exporter
data *tests.Data
ctx context.Context
options func(*source.Options)
}
func NewRunner(exporter packagestest.Exporter, data *tests.Data, ctx context.Context, options func(*source.Options)) tests.Tests {
return &runner{
exporter: exporter,
data: data,
ctx: ctx,
options: options,
}
}
func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) FoldingRange(t *testing.T, spn span.Span) {
//TODO: add command line folding range tests when it works
}
func (r *runner) Highlight(t *testing.T, name string, locations []span.Span) {
//TODO: add command line highlight tests when it works
}
func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
//TODO: add command line prepare rename tests when it works
}
func (r *runner) Symbol(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
//TODO: add command line symbol tests when it works
}
func (r *runner) Implementation(t *testing.T, spn span.Span, imp tests.Implementations) {
//TODO: add implements tests when it works
}
func CaptureStdOut(t testing.TB, f func()) string {
r, out, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
old := os.Stdout
defer func() {
os.Stdout = old
out.Close()
r.Close()
}()
os.Stdout = out
f()
out.Close()
data, err := ioutil.ReadAll(r)
if err != nil {
t.Fatal(err)
}
return string(data)
}
// normalizePaths replaces all paths present in s with just the fragment portion
// this is used to make golden files not depend on the temporary paths of the files
func normalizePaths(data *tests.Data, s string) string {
type entry struct {
path string
index int
fragment string
}
match := make([]entry, 0, len(data.Exported.Modules))
// collect the initial state of all the matchers
for _, m := range data.Exported.Modules {
for fragment := range m.Files {
filename := data.Exported.File(m.Name, fragment)
index := strings.Index(s, filename)
if index >= 0 {
match = append(match, entry{filename, index, fragment})
}
if slash := filepath.ToSlash(filename); slash != filename {
index := strings.Index(s, slash)
if index >= 0 {
match = append(match, entry{slash, index, fragment})
}
}
quoted := strconv.Quote(filename)
if escaped := quoted[1 : len(quoted)-1]; escaped != filename {
index := strings.Index(s, escaped)
if index >= 0 {
match = append(match, entry{escaped, index, fragment})
}
}
}
}
// result should be the same or shorter than the input
buf := bytes.NewBuffer(make([]byte, 0, len(s)))
last := 0
for {
// find the nearest path match to the start of the buffer
next := -1
nearest := len(s)
for i, c := range match {
if c.index >= 0 && nearest > c.index {
nearest = c.index
next = i
}
}
// if there are no matches, we copy the rest of the string and are done
if next < 0 {
buf.WriteString(s[last:])
return buf.String()
}
// we have a match
n := &match[next]
// copy up to the start of the match
buf.WriteString(s[last:n.index])
// skip over the filename
last = n.index + len(n.path)
// add in the fragment instead
buf.WriteString(n.fragment)
// see what the next match for this path is
n.index = strings.Index(s[last:], n.path)
if n.index >= 0 {
n.index += last
}
}
}