Skip to content

Commit

Permalink
langserver: Implement textDocument/hover
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Nov 12, 2020
1 parent 552c97a commit 1a35102
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 1 deletion.
26 changes: 26 additions & 0 deletions internal/lsp/hover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package lsp

import (
"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/terraform-ls/internal/mdplain"
"github.com/sourcegraph/go-lsp"
)

func HoverData(data *lang.HoverData, cc lsp.TextDocumentClientCapabilities) lsp.Hover {
mdSupported := cc.Hover != nil &&
len(cc.Hover.ContentFormat) > 0 &&
cc.Hover.ContentFormat[0] == "markdown"

value := data.Content.Value
if data.Content.Kind == lang.MarkdownKind && !mdSupported {
value = mdplain.Clean(value)
}

content := lsp.RawMarkedString(value)
rng := HCLRangeToLSP(data.Range)

return lsp.Hover{
Contents: []lsp.MarkedString{content},
Range: &rng,
}
}
1 change: 0 additions & 1 deletion langserver/handlers/complete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ func TestCompletion_withoutInitialization(t *testing.T) {

func TestCompletion_withValidData(t *testing.T) {
tmpDir := TempDir(t)
t.Logf("will init at %s", tmpDir.Dir())
InitPluginCache(t, tmpDir.Dir())

var testSchema tfjson.ProviderSchemas
Expand Down
1 change: 1 addition & 0 deletions langserver/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func initializeResponse(t *testing.T, commandPrefix string) string {
"openClose": true,
"change": 2
},
"hoverProvider": true,
"completionProvider": {},
"documentSymbolProvider":true,
"documentFormattingProvider":true,
Expand Down
62 changes: 62 additions & 0 deletions langserver/handlers/hover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package handlers

import (
"context"

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

func (h *logHandler) TextDocumentHover(ctx context.Context, params lsp.TextDocumentPositionParams) (lsp.Hover, error) {
var data lsp.Hover

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

cc, err := lsctx.ClientCapabilities(ctx)
if err != nil {
return data, err
}

rmf, err := lsctx.RootModuleFinder(ctx)
if err != nil {
return data, err
}

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

rm, err := rmf.RootModuleByPath(file.Dir())
if err != nil {
return data, err
}

schema, err := rmf.SchemaForPath(file.Dir())
if err != nil {
return data, err
}

d, err := rm.DecoderWithSchema(schema)
if err != nil {
return data, err
}

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

h.logger.Printf("Looking for hover data at %q -> %#v", file.Filename(), fPos.Position())
hoverData, err := d.HoverAtPos(file.Filename(), fPos.Position())
h.logger.Printf("received hover data: %#v", data)
if err != nil {
return data, err
}

return ilsp.HoverData(hoverData, cc.TextDocument), nil
}
129 changes: 129 additions & 0 deletions langserver/handlers/hover_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package handlers

import (
"encoding/json"
"fmt"
"testing"

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

func TestHover_withoutInitialization(t *testing.T) {
ls := langserver.NewLangServerMock(t, NewMockSession(nil))
stop := ls.Start(t)
defer stop()

ls.CallAndExpectError(t, &langserver.CallRequest{
Method: "textDocument/hover",
ReqParams: fmt.Sprintf(`{
"textDocument": {
"uri": "%s/main.tf"
},
"position": {
"character": 0,
"line": 1
}
}`, TempDir(t).URI())}, session.SessionNotInitialized.Err())
}

func TestHover_withValidData(t *testing.T) {
tmpDir := TempDir(t)
InitPluginCache(t, tmpDir.Dir())

var testSchema tfjson.ProviderSchemas
err := json.Unmarshal([]byte(testSchemaOutput), &testSchema)
if err != nil {
t.Fatal(err)
}

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

ls.Call(t, &langserver.CallRequest{
Method: "initialize",
ReqParams: fmt.Sprintf(`{
"capabilities": {},
"rootUri": %q,
"processId": 12345
}`, TempDir(t).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": "provider \"test\" {\n\n}\n",
"uri": "%s/main.tf"
}
}`, TempDir(t).URI())})

ls.CallAndExpectResponse(t, &langserver.CallRequest{
Method: "textDocument/hover",
ReqParams: fmt.Sprintf(`{
"textDocument": {
"uri": "%s/main.tf"
},
"position": {
"character": 3,
"line": 0
}
}`, TempDir(t).URI())}, `{
"jsonrpc": "2.0",
"id": 3,
"result": {
"contents": [
"provider Block\n\nA provider block is used to specify a provider configuration"
],
"range": {
"start": { "line":0, "character":0 },
"end": { "line":0, "character":8 }
}
}
}`)
}
1 change: 1 addition & 0 deletions langserver/handlers/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func (lh *logHandler) Initialize(ctx context.Context, params lsp.InitializeParam
CompletionProvider: &lsp.CompletionOptions{
ResolveProvider: false,
},
HoverProvider: true,
DocumentFormattingProvider: true,
DocumentSymbolProvider: true,
},
Expand Down
12 changes: 12 additions & 0 deletions langserver/handlers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,18 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {

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

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

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

0 comments on commit 1a35102

Please sign in to comment.