Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial language server protocol implementation #27

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
34 changes: 34 additions & 0 deletions cmd/langserver-vim/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"context"
"log"
"os"

"github.com/haya14busa/go-vimlparser/langserver"
"github.com/sourcegraph/jsonrpc2"
)

func main() {
log.Println("langserver-vim: reading on stdin, writing on stdout")
var connOpt []jsonrpc2.ConnOpt
<-jsonrpc2.NewConn(context.Background(), jsonrpc2.NewBufferedStream(stdrwc{}, jsonrpc2.VSCodeObjectCodec{}), langserver.NewHandler(), connOpt...).DisconnectNotify()
log.Println("langserver-vim: connections closed")
}

type stdrwc struct{}

func (stdrwc) Read(p []byte) (int, error) {
return os.Stdin.Read(p)
}

func (stdrwc) Write(p []byte) (int, error) {
return os.Stdout.Write(p)
}

func (stdrwc) Close() error {
if err := os.Stdin.Close(); err != nil {
return err
}
return os.Stdout.Close()
}
26 changes: 26 additions & 0 deletions langserver/handle_initialize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package langserver

import (
"context"
"encoding/json"

"github.com/sourcegraph/jsonrpc2"
)

func (h *LangHandler) handleInitialize(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (result interface{}, err error) {
if req.Params == nil {
return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams}
}

var params InitializeParams
if err := json.Unmarshal(*req.Params, &params); err != nil {
return nil, err
}

return InitializeResult{
Capabilities: ServerCapabilities{
TextDocumentSync: TDSKFull,
DocumentSymbolProvider: true,
},
}, nil
}
26 changes: 26 additions & 0 deletions langserver/handle_text_document_did_open.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package langserver

import (
"context"
"encoding/json"

"github.com/sourcegraph/jsonrpc2"
)

func (h *LangHandler) handleTextDocumentDidOpen(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (result interface{}, err error) {
if req.Params == nil {
return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams}
}

var params DidOpenTextDocumentParams
if err := json.Unmarshal(*req.Params, &params); err != nil {
return nil, err
}

f, err := newVimFile(params.TextDocument)
if err != nil {
return nil, err
}
h.files[params.TextDocument.URI] = f
return nil, nil
}
72 changes: 72 additions & 0 deletions langserver/handle_text_document_symbol.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package langserver

import (
"context"
"encoding/json"
"fmt"

"github.com/haya14busa/go-vimlparser/ast"

"github.com/sourcegraph/jsonrpc2"
)

func (h *LangHandler) handleTextDocumentSymbols(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (result interface{}, err error) {
if req.Params == nil {
return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams}
}

var params DocumentSymbolParams
if err := json.Unmarshal(*req.Params, &params); err != nil {
return nil, err
}

if f, ok := h.files[params.TextDocument.URI]; ok {
node, err := f.GetAst()
if err != nil {
return nil, err
}
return getDocumentSymbols(params, node), nil
}
return nil, fmt.Errorf("%s not open", params.TextDocument.URI)
}

func getDocumentSymbols(params DocumentSymbolParams, node ast.Node) []SymbolInformation {
var symbols []SymbolInformation
ast.Inspect(node, func(n ast.Node) bool {
var name string
var kind SymbolKind
var pos ast.Pos
switch x := n.(type) {
case *ast.Function:
switch y := x.Name.(type) {
case *ast.Ident:
kind = SKFunction
name = y.Name
pos = y.NamePos
}

if name != "" {
symbols = append(symbols, SymbolInformation{
Name: name,
Kind: kind,
Location: Location{
URI: params.TextDocument.URI,
Range: Range{
Start: Position{
Line: pos.Line - 1,
Character: pos.Column - 1,
},
End: Position{
Line: pos.Line - 1,
Character: pos.Column + len(name) - 1,
},
},
},
})
return false
}
}
return true
})
return symbols
}
65 changes: 65 additions & 0 deletions langserver/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package langserver

import (
"bytes"
"context"
"fmt"

"github.com/haya14busa/go-vimlparser"
"github.com/haya14busa/go-vimlparser/ast"

"github.com/sourcegraph/jsonrpc2"
)

func NewHandler() jsonrpc2.Handler {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported function NewHandler should have comment or be unexported

var langHandler = &LangHandler{
files: make(map[string]*vimfile),
}
return jsonrpc2.HandlerWithError(langHandler.handle)
}

type LangHandler struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type LangHandler should have comment or be unexported

files map[string]*vimfile
}

type vimfile struct {
TextDocumentItem
Ast *ast.File
AstError error
}

func newVimFile(textDocumentItem TextDocumentItem) (result *vimfile, error error) {
return &vimfile{
TextDocumentItem: textDocumentItem,
Ast: nil,
AstError: nil,
}, nil
}

func (f *vimfile) GetAst() (result *ast.File, error error) {
if f.AstError != nil {
return nil, f.AstError
} else if f.Ast != nil {
return f.Ast, nil
} else {
opt := &vimlparser.ParseOption{Neovim: false}
r := bytes.NewBufferString(f.Text)
ast, err := vimlparser.ParseFile(r, f.URI, opt)
f.Ast = ast
f.AstError = err
return ast, err
}
}

func (h *LangHandler) handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (result interface{}, err error) {
switch req.Method {
case "initialize":
return h.handleInitialize(ctx, conn, req)
case "textDocument/didOpen":
return h.handleTextDocumentDidOpen(ctx, conn, req)
case "textDocument/documentSymbol":
return h.handleTextDocumentSymbols(ctx, conn, req)
}

return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeMethodNotFound, Message: fmt.Sprintf("method not supported: %s", req.Method)}
}
95 changes: 95 additions & 0 deletions langserver/lsp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package langserver

type InitializeParams struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type InitializeParams should have comment or be unexported

ProcessID int `json:"processId,omitempty"`
RootPath string `json:"rootPath,omitempty"`
InitializationOptions InitializeOptions `json:"initializationOptions,omitempty"`
Capabilities ClientCapabilities `json:"capabilities,omitempty"`
Trace string `json:"trace,omitempty"`
}

type InitializeOptions struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type InitializeOptions should have comment or be unexported

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type InitializeOptions should have comment or be unexported

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type InitializeOptions should have comment or be unexported

}

type ClientCapabilities struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type ClientCapabilities should have comment or be unexported

}

type InitializeResult struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type InitializeResult should have comment or be unexported

Capabilities ServerCapabilities `json:"capabilities,omitempty"`
}

type TextDocumentSyncKind int

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type TextDocumentSyncKind should have comment or be unexported


const (
TDSKNone TextDocumentSyncKind = 0

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported const TDSKNone should have comment (or a comment on this block) or be unexported

TDSKFull = 1
TDSKIncremental = 2
)

type ServerCapabilities struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type ServerCapabilities should have comment or be unexported

TextDocumentSync TextDocumentSyncKind `json:"textDocumentSync,omitempty"`
DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"`
}

type TextDocumentItem struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type TextDocumentItem should have comment or be unexported

URI string `json:"uri"`
LanguageID string `json:"languageId"`
Version int `json:"version"`
Text string `json:"text"`
}

type TextDocumentIdentifier struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type TextDocumentIdentifier should have comment or be unexported

URI string `json:"uri"`
}

type DidOpenTextDocumentParams struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type DidOpenTextDocumentParams should have comment or be unexported

TextDocument TextDocumentItem `json:"textDocument"`
}

type DocumentSymbolParams struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type DocumentSymbolParams should have comment or be unexported

TextDocument TextDocumentIdentifier
}

type SymbolKind int

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type SymbolKind should have comment or be unexported


const (
SKFile SymbolKind = 1

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported const SKFile should have comment (or a comment on this block) or be unexported

SKModule SymbolKind = 2
SKNamespace SymbolKind = 3
SKPackage SymbolKind = 4
SKClass SymbolKind = 5
SKMethod SymbolKind = 6
SKProperty SymbolKind = 7
SKField SymbolKind = 8
SKConstructor SymbolKind = 9
SKEnum SymbolKind = 10
SKInterface SymbolKind = 11
SKFunction SymbolKind = 12
SKVariable SymbolKind = 13
SKConstant SymbolKind = 14
SKString SymbolKind = 15
SKNumber SymbolKind = 16
SKBoolean SymbolKind = 17
SKArray SymbolKind = 18
)

type SymbolInformation struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type SymbolInformation should have comment or be unexported

Name string `json:"name"`
Kind SymbolKind `json:"kind"`
Location Location `json:"location"`
}

type Location struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type Location should have comment or be unexported

URI string `json:"uri"`
Range Range `json:"range"`
}

type Range struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type Range should have comment or be unexported

Start Position `json:"start"`
End Position `json:"end"`
}

type Position struct {

Choose a reason for hiding this comment

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

[golint] reported by reviewdog 🐶
exported type Position should have comment or be unexported

Line int `json:"line"`
Character int `json:"character"`
}