@@ -7,8 +7,10 @@ import (
77 "io"
88 "io/ioutil"
99 "log"
10+ "os"
1011 "regexp"
1112 "strings"
13+ "time"
1214
1315 "github.com/pkg/errors"
1416 lsp "github.com/sourcegraph/go-lsp"
@@ -42,7 +44,7 @@ func NewInoHandler(stdin io.ReadCloser, stdout io.WriteCloser, stdinLog, stdoutL
4244 }
4345 handler .StartClangd ()
4446 stdStream := jsonrpc2 .NewBufferedStream (StreamReadWrite {stdin , stdout , stdinLog , stdoutLog }, jsonrpc2.VSCodeObjectCodec {})
45- stdHandler := jsonrpc2 .HandlerWithError (handler .FromStdio )
47+ stdHandler := jsonrpc2 .AsyncHandler ( jsonrpc2 . HandlerWithError (handler .FromStdio ) )
4648 handler .StdioConn = jsonrpc2 .NewConn (context .Background (), stdStream , stdHandler )
4749 if enableLogging {
4850 log .Println ("Initial board configuration:" , board )
@@ -84,7 +86,7 @@ func (handler *InoHandler) StartClangd() {
8486 }
8587 srw := StreamReadWrite {clangdRead , clangdWrite , handler .clangdProc .inLog , handler .clangdProc .outLog }
8688 clangdStream := jsonrpc2 .NewBufferedStream (srw , jsonrpc2.VSCodeObjectCodec {})
87- clangdHandler := jsonrpc2 .HandlerWithError (handler .FromClangd )
89+ clangdHandler := jsonrpc2 .AsyncHandler ( jsonrpc2 . HandlerWithError (handler .FromClangd ) )
8890 handler .ClangdConn = jsonrpc2 .NewConn (context .Background (), clangdStream , clangdHandler )
8991}
9092
@@ -114,20 +116,26 @@ func (handler *InoHandler) FromStdio(ctx context.Context, conn *jsonrpc2.Conn, r
114116 if params == nil {
115117 params = req .Params
116118 } else {
117- uri , err = handler .transformClangdParams (ctx , req .Method , params )
119+ uri , err = handler .transformParamsToClangd (ctx , req .Method , params )
118120 }
119121 if err != nil {
120122 return
121123 }
122- if handler .ClangdConn == nil {
123- panic ("Illegal state: handler.ClangdConn is nil" )
124- }
125124 if req .Notif {
126125 err = handler .ClangdConn .Notify (ctx , req .Method , params )
127126 } else {
127+ ctx , cancel := context .WithTimeout (ctx , 800 * time .Millisecond )
128+ defer cancel ()
128129 result , err = sendRequest (ctx , handler .ClangdConn , req .Method , params )
129130 }
130131 if err != nil {
132+ if err .Error () == "context deadline exceeded" {
133+ // Exit the process and trigger a restart by the client
134+ log .Println ("Timeout exceeded while waiting for a reply from clangd:" , req .Method )
135+ log .Println ("Please restart the language server." )
136+ handler .StopClangd ()
137+ os .Exit (1 )
138+ }
131139 return
132140 }
133141 if enableLogging {
@@ -197,7 +205,7 @@ func (handler *InoHandler) changeBoardConfig(ctx context.Context, config *BoardC
197205 return
198206}
199207
200- func (handler * InoHandler ) transformClangdParams (ctx context.Context , method string , params interface {}) (uri lsp.DocumentURI , err error ) {
208+ func (handler * InoHandler ) transformParamsToClangd (ctx context.Context , method string , params interface {}) (uri lsp.DocumentURI , err error ) {
201209 switch method {
202210 case "initialize" :
203211 handler .clangdProc .initParams = * params .(* lsp.InitializeParams )
@@ -267,6 +275,9 @@ func (handler *InoHandler) transformClangdParams(ctx context.Context, method str
267275 case "workspace/didChangeWatchedFiles" :
268276 p := params .(* lsp.DidChangeWatchedFilesParams )
269277 err = handler .ino2cppDidChangeWatchedFilesParams (p )
278+ case "workspace/executeCommand" :
279+ p := params .(* lsp.ExecuteCommandParams )
280+ err = handler .ino2cppExecuteCommand (p )
270281 }
271282 return
272283}
@@ -495,18 +506,61 @@ func (handler *InoHandler) ino2cppDidChangeWatchedFilesParams(params *lsp.DidCha
495506 return nil
496507}
497508
509+ func (handler * InoHandler ) ino2cppExecuteCommand (executeCommand * lsp.ExecuteCommandParams ) error {
510+ if len (executeCommand .Arguments ) == 1 {
511+ arg := handler .parseCommandArgument (executeCommand .Arguments [0 ])
512+ if workspaceEdit , ok := arg .(* lsp.WorkspaceEdit ); ok {
513+ executeCommand .Arguments [0 ] = handler .ino2cppWorkspaceEdit (workspaceEdit )
514+ }
515+ }
516+ return nil
517+ }
518+
519+ func (handler * InoHandler ) ino2cppWorkspaceEdit (origEdit * lsp.WorkspaceEdit ) * lsp.WorkspaceEdit {
520+ newEdit := lsp.WorkspaceEdit {Changes : make (map [string ][]lsp.TextEdit )}
521+ for uri , edit := range origEdit .Changes {
522+ if data , ok := handler .data [lsp .DocumentURI (uri )]; ok {
523+ newValue := make ([]lsp.TextEdit , len (edit ))
524+ for index := range edit {
525+ r := edit [index ].Range
526+ newValue [index ] = lsp.TextEdit {
527+ NewText : edit [index ].NewText ,
528+ Range : lsp.Range {
529+ Start : lsp.Position {Line : data .targetLineMap [r .Start .Line ], Character : r .Start .Character },
530+ End : lsp.Position {Line : data .targetLineMap [r .End .Line ], Character : r .End .Character },
531+ },
532+ }
533+ }
534+ newEdit .Changes [string (data .targetURI )] = newValue
535+ } else {
536+ newEdit .Changes [uri ] = edit
537+ }
538+ }
539+ return & newEdit
540+ }
541+
498542func (handler * InoHandler ) transformClangdResult (method string , uri lsp.DocumentURI , result interface {}) interface {} {
499543 switch method {
500544 case "textDocument/completion" :
501545 r := result .(* lsp.CompletionList )
502546 handler .cpp2inoCompletionList (r , uri )
503547 case "textDocument/codeAction" :
504- r := result .(* []CodeAction )
548+ r := result .(* []* commandOrCodeAction )
505549 for index := range * r {
506- handler .cpp2inoCodeAction (& (* r )[index ], uri )
550+ command := (* r )[index ].Command
551+ if command != nil {
552+ handler .cpp2inoCommand (command )
553+ }
554+ codeAction := (* r )[index ].CodeAction
555+ if codeAction != nil {
556+ handler .cpp2inoCodeAction (codeAction , uri )
557+ }
507558 }
508559 case "textDocument/hover" :
509560 r := result .(* Hover )
561+ if len (r .Contents .Value ) == 0 {
562+ return nil
563+ }
510564 handler .cpp2inoHover (r , uri )
511565 case "textDocument/definition" :
512566 fallthrough
@@ -534,8 +588,26 @@ func (handler *InoHandler) transformClangdResult(method string, uri lsp.Document
534588 handler .cpp2inoTextEdit (& (* r )[index ], uri )
535589 }
536590 case "textDocument/documentSymbol" :
537- r := result .(* []DocumentSymbol )
538- result = handler .cpp2inoDocumentSymbols (* r , uri )
591+ r := result .(* []* documentSymbolOrSymbolInformation )
592+ slice := * r
593+ if len (slice ) > 0 && slice [0 ].DocumentSymbol != nil {
594+ // Treat the input as []DocumentSymbol
595+ symbols := make ([]DocumentSymbol , len (slice ))
596+ for index := range slice {
597+ symbols [index ] = * slice [index ].DocumentSymbol
598+ }
599+ result = handler .cpp2inoDocumentSymbols (symbols , uri )
600+ } else if len (slice ) > 0 && slice [0 ].SymbolInformation != nil {
601+ // Treat the input as []SymbolInformation
602+ symbols := make ([]lsp.SymbolInformation , len (slice ))
603+ for index := range slice {
604+ symbols [index ] = * slice [index ].SymbolInformation
605+ }
606+ for index := range symbols {
607+ handler .cpp2inoLocation (& symbols [index ].Location )
608+ }
609+ result = symbols
610+ }
539611 case "textDocument/rename" :
540612 r := result .(* lsp.WorkspaceEdit )
541613 result = handler .cpp2inoWorkspaceEdit (r )
@@ -571,6 +643,15 @@ func (handler *InoHandler) cpp2inoCodeAction(codeAction *CodeAction, uri lsp.Doc
571643 }
572644}
573645
646+ func (handler * InoHandler ) cpp2inoCommand (command * lsp.Command ) {
647+ if len (command .Arguments ) == 1 {
648+ arg := handler .parseCommandArgument (command .Arguments [0 ])
649+ if workspaceEdit , ok := arg .(* lsp.WorkspaceEdit ); ok {
650+ command .Arguments [0 ] = handler .cpp2inoWorkspaceEdit (workspaceEdit )
651+ }
652+ }
653+ }
654+
574655func (handler * InoHandler ) cpp2inoWorkspaceEdit (origEdit * lsp.WorkspaceEdit ) * lsp.WorkspaceEdit {
575656 newEdit := lsp.WorkspaceEdit {Changes : make (map [string ][]lsp.TextEdit )}
576657 for uri , edit := range origEdit .Changes {
@@ -658,7 +739,7 @@ func (handler *InoHandler) cpp2inoDocumentSymbols(origSymbols []DocumentSymbol,
658739
659740// FromClangd handles a message received from clangd.
660741func (handler * InoHandler ) FromClangd (ctx context.Context , connection * jsonrpc2.Conn , req * jsonrpc2.Request ) (interface {}, error ) {
661- params , _ , err := handler .transformStdioParams (req .Method , req .Params )
742+ params , _ , err := handler .transformParamsToStdio (req .Method , req .Params )
662743 if err != nil {
663744 log .Println ("From clangd: Method:" , req .Method , "Error:" , err )
664745 return nil , err
@@ -679,7 +760,7 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
679760 return result , err
680761}
681762
682- func (handler * InoHandler ) transformStdioParams (method string , raw * json.RawMessage ) (params interface {}, uri lsp.DocumentURI , err error ) {
763+ func (handler * InoHandler ) transformParamsToStdio (method string , raw * json.RawMessage ) (params interface {}, uri lsp.DocumentURI , err error ) {
683764 params , err = readParams (method , raw )
684765 if err != nil {
685766 return
@@ -692,6 +773,9 @@ func (handler *InoHandler) transformStdioParams(method string, raw *json.RawMess
692773 p := params .(* lsp.PublishDiagnosticsParams )
693774 uri = p .URI
694775 err = handler .cpp2inoPublishDiagnosticsParams (p )
776+ case "workspace/applyEdit" :
777+ p := params .(* ApplyWorkspaceEditParams )
778+ p .Edit = * handler .cpp2inoWorkspaceEdit (& p .Edit )
695779 }
696780 return
697781}
@@ -708,6 +792,34 @@ func (handler *InoHandler) cpp2inoPublishDiagnosticsParams(params *lsp.PublishDi
708792 return nil
709793}
710794
795+ func (handler * InoHandler ) parseCommandArgument (rawArg interface {}) interface {} {
796+ if m1 , ok := rawArg .(map [string ]interface {}); ok && len (m1 ) == 1 && m1 ["changes" ] != nil {
797+ m2 := m1 ["changes" ].(map [string ]interface {})
798+ workspaceEdit := lsp.WorkspaceEdit {Changes : make (map [string ][]lsp.TextEdit )}
799+ for uri , rawValue := range m2 {
800+ rawTextEdits := rawValue .([]interface {})
801+ textEdits := make ([]lsp.TextEdit , len (rawTextEdits ))
802+ for index := range rawTextEdits {
803+ m3 := rawTextEdits [index ].(map [string ]interface {})
804+ rawRange := m3 ["range" ]
805+ m4 := rawRange .(map [string ]interface {})
806+ rawStart := m4 ["start" ]
807+ m5 := rawStart .(map [string ]interface {})
808+ textEdits [index ].Range .Start .Line = int (m5 ["line" ].(float64 ))
809+ textEdits [index ].Range .Start .Character = int (m5 ["character" ].(float64 ))
810+ rawEnd := m4 ["end" ]
811+ m6 := rawEnd .(map [string ]interface {})
812+ textEdits [index ].Range .End .Line = int (m6 ["line" ].(float64 ))
813+ textEdits [index ].Range .End .Character = int (m6 ["character" ].(float64 ))
814+ textEdits [index ].NewText = m3 ["newText" ].(string )
815+ }
816+ workspaceEdit .Changes [uri ] = textEdits
817+ }
818+ return & workspaceEdit
819+ }
820+ return nil
821+ }
822+
711823func (handler * InoHandler ) showMessage (ctx context.Context , msgType lsp.MessageType , message string ) {
712824 params := lsp.ShowMessageParams {
713825 Type : msgType ,
0 commit comments