forked from wa-lang/wa
lsp 更新保存
This commit is contained in:
parent
5862f8e68b
commit
a5f7fd3879
|
|
@ -21,6 +21,7 @@ import (
|
|||
"wa-lang.org/wa/internal/app/apputil"
|
||||
"wa-lang.org/wa/internal/app/yacc"
|
||||
"wa-lang.org/wa/internal/config"
|
||||
"wa-lang.org/wa/internal/lsp"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
|
|
@ -457,6 +458,22 @@ func Main() {
|
|||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "lsp",
|
||||
Usage: "run wa lang server",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "log-file",
|
||||
Usage: "set log file",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
lsp.NewLSPServer(&lsp.Option{}).Run()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "logo",
|
||||
Usage: "print wa-lang text format logo",
|
||||
|
|
|
|||
|
|
@ -24,4 +24,6 @@ const (
|
|||
k_textDocument_references = "textDocument/references"
|
||||
k_textDocument_hover = "textDocument/hover"
|
||||
k_textDocument_codeAction = "textDocument/codeAction"
|
||||
|
||||
k_window_logMessage = "window/logMessage"
|
||||
)
|
||||
|
|
@ -4,6 +4,14 @@
|
|||
|
||||
package lsp
|
||||
|
||||
type DocumentURI string
|
||||
|
||||
type File struct {
|
||||
LanguageID string
|
||||
Text string
|
||||
Version int
|
||||
}
|
||||
|
||||
type InitializeParams struct {
|
||||
ProcessID int `json:"processId,omitempty"`
|
||||
RootURI string `json:"rootUri,omitempty"`
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
// 版权 @2023 凹语言 作者。保留所有权利。
|
||||
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type lspChannel struct {
|
||||
r io.Reader
|
||||
w io.Writer
|
||||
dec *json.Decoder
|
||||
}
|
||||
|
||||
type lspRequest struct {
|
||||
ID int `json:"id"`
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params []json.RawMessage `json:"params"`
|
||||
}
|
||||
|
||||
type lspResponse struct {
|
||||
ID int `json:"id"`
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Result interface{} `json:"result"`
|
||||
Error *lspError `json:"error"`
|
||||
}
|
||||
|
||||
// JSONRPC错误
|
||||
type lspError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (err *lspError) Error() string {
|
||||
return fmt.Sprintf("lsp.Error{code:%d, message: %q}", err.Code, err.Message)
|
||||
}
|
||||
|
||||
func (p *lspChannel) RecvRequest() (req *lspRequest, err error) {
|
||||
if p.dec == nil {
|
||||
p.dec = json.NewDecoder(p.r)
|
||||
}
|
||||
req = new(lspRequest)
|
||||
err = p.dec.Decode(&req)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *lspChannel) SendRespose(resp *lspResponse) error {
|
||||
reqBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = p.w.Write(reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -4,95 +4,123 @@ package lsp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/jsonrpc2"
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
LogFile string
|
||||
}
|
||||
|
||||
type LSPServer struct {
|
||||
conn *lspChannel
|
||||
mu sync.Mutex
|
||||
|
||||
conn *jsonrpc2.Conn
|
||||
rootPath string
|
||||
shutdown bool
|
||||
|
||||
logger *log.Logger
|
||||
loglevel int
|
||||
}
|
||||
|
||||
func NewLSPServer() *LSPServer {
|
||||
return &LSPServer{
|
||||
conn: &lspChannel{r: os.Stdin, w: os.Stdout},
|
||||
func (p *Option) clone() *Option {
|
||||
if p == nil {
|
||||
return &Option{}
|
||||
}
|
||||
return &Option{
|
||||
LogFile: p.LogFile,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *LSPServer) Run() error {
|
||||
ctx := context.Background()
|
||||
for {
|
||||
req, err := p.conn.RecvRequest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp = &lspResponse{
|
||||
ID: req.ID,
|
||||
JSONRPC: req.JSONRPC,
|
||||
}
|
||||
resp.Result, err = p.handleMethod(ctx, req)
|
||||
if err != nil {
|
||||
resp.Error = &lspError{
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
if err = p.conn.SendRespose(resp); err != nil {
|
||||
// log
|
||||
}
|
||||
}
|
||||
func NewLSPServer(opt *Option) *LSPServer {
|
||||
return &LSPServer{}
|
||||
}
|
||||
|
||||
func (p *LSPServer) handleMethod(ctx context.Context, req *lspRequest) (result interface{}, err error) {
|
||||
var argsData []byte
|
||||
if len(req.Params) > 0 {
|
||||
argsData = []byte(req.Params[0])
|
||||
func (p *LSPServer) Run() {
|
||||
var rwc = struct {
|
||||
io.Writer
|
||||
io.ReadCloser
|
||||
}{
|
||||
ReadCloser: ioutil.NopCloser(os.Stdin),
|
||||
Writer: os.Stdout,
|
||||
}
|
||||
|
||||
conn := jsonrpc2.NewConn(
|
||||
context.Background(),
|
||||
jsonrpc2.NewBufferedStream(rwc, jsonrpc2.VSCodeObjectCodec{}),
|
||||
jsonrpc2.HandlerWithError(p.handle),
|
||||
)
|
||||
ch := conn.DisconnectNotify()
|
||||
<-ch
|
||||
}
|
||||
|
||||
func (h *LSPServer) handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (result interface{}, err error) {
|
||||
if req.Notif {
|
||||
switch req.Method {
|
||||
case k_initialized:
|
||||
return
|
||||
case k_textDocument_didOpen:
|
||||
return h.handleTextDocumentDidOpen(ctx, conn, req)
|
||||
case k_textDocument_didChange:
|
||||
return h.handleTextDocumentDidChange(ctx, conn, req)
|
||||
case k_textDocument_didSave:
|
||||
return h.handleTextDocumentDidSave(ctx, conn, req)
|
||||
case k_textDocument_didClose:
|
||||
return h.handleTextDocumentDidClose(ctx, conn, req)
|
||||
case k_exit:
|
||||
return nil, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case "initialized":
|
||||
case k_initialize:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case k_initialized:
|
||||
return
|
||||
case "textDocument/didOpen":
|
||||
var params DidOpenTextDocumentParams
|
||||
if err := json.Unmarshal(argsData, ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/didChange":
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/didSave":
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/didClose":
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "exit":
|
||||
case k_shutdown:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
|
||||
case "initialize":
|
||||
case k_workspace_executeCommand:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "shutdown":
|
||||
case k_workspace_didChangeConfiguration:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/formatting":
|
||||
case k_workspace_workspaceFolders:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/documentSymbol":
|
||||
case k_workspace_didChangeWorkspaceFolders:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/completion":
|
||||
|
||||
case k_textDocument_didOpen:
|
||||
//var params DidOpenTextDocumentParams
|
||||
//if err := json.Unmarshal(argsData, ¶ms); err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/definition":
|
||||
case k_textDocument_didChange:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/references":
|
||||
case k_textDocument_didSave:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/hover":
|
||||
case k_textDocument_didClose:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "textDocument/codeAction":
|
||||
case k_textDocument_formatting:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "workspace/executeCommand":
|
||||
case k_textDocument_documentSymbol:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "workspace/didChangeConfiguration":
|
||||
case k_textDocument_completion:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "workspace/workspaceFolders":
|
||||
case k_textDocument_definition:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case "workspace/didChangeWorkspaceFolders":
|
||||
case k_textDocument_references:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case k_textDocument_hover:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
case k_textDocument_codeAction:
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
return nil, nil
|
||||
|
|
|
|||
|
|
@ -3,9 +3,122 @@
|
|||
package lsp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"wa-lang.org/wa/internal/3rdparty/jsonrpc2"
|
||||
)
|
||||
|
||||
func (h *LSPServer) 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, ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
|
||||
func (h *LSPServer) handleTextDocumentDidChange(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 DidChangeTextDocumentParams
|
||||
if err := json.Unmarshal(*req.Params, ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(params.ContentChanges) == 1 {
|
||||
// todo
|
||||
}
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
|
||||
func (h *LSPServer) handleTextDocumentDidSave(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 DidSaveTextDocumentParams
|
||||
if err := json.Unmarshal(*req.Params, ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params.Text != nil {
|
||||
// update
|
||||
} else {
|
||||
// save
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
|
||||
func (h *LSPServer) handleTextDocumentDidClose(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 DidCloseTextDocumentParams
|
||||
if err := json.Unmarshal(*req.Params, ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
|
||||
func (h *LSPServer) 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}
|
||||
}
|
||||
h.conn = conn
|
||||
|
||||
var params InitializeParams
|
||||
if err := json.Unmarshal(*req.Params, ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Only try to parse the workspace root if its not null. Otherwise initialize will fail
|
||||
if params.RootURI != "" {
|
||||
//
|
||||
}
|
||||
|
||||
var completion = &CompletionProvider{
|
||||
TriggerCharacters: []string{"*"},
|
||||
}
|
||||
|
||||
return InitializeResult{
|
||||
Capabilities: ServerCapabilities{
|
||||
TextDocumentSync: TDSKFull,
|
||||
DocumentFormattingProvider: true,
|
||||
DocumentSymbolProvider: true,
|
||||
DefinitionProvider: true,
|
||||
ReferencesProvider: true,
|
||||
CompletionProvider: completion,
|
||||
HoverProvider: true,
|
||||
CodeActionProvider: true,
|
||||
Workspace: &ServerCapabilitiesWorkspace{
|
||||
WorkspaceFolders: WorkspaceFoldersServerCapabilities{
|
||||
Supported: true,
|
||||
ChangeNotifications: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *LSPServer) logMessage(typ MessageType, message string) {
|
||||
h.conn.Notify(context.Background(),
|
||||
k_window_logMessage, &LogMessageParams{
|
||||
Type: typ, Message: message,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (p *LSPServer) executeCommand(params *ExecuteCommandParams) (interface{}, error) {
|
||||
if len(params.Arguments) != 1 {
|
||||
return nil, fmt.Errorf("invalid command")
|
||||
|
|
|
|||
Loading…
Reference in New Issue