Skip to content

Commit

Permalink
Go to definition/declaration (#569)
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko authored Jul 5, 2021
1 parent f98515e commit b078064
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 0 deletions.
68 changes: 68 additions & 0 deletions internal/langserver/handlers/go_to_ref_target.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package handlers

import (
"context"

lsctx "github.com/hashicorp/terraform-ls/internal/context"
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
)

func (h *logHandler) GoToReferenceTarget(ctx context.Context, params lsp.TextDocumentPositionParams) (interface{}, error) {
cc, err := lsctx.ClientCapabilities(ctx)
if err != nil {
return nil, err
}

fs, err := lsctx.DocumentStorage(ctx)
if err != nil {
return nil, err
}

mf, err := lsctx.ModuleFinder(ctx)
if err != nil {
return nil, err
}

file, err := fs.GetDocument(ilsp.FileHandlerFromDocumentURI(params.TextDocument.URI))
if err != nil {
return nil, err
}

mod, err := mf.ModuleByPath(file.Dir())
if err != nil {
return nil, err
}

schema, err := schemaForDocument(mf, file)
if err != nil {
return nil, err
}

d, err := decoderForDocument(ctx, mod, file.LanguageID())
if err != nil {
return nil, err
}
d.SetSchema(schema)

fPos, err := ilsp.FilePositionFromDocumentPosition(params, file)
if err != nil {
return nil, err
}

h.logger.Printf("Looking for ref origin at %q -> %#v", file.Filename(), fPos.Position())
origin, err := d.ReferenceOriginAtPos(file.Filename(), fPos.Position())
if err != nil {
return nil, err
}
if origin == nil {
return nil, nil
}

target, err := d.ReferenceTargetForOrigin(*origin)
if err != nil {
return nil, err
}

return ilsp.ReferenceToLocationLink(mod.Path, *origin, target, cc.TextDocument.Declaration.LinkSupport), nil
}
197 changes: 197 additions & 0 deletions internal/langserver/handlers/go_to_ref_target_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package handlers

import (
"fmt"
"testing"

"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-ls/internal/langserver"
"github.com/hashicorp/terraform-ls/internal/terraform/exec"
"github.com/stretchr/testify/mock"
)

func TestDefinition(t *testing.T) {
tmpDir := TempDir(t)

ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{
TerraformCalls: &exec.TerraformMockCalls{
PerWorkDir: map[string][]*mock.Call{
tmpDir.Dir(): {
{
Method: "Version",
Repeatability: 1,
Arguments: []interface{}{
mock.AnythingOfType(""),
},
ReturnArguments: []interface{}{
version.Must(version.NewVersion("0.12.0")),
nil,
nil,
},
},
{
Method: "GetExecPath",
Repeatability: 1,
ReturnArguments: []interface{}{
"",
},
},
},
},
},
}))
stop := ls.Start(t)
defer stop()

ls.Call(t, &langserver.CallRequest{
Method: "initialize",
ReqParams: fmt.Sprintf(`{
"capabilities": {
"definition": {
"linkSupport": true
}
},
"rootUri": %q,
"processId": 12345
}`, tmpDir.URI())})
ls.Notify(t, &langserver.CallRequest{
Method: "initialized",
ReqParams: "{}",
})
ls.Call(t, &langserver.CallRequest{
Method: "textDocument/didOpen",
ReqParams: fmt.Sprintf(`{
"textDocument": {
"version": 0,
"languageId": "terraform",
"text": `+fmt.Sprintf("%q",
`variable "test" {
}
output "foo" {
value = var.test
}`)+`,
"uri": "%s/main.tf"
}
}`, tmpDir.URI())})
ls.CallAndExpectResponse(t, &langserver.CallRequest{
Method: "textDocument/definition",
ReqParams: fmt.Sprintf(`{
"textDocument": {
"uri": "%s/main.tf"
},
"position": {
"line": 4,
"character": 13
}
}`, tmpDir.URI())}, fmt.Sprintf(`{
"jsonrpc": "2.0",
"id": 3,
"result": {
"uri":"%s/main.tf",
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 1,
"character": 1
}
}
}
}`, tmpDir.URI()))
}

func TestDeclaration(t *testing.T) {
tmpDir := TempDir(t)

ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{
TerraformCalls: &exec.TerraformMockCalls{
PerWorkDir: map[string][]*mock.Call{
tmpDir.Dir(): {
{
Method: "Version",
Repeatability: 1,
Arguments: []interface{}{
mock.AnythingOfType(""),
},
ReturnArguments: []interface{}{
version.Must(version.NewVersion("0.12.0")),
nil,
nil,
},
},
{
Method: "GetExecPath",
Repeatability: 1,
ReturnArguments: []interface{}{
"",
},
},
},
},
},
}))
stop := ls.Start(t)
defer stop()

ls.Call(t, &langserver.CallRequest{
Method: "initialize",
ReqParams: fmt.Sprintf(`{
"capabilities": {
"definition": {
"linkSupport": true
}
},
"rootUri": %q,
"processId": 12345
}`, tmpDir.URI())})
ls.Notify(t, &langserver.CallRequest{
Method: "initialized",
ReqParams: "{}",
})
ls.Call(t, &langserver.CallRequest{
Method: "textDocument/didOpen",
ReqParams: fmt.Sprintf(`{
"textDocument": {
"version": 0,
"languageId": "terraform",
"text": `+fmt.Sprintf("%q",
`variable "test" {
}
output "foo" {
value = var.test
}`)+`,
"uri": "%s/main.tf"
}
}`, tmpDir.URI())})
ls.CallAndExpectResponse(t, &langserver.CallRequest{
Method: "textDocument/declaration",
ReqParams: fmt.Sprintf(`{
"textDocument": {
"uri": "%s/main.tf"
},
"position": {
"line": 4,
"character": 13
}
}`, tmpDir.URI())}, fmt.Sprintf(`{
"jsonrpc": "2.0",
"id": 3,
"result": {
"uri":"%s/main.tf",
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 1,
"character": 1
}
}
}
}`, tmpDir.URI()))
}
2 changes: 2 additions & 0 deletions internal/langserver/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ func initializeResponse(t *testing.T, commandPrefix string) string {
},
"hoverProvider": true,
"signatureHelpProvider": {},
"declarationProvider": {},
"definitionProvider": true,
"documentSymbolProvider": true,
"codeLensProvider": {},
"documentLinkProvider": {},
Expand Down
2 changes: 2 additions & 0 deletions internal/langserver/handlers/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ func (lh *logHandler) Initialize(ctx context.Context, params lsp.InitializeParam
ResolveProvider: false,
TriggerCharacters: []string{".", "["},
},
DeclarationProvider: lsp.DeclarationOptions{},
DefinitionProvider: true,
CodeLensProvider: lsp.CodeLensOptions{},
HoverProvider: true,
DocumentFormattingProvider: true,
Expand Down
24 changes: 24 additions & 0 deletions internal/langserver/handlers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,30 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {

return handle(ctx, req, lh.TextDocumentLink)
},
"textDocument/declaration": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
err := session.CheckInitializationIsConfirmed()
if err != nil {
return nil, err
}

ctx = lsctx.WithDocumentStorage(ctx, svc.fs)
ctx = lsctx.WithClientCapabilities(ctx, cc)
ctx = lsctx.WithModuleFinder(ctx, svc.modMgr)

return handle(ctx, req, lh.GoToReferenceTarget)
},
"textDocument/definition": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
err := session.CheckInitializationIsConfirmed()
if err != nil {
return nil, err
}

ctx = lsctx.WithDocumentStorage(ctx, svc.fs)
ctx = lsctx.WithClientCapabilities(ctx, cc)
ctx = lsctx.WithModuleFinder(ctx, svc.modMgr)

return handle(ctx, req, lh.GoToReferenceTarget)
},
"textDocument/completion": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
err := session.CheckInitializationIsConfirmed()
if err != nil {
Expand Down
File renamed without changes.
33 changes: 33 additions & 0 deletions internal/lsp/location_links.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package lsp

import (
"path/filepath"

"github.com/hashicorp/hcl-lang/lang"
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
"github.com/hashicorp/terraform-ls/internal/uri"
)

func ReferenceToLocationLink(targetModPath string, origin lang.ReferenceOrigin,
target *lang.ReferenceTarget, linkSupport bool) interface{} {

if target == nil || target.RangePtr == nil {
return nil
}

targetUri := uri.FromPath(filepath.Join(targetModPath, target.RangePtr.Filename))

if linkSupport {
return lsp.LocationLink{
OriginSelectionRange: HCLRangeToLSP(origin.Range),
TargetURI: lsp.DocumentURI(targetUri),
TargetRange: HCLRangeToLSP(*target.RangePtr),
TargetSelectionRange: HCLRangeToLSP(*target.RangePtr),
}
}

return lsp.Location{
URI: lsp.DocumentURI(targetUri),
Range: HCLRangeToLSP(*target.RangePtr),
}
}

0 comments on commit b078064

Please sign in to comment.