mirror of
https://github.com/golang/go
synced 2024-11-18 09:04:49 -07:00
internal/lsp/fake: define Symbol method on Editor
In preparation for later changes to the implementation of the workspace Symbol method, we add the Symbol method to fake.Editor. This requires the definition of a number of associated fake types (editor-friendly, byte-offset-based versions of protocol UTF16-based types) for example fake.SymbolInformation and the types it references. We also implement a basic regtest for the Symbol method, exposing Symbol on regtest.Env like other LSP server methods. To aid with the writing of Symbol result assertions, we provide some helper functions to simplify the process of defining matches that are evaluated against the result set. Change-Id: If73b493e1e791c8201423a303af8041f5a15ccfc Reviewed-on: https://go-review.googlesource.com/c/tools/+/228121 Run-TryBot: Paul Jolly <paul@myitcv.org.uk> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
ca5866bcf9
commit
0bd3dbed90
@ -18,6 +18,13 @@ type Pos struct {
|
||||
Line, Column int
|
||||
}
|
||||
|
||||
// Range corresponds to protocol.Range, but uses the editor friend Pos
|
||||
// instead of UTF-16 oriented protocol.Position
|
||||
type Range struct {
|
||||
Start Pos
|
||||
End Pos
|
||||
}
|
||||
|
||||
func (p Pos) toProtocolPosition() protocol.Position {
|
||||
return protocol.Position{
|
||||
Line: float64(p.Line),
|
||||
@ -38,6 +45,21 @@ type Edit struct {
|
||||
Text string
|
||||
}
|
||||
|
||||
// Location is the editor friendly equivalent of protocol.Location
|
||||
type Location struct {
|
||||
Path string
|
||||
Range Range
|
||||
}
|
||||
|
||||
// SymbolInformation is an editor friendly version of
|
||||
// protocol.SymbolInformation, with location information transformed to byte
|
||||
// offsets. Field names correspond to the protocol type.
|
||||
type SymbolInformation struct {
|
||||
Name string
|
||||
Kind protocol.SymbolKind
|
||||
Location Location
|
||||
}
|
||||
|
||||
// NewEdit creates an edit replacing all content between
|
||||
// (startLine, startColumn) and (endLine, endColumn) with text.
|
||||
func NewEdit(startLine, startColumn, endLine, endColumn int, text string) Edit {
|
||||
|
@ -526,6 +526,38 @@ func (e *Editor) GoToDefinition(ctx context.Context, path string, pos Pos) (stri
|
||||
return newPath, newPos, nil
|
||||
}
|
||||
|
||||
// Symbol performs a workspace symbol search using query
|
||||
func (e *Editor) Symbol(ctx context.Context, query string) ([]SymbolInformation, error) {
|
||||
params := &protocol.WorkspaceSymbolParams{}
|
||||
params.Query = query
|
||||
|
||||
resp, err := e.server.Symbol(ctx, params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("symbol: %w", err)
|
||||
}
|
||||
var res []SymbolInformation
|
||||
for _, si := range resp {
|
||||
ploc := si.Location
|
||||
path := e.sandbox.Workdir.URIToPath(ploc.URI)
|
||||
start := fromProtocolPosition(ploc.Range.Start)
|
||||
end := fromProtocolPosition(ploc.Range.End)
|
||||
rnge := Range{
|
||||
Start: start,
|
||||
End: end,
|
||||
}
|
||||
loc := Location{
|
||||
Path: path,
|
||||
Range: rnge,
|
||||
}
|
||||
res = append(res, SymbolInformation{
|
||||
Name: si.Name,
|
||||
Kind: si.Kind,
|
||||
Location: loc,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// OrganizeImports requests and performs the source.organizeImports codeAction.
|
||||
func (e *Editor) OrganizeImports(ctx context.Context, path string) error {
|
||||
return e.codeAction(ctx, path, nil, protocol.SourceOrganizeImports)
|
||||
|
114
internal/lsp/regtest/symbol_helper_test.go
Normal file
114
internal/lsp/regtest/symbol_helper_test.go
Normal file
@ -0,0 +1,114 @@
|
||||
// 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 regtest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/fake"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
)
|
||||
|
||||
// expSymbolInformation and the types it references are pointer-based versions
|
||||
// of fake.SymbolInformation, used to make it easier to partially assert
|
||||
// against values of type fake.SymbolInformation
|
||||
|
||||
// expSymbolInformation is a pointer-based version of fake.SymbolInformation
|
||||
type expSymbolInformation struct {
|
||||
Name *string
|
||||
Kind *protocol.SymbolKind
|
||||
Location *expLocation
|
||||
}
|
||||
|
||||
func (e *expSymbolInformation) matchAgainst(sis []fake.SymbolInformation) bool {
|
||||
for _, si := range sis {
|
||||
if e.match(si) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *expSymbolInformation) match(si fake.SymbolInformation) bool {
|
||||
if e.Name != nil && *e.Name != si.Name {
|
||||
return false
|
||||
}
|
||||
if e.Kind != nil && *e.Kind != si.Kind {
|
||||
return false
|
||||
}
|
||||
if e.Location != nil && !e.Location.match(si.Location) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *expSymbolInformation) String() string {
|
||||
byts, err := json.MarshalIndent(e, "", " ")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to json.Marshal *expSymbolInformation: %v", err))
|
||||
}
|
||||
return string(byts)
|
||||
}
|
||||
|
||||
// expLocation is a pointer-based version of fake.Location
|
||||
type expLocation struct {
|
||||
Path *string
|
||||
Range *expRange
|
||||
}
|
||||
|
||||
func (e *expLocation) match(l fake.Location) bool {
|
||||
if e.Path != nil && *e.Path != l.Path {
|
||||
return false
|
||||
}
|
||||
if e.Range != nil && !e.Range.match(l.Range) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// expRange is a pointer-based version of fake.Range
|
||||
type expRange struct {
|
||||
Start *expPos
|
||||
End *expPos
|
||||
}
|
||||
|
||||
func (e *expRange) match(l fake.Range) bool {
|
||||
if e.Start != nil && !e.Start.match(l.Start) {
|
||||
return false
|
||||
}
|
||||
if e.End != nil && !e.End.match(l.End) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// expPos is a pointer-based version of fake.Pos
|
||||
type expPos struct {
|
||||
Line *int
|
||||
Column *int
|
||||
}
|
||||
|
||||
func (e *expPos) match(l fake.Pos) bool {
|
||||
if e.Line != nil && *e.Line != l.Line {
|
||||
return false
|
||||
}
|
||||
if e.Column != nil && *e.Column != l.Column {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func pString(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func pInt(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
func pKind(k protocol.SymbolKind) *protocol.SymbolKind {
|
||||
return &k
|
||||
}
|
58
internal/lsp/regtest/symbol_test.go
Normal file
58
internal/lsp/regtest/symbol_test.go
Normal file
@ -0,0 +1,58 @@
|
||||
// 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 regtest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/fake"
|
||||
)
|
||||
|
||||
const symbolSetup = `
|
||||
-- go.mod --
|
||||
module mod.com
|
||||
|
||||
go 1.12
|
||||
-- main.go --
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println(Message)
|
||||
}
|
||||
-- const.go --
|
||||
package main
|
||||
|
||||
const Message = "Hello World."
|
||||
`
|
||||
|
||||
// TestSymbolPos tests that, at a basic level, we get the correct position
|
||||
// information for symbols matches that are returned.
|
||||
func TestSymbolPos(t *testing.T) {
|
||||
matcher := "caseSensitive"
|
||||
opts := []RunOption{
|
||||
WithEditorConfig(fake.EditorConfig{SymbolMatcher: &matcher}),
|
||||
}
|
||||
|
||||
runner.Run(t, symbolSetup, func(t *testing.T, env *Env) {
|
||||
res := env.Symbol("main")
|
||||
exp := &expSymbolInformation{
|
||||
Name: pString("main"),
|
||||
Location: &expLocation{
|
||||
Path: pString("main.go"),
|
||||
Range: &expRange{
|
||||
Start: &expPos{
|
||||
Line: pInt(4),
|
||||
Column: pInt(5),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !exp.matchAgainst(res) {
|
||||
t.Fatalf("failed to find match for main function")
|
||||
}
|
||||
}, opts...)
|
||||
}
|
@ -106,6 +106,16 @@ func (e *Env) GoToDefinition(name string, pos fake.Pos) (string, fake.Pos) {
|
||||
return n, p
|
||||
}
|
||||
|
||||
// Symbol returns symbols matching query
|
||||
func (e *Env) Symbol(query string) []fake.SymbolInformation {
|
||||
e.T.Helper()
|
||||
r, err := e.Editor.Symbol(e.Ctx, query)
|
||||
if err != nil {
|
||||
e.T.Fatal(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// FormatBuffer formats the editor buffer, calling t.Fatal on any error.
|
||||
func (e *Env) FormatBuffer(name string) {
|
||||
e.T.Helper()
|
||||
|
Loading…
Reference in New Issue
Block a user