@@ -5,8 +5,10 @@ import (
5
5
"encoding/json"
6
6
"flag"
7
7
"fmt"
8
+ "io/ioutil"
8
9
"log"
9
10
"net"
11
+ "net/url"
10
12
"os"
11
13
"os/signal"
12
14
"path/filepath"
@@ -21,9 +23,10 @@ import (
21
23
)
22
24
23
25
var (
24
- proxyAddr = flag .String ("proxyAddress" , "127.0.0.1:8080" , "proxy server listen address (tcp)" )
25
- cacheDir = flag .String ("cacheDirectory" , filepath .Join (os .TempDir (), "proxy-cache" ), "cache directory location" )
26
- trace = flag .Bool ("trace" , false , "trace logs to stderr" )
26
+ proxyAddr = flag .String ("proxyAddress" , "127.0.0.1:8080" , "proxy server listen address (tcp)" )
27
+ cacheDir = flag .String ("cacheDirectory" , filepath .Join (os .TempDir (), "proxy-cache" ), "cache directory location" )
28
+ didOpenLanguage = flag .String ("didOpenLanguage" , "" , "(HACK) If non-empty, send 'textDocument/didOpen' notifications with the specified language field (e.x. 'python') to the language server for every file." )
29
+ trace = flag .Bool ("trace" , false , "trace logs to stderr" )
27
30
)
28
31
29
32
type cloneProxy struct {
@@ -35,6 +38,10 @@ type cloneProxy struct {
35
38
36
39
ready chan struct {} // barrier to block handling requests until the proxy is fully initalized
37
40
ctx context.Context
41
+
42
+ // HACK
43
+ didOpenMu sync.Mutex
44
+ didOpen map [string ]bool
38
45
}
39
46
40
47
func (p * cloneProxy ) start () {
@@ -114,6 +121,7 @@ func main() {
114
121
ctx : ctx ,
115
122
sessionID : uuid .New (),
116
123
lastRequestID : newAtomicCounter (),
124
+ didOpen : map [string ]bool {},
117
125
}
118
126
119
127
var serverConnOpts []jsonrpc2.ConnOpt
@@ -177,7 +185,53 @@ func (p *cloneProxy) handleClientRequest(ctx context.Context, conn *jsonrpc2.Con
177
185
src : p .client ,
178
186
dest : p .server ,
179
187
180
- updateURIFromSrc : func (uri lsp.DocumentURI ) lsp.DocumentURI { return clientToServerURI (uri , p .workspaceCacheDir ()) },
188
+ updateURIFromSrc : func (uri lsp.DocumentURI ) lsp.DocumentURI {
189
+ uri = clientToServerURI (uri , p .workspaceCacheDir ())
190
+
191
+ // HACK
192
+ //
193
+ // Some language servers don't follow LSP correctly, and refuse to handle requests that
194
+ // operate on files that the language server hasn't received a a 'textDocument/didOpen'
195
+ // request for yet.
196
+ //
197
+ // See this issue for more context: https://github.com/Microsoft/language-server-protocol/issues/177
198
+ // There is also a corresponding PR to officialy put this clarification in the text:
199
+ // https://github.com/Microsoft/language-server-protocol/pull/431
200
+ //
201
+ // This hack is necessary to get those offending language servers to work at all.
202
+ //
203
+ // This is not indended to be a robust implementation, so there is no attempt to send
204
+ // matching 'textDocument/didClose' requests / etc.
205
+ if * didOpenLanguage != "" {
206
+ if parsedURI , err := url .Parse (string (uri )); err == nil && probablyFileURI (parsedURI ) {
207
+ p .didOpenMu .Lock ()
208
+ sent := p .didOpen [parsedURI .Path ]
209
+ if ! sent {
210
+ p .didOpen [parsedURI .Path ] = true
211
+ }
212
+ p .didOpenMu .Unlock ()
213
+
214
+ if ! sent {
215
+ b , err := ioutil .ReadFile (parsedURI .Path )
216
+ if err == nil {
217
+ err = p .server .Notify (ctx , "textDocument/didOpen" , & lsp.DidOpenTextDocumentParams {
218
+ TextDocument : lsp.TextDocumentItem {
219
+ URI : uri ,
220
+ LanguageID : * didOpenLanguage ,
221
+ Version : 1 ,
222
+ Text : string (b ),
223
+ },
224
+ })
225
+ if err != nil {
226
+ log .Println ("error sending didOpen" , err )
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ return uri
234
+ },
181
235
updateURIFromDest : func (uri lsp.DocumentURI ) lsp.DocumentURI { return serverToClientURI (uri , p .workspaceCacheDir ()) },
182
236
}
183
237
0 commit comments