Skip to content

Commit

Permalink
initial language server protocol implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
prabirshrestha committed Jan 8, 2017
1 parent f57a807 commit d320a46
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 0 deletions.
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
}
27 changes: 27 additions & 0 deletions langserver/handle_text_document_did_open.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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
} else {
h.files[params.TextDocument.URI] = f
return nil, nil
}
}
76 changes: 76 additions & 0 deletions langserver/handle_text_document_symbol.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package langserver

import (
"context"
"encoding/json"
"errors"
"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
} else {
return getDocumentSymbols(params, node), nil
}
return nil, nil
} else {
return nil, errors.New(fmt.Sprintf("% 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 {
var langHandler = &LangHandler{
files: make(map[string]*vimfile),
}
return jsonrpc2.HandlerWithError(langHandler.handle)
}

type LangHandler struct {
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 {
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 {
}

type ClientCapabilities struct {
}

type InitializeResult struct {
Capabilities ServerCapabilities `json:"capabilities,omitempty"`
}

type TextDocumentSyncKind int

const (
TDSKNone TextDocumentSyncKind = 0
TDSKFull = 1
TDSKIncremental = 2
)

type ServerCapabilities struct {
TextDocumentSync TextDocumentSyncKind `json:"textDocumentSync,omitempty"`
DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"`
}

type TextDocumentItem struct {
URI string `json:"uri"`
LanguageId string `json:"languageId"`
Version int `json:"version"`
Text string `json:"text"`
}

type TextDocumentIdentifier struct {
URI string `json:"uri"`
}

type DidOpenTextDocumentParams struct {
TextDocument TextDocumentItem `json:"textDocument"`
}

type DocumentSymbolParams struct {
TextDocument TextDocumentIdentifier
}

type SymbolKind int

const (
SKFile SymbolKind = 1
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 {
Name string `json:"name"`
Kind SymbolKind `json:"kind"`
Location Location `json:"location"`
}

type Location struct {
URI string `json:"uri"`
Range Range `json:"range"`
}

type Range struct {
Start Position `json:"start"`
End Position `json:"end"`
}

type Position struct {
Line int `json:"line"`
Character int `json:"character"`
}

0 comments on commit d320a46

Please sign in to comment.