@@ -5,8 +5,10 @@ import (
55 "encoding/json"
66 "flag"
77 "fmt"
8+ "io/ioutil"
89 "log"
910 "net"
11+ "net/url"
1012 "os"
1113 "os/signal"
1214 "path/filepath"
@@ -21,9 +23,10 @@ import (
2123)
2224
2325var (
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" )
2730)
2831
2932type cloneProxy struct {
@@ -35,6 +38,10 @@ type cloneProxy struct {
3538
3639 ready chan struct {} // barrier to block handling requests until the proxy is fully initalized
3740 ctx context.Context
41+
42+ // HACK
43+ didOpenMu sync.Mutex
44+ didOpen map [string ]bool
3845}
3946
4047func (p * cloneProxy ) start () {
@@ -114,6 +121,7 @@ func main() {
114121 ctx : ctx ,
115122 sessionID : uuid .New (),
116123 lastRequestID : newAtomicCounter (),
124+ didOpen : map [string ]bool {},
117125 }
118126
119127 var serverConnOpts []jsonrpc2.ConnOpt
@@ -177,7 +185,53 @@ func (p *cloneProxy) handleClientRequest(ctx context.Context, conn *jsonrpc2.Con
177185 src : p .client ,
178186 dest : p .server ,
179187
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+ },
181235 updateURIFromDest : func (uri lsp.DocumentURI ) lsp.DocumentURI { return serverToClientURI (uri , p .workspaceCacheDir ()) },
182236 }
183237
0 commit comments