lsp 更新保存

This commit is contained in:
chai2010 2023-05-06 00:45:02 +08:00
parent 5862f8e68b
commit a5f7fd3879
6 changed files with 227 additions and 119 deletions

View File

@ -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",

View File

@ -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"
)

View File

@ -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"`

View File

@ -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
}

View File

@ -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, &params); 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, &params); 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

View File

@ -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, &params); 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, &params); 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, &params); 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, &params); 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, &params); 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")