@@ -7,8 +7,10 @@ import (
7
7
"io"
8
8
"io/ioutil"
9
9
"log"
10
+ "os"
10
11
"regexp"
11
12
"strings"
13
+ "time"
12
14
13
15
"github.com/pkg/errors"
14
16
lsp "github.com/sourcegraph/go-lsp"
@@ -42,7 +44,7 @@ func NewInoHandler(stdin io.ReadCloser, stdout io.WriteCloser, stdinLog, stdoutL
42
44
}
43
45
handler .StartClangd ()
44
46
stdStream := jsonrpc2 .NewBufferedStream (StreamReadWrite {stdin , stdout , stdinLog , stdoutLog }, jsonrpc2.VSCodeObjectCodec {})
45
- stdHandler := jsonrpc2 .HandlerWithError (handler .FromStdio )
47
+ stdHandler := jsonrpc2 .AsyncHandler ( jsonrpc2 . HandlerWithError (handler .FromStdio ) )
46
48
handler .StdioConn = jsonrpc2 .NewConn (context .Background (), stdStream , stdHandler )
47
49
if enableLogging {
48
50
log .Println ("Initial board configuration:" , board )
@@ -84,7 +86,7 @@ func (handler *InoHandler) StartClangd() {
84
86
}
85
87
srw := StreamReadWrite {clangdRead , clangdWrite , handler .clangdProc .inLog , handler .clangdProc .outLog }
86
88
clangdStream := jsonrpc2 .NewBufferedStream (srw , jsonrpc2.VSCodeObjectCodec {})
87
- clangdHandler := jsonrpc2 .HandlerWithError (handler .FromClangd )
89
+ clangdHandler := jsonrpc2 .AsyncHandler ( jsonrpc2 . HandlerWithError (handler .FromClangd ) )
88
90
handler .ClangdConn = jsonrpc2 .NewConn (context .Background (), clangdStream , clangdHandler )
89
91
}
90
92
@@ -114,20 +116,26 @@ func (handler *InoHandler) FromStdio(ctx context.Context, conn *jsonrpc2.Conn, r
114
116
if params == nil {
115
117
params = req .Params
116
118
} else {
117
- uri , err = handler .transformClangdParams (ctx , req .Method , params )
119
+ uri , err = handler .transformParamsToClangd (ctx , req .Method , params )
118
120
}
119
121
if err != nil {
120
122
return
121
123
}
122
- if handler .ClangdConn == nil {
123
- panic ("Illegal state: handler.ClangdConn is nil" )
124
- }
125
124
if req .Notif {
126
125
err = handler .ClangdConn .Notify (ctx , req .Method , params )
127
126
} else {
127
+ ctx , cancel := context .WithTimeout (ctx , 800 * time .Millisecond )
128
+ defer cancel ()
128
129
result , err = sendRequest (ctx , handler .ClangdConn , req .Method , params )
129
130
}
130
131
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
+ }
131
139
return
132
140
}
133
141
if enableLogging {
@@ -197,7 +205,7 @@ func (handler *InoHandler) changeBoardConfig(ctx context.Context, config *BoardC
197
205
return
198
206
}
199
207
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 ) {
201
209
switch method {
202
210
case "initialize" :
203
211
handler .clangdProc .initParams = * params .(* lsp.InitializeParams )
@@ -267,6 +275,9 @@ func (handler *InoHandler) transformClangdParams(ctx context.Context, method str
267
275
case "workspace/didChangeWatchedFiles" :
268
276
p := params .(* lsp.DidChangeWatchedFilesParams )
269
277
err = handler .ino2cppDidChangeWatchedFilesParams (p )
278
+ case "workspace/executeCommand" :
279
+ p := params .(* lsp.ExecuteCommandParams )
280
+ err = handler .ino2cppExecuteCommand (p )
270
281
}
271
282
return
272
283
}
@@ -495,18 +506,61 @@ func (handler *InoHandler) ino2cppDidChangeWatchedFilesParams(params *lsp.DidCha
495
506
return nil
496
507
}
497
508
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
+
498
542
func (handler * InoHandler ) transformClangdResult (method string , uri lsp.DocumentURI , result interface {}) interface {} {
499
543
switch method {
500
544
case "textDocument/completion" :
501
545
r := result .(* lsp.CompletionList )
502
546
handler .cpp2inoCompletionList (r , uri )
503
547
case "textDocument/codeAction" :
504
- r := result .(* []CodeAction )
548
+ r := result .(* []* commandOrCodeAction )
505
549
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
+ }
507
558
}
508
559
case "textDocument/hover" :
509
560
r := result .(* Hover )
561
+ if len (r .Contents .Value ) == 0 {
562
+ return nil
563
+ }
510
564
handler .cpp2inoHover (r , uri )
511
565
case "textDocument/definition" :
512
566
fallthrough
@@ -534,8 +588,26 @@ func (handler *InoHandler) transformClangdResult(method string, uri lsp.Document
534
588
handler .cpp2inoTextEdit (& (* r )[index ], uri )
535
589
}
536
590
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
+ }
539
611
case "textDocument/rename" :
540
612
r := result .(* lsp.WorkspaceEdit )
541
613
result = handler .cpp2inoWorkspaceEdit (r )
@@ -571,6 +643,15 @@ func (handler *InoHandler) cpp2inoCodeAction(codeAction *CodeAction, uri lsp.Doc
571
643
}
572
644
}
573
645
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
+
574
655
func (handler * InoHandler ) cpp2inoWorkspaceEdit (origEdit * lsp.WorkspaceEdit ) * lsp.WorkspaceEdit {
575
656
newEdit := lsp.WorkspaceEdit {Changes : make (map [string ][]lsp.TextEdit )}
576
657
for uri , edit := range origEdit .Changes {
@@ -658,7 +739,7 @@ func (handler *InoHandler) cpp2inoDocumentSymbols(origSymbols []DocumentSymbol,
658
739
659
740
// FromClangd handles a message received from clangd.
660
741
func (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 )
662
743
if err != nil {
663
744
log .Println ("From clangd: Method:" , req .Method , "Error:" , err )
664
745
return nil , err
@@ -679,7 +760,7 @@ func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.
679
760
return result , err
680
761
}
681
762
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 ) {
683
764
params , err = readParams (method , raw )
684
765
if err != nil {
685
766
return
@@ -692,6 +773,9 @@ func (handler *InoHandler) transformStdioParams(method string, raw *json.RawMess
692
773
p := params .(* lsp.PublishDiagnosticsParams )
693
774
uri = p .URI
694
775
err = handler .cpp2inoPublishDiagnosticsParams (p )
776
+ case "workspace/applyEdit" :
777
+ p := params .(* ApplyWorkspaceEditParams )
778
+ p .Edit = * handler .cpp2inoWorkspaceEdit (& p .Edit )
695
779
}
696
780
return
697
781
}
@@ -708,6 +792,34 @@ func (handler *InoHandler) cpp2inoPublishDiagnosticsParams(params *lsp.PublishDi
708
792
return nil
709
793
}
710
794
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
+
711
823
func (handler * InoHandler ) showMessage (ctx context.Context , msgType lsp.MessageType , message string ) {
712
824
params := lsp.ShowMessageParams {
713
825
Type : msgType ,
0 commit comments