Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

textDocument/completion support via gocode #219

Merged
merged 7 commits into from
Dec 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions langserver/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package langserver

import (
"context"
"fmt"

"github.com/sourcegraph/go-langserver/langserver/internal/gocode"
"github.com/sourcegraph/go-langserver/pkg/lsp"
"github.com/sourcegraph/jsonrpc2"
)

var (
GocodeCompletionEnabled = false
CIKConstantSupported = lsp.CIKVariable // or lsp.CIKConstant if client supported
)

func (h *LangHandler) handleTextDocumentCompletion(ctx context.Context, conn jsonrpc2.JSONRPC2, req *jsonrpc2.Request, params lsp.CompletionParams) (*lsp.CompletionList, error) {
if !isFileURI(params.TextDocument.URI) {
return nil, &jsonrpc2.Error{
Code: jsonrpc2.CodeInvalidParams,
Message: fmt.Sprintf("textDocument/completion not yet supported for out-of-workspace URI (%q)", params.TextDocument.URI),
}
}

// In the case of testing, our OS paths and VFS paths do not match. In the
// real world, this is never the case. Give the test suite the opportunity
// to correct the path now.
vfsURI := params.TextDocument.URI
if testOSToVFSPath != nil {
vfsURI = pathToURI(testOSToVFSPath(uriToFilePath(vfsURI)))
}

// Read file contents and calculate byte offset.
contents, err := h.readFile(ctx, vfsURI)
if err != nil {
return nil, err
}
filename := h.FilePath(params.TextDocument.URI)
offset, valid, why := offsetForPosition(contents, params.Position)
if !valid {
return nil, fmt.Errorf("invalid position: %s:%d:%d (%s)", filename, params.Position.Line, params.Position.Character, why)
}

ca, rangelen := gocode.AutoComplete(contents, filename, offset)
citems := make([]lsp.CompletionItem, len(ca))
for i, it := range ca {
var kind lsp.CompletionItemKind
switch it.Class.String() {
case "const":
kind = CIKConstantSupported
case "func":
kind = lsp.CIKFunction
case "import":
kind = lsp.CIKModule
case "package":
kind = lsp.CIKModule
case "type":
kind = lsp.CIKClass
case "var":
kind = lsp.CIKVariable
}
citems[i] = lsp.CompletionItem{
Label: it.Name,
Kind: kind,
Detail: it.Type,
TextEdit: &lsp.TextEdit{
Range: lsp.Range{
Start: lsp.Position{Line: params.Position.Line, Character: params.Position.Character - rangelen},
End: lsp.Position{Line: params.Position.Line, Character: params.Position.Character},
},
NewText: it.Name,
},
}
}
return &lsp.CompletionList{
IsIncomplete: false,
Items: citems,
}, nil
}
26 changes: 26 additions & 0 deletions langserver/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"

"github.com/sourcegraph/go-langserver/langserver/internal/gocode"
"github.com/sourcegraph/go-langserver/pkg/lsp"
"github.com/sourcegraph/go-langserver/pkg/lspext"
"github.com/sourcegraph/jsonrpc2"
Expand Down Expand Up @@ -72,6 +73,13 @@ type LangHandler struct {

// reset clears all internal state in h.
func (h *LangHandler) reset(init *InitializeParams) error {
for _, k := range init.Capabilities.TextDocument.Completion.CompletionItemKind.ValueSet {
if k == lsp.CIKConstant {
CIKConstantSupported = lsp.CIKConstant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are setting a global variable here, but langserver supports multiple instances running at once in the same process.

break
}
}

if isFileURI(lsp.DocumentURI(init.InitializeParams.RootPath)) {
log.Printf("Passing an initialize rootPath URI (%q) is deprecated. Use rootUri instead.", init.InitializeParams.RootPath)
}
Expand Down Expand Up @@ -189,6 +197,9 @@ func (h *LangHandler) Handle(ctx context.Context, conn jsonrpc2.JSONRPC2, req *j
if err := h.reset(&params); err != nil {
return nil, err
}
if GocodeCompletionEnabled {
gocode.InitDaemon(h.BuildContext(ctx))
}

// PERF: Kick off a workspace/symbol in the background to warm up the server
if yes, _ := strconv.ParseBool(envWarmupOnInitialize); yes {
Expand All @@ -203,11 +214,16 @@ func (h *LangHandler) Handle(ctx context.Context, conn jsonrpc2.JSONRPC2, req *j
}

kind := lsp.TDSKIncremental
var completionOp *lsp.CompletionOptions
if GocodeCompletionEnabled {
completionOp = &lsp.CompletionOptions{TriggerCharacters: []string{"."}}
}
return lsp.InitializeResult{
Capabilities: lsp.ServerCapabilities{
TextDocumentSync: lsp.TextDocumentSyncOptionsOrKind{
Kind: &kind,
},
CompletionProvider: completionOp,
DefinitionProvider: true,
DocumentFormattingProvider: true,
DocumentSymbolProvider: true,
Expand Down Expand Up @@ -284,6 +300,16 @@ func (h *LangHandler) Handle(ctx context.Context, conn jsonrpc2.JSONRPC2, req *j
}
return h.handleXDefinition(ctx, conn, req, params)

case "textDocument/completion":
if req.Params == nil {
return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams}
}
var params lsp.CompletionParams
if err := json.Unmarshal(*req.Params, &params); err != nil {
return nil, err
}
return h.handleTextDocumentCompletion(ctx, conn, req, params)

case "textDocument/references":
if req.Params == nil {
return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams}
Expand Down
12 changes: 12 additions & 0 deletions langserver/internal/gocode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*.8
*.a
*.out
gocode
gocode.exe
goremote
gocodetest
*.swp
listidents
showcursor
showsmap
rename
19 changes: 19 additions & 0 deletions langserver/internal/gocode/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (C) 2010 nsf <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Loading