1- import { MessageSignature , MessageConnection } from '@codingame/monaco -jsonrpc'
1+ import { MessageReader , MessageWriter , Message , Event , DataCallback , Disposable , PartialMessageInfo } from 'vscode -jsonrpc'
22import { Uri } from 'monaco-editor'
33import {
4- MonacoLanguageClient ,
5- createConnection , ConnectionErrorHandler , ConnectionCloseHandler , IConnection , Middleware , ErrorHandler , IConnectionProvider , InitializeParams , RegistrationRequest , RegistrationParams , UnregistrationRequest , UnregistrationParams , LanguageClientOptions
4+ MonacoLanguageClient , Middleware , ErrorHandler , IConnectionProvider , InitializeParams , RegistrationRequest , RegistrationParams , UnregistrationRequest , UnregistrationParams , LanguageClientOptions , MessageTransports , InitializeRequest
65} from 'monaco-languageclient'
7- import once from 'once'
86import { registerExtensionFeatures } from './extensions'
97import { LanguageClientId } from './languageClientOptions'
108import { Infrastructure } from './infrastructure'
119
12- async function messageConnectionToConnection ( messageConnection : MessageConnection , errorHandler : ConnectionErrorHandler , closeHandler : ( ) => void ) : Promise < IConnection > {
13- const connection = createConnection ( messageConnection , errorHandler , closeHandler )
10+ interface MessageMiddleware {
11+ ( message : Message ) : Message
12+ }
13+
14+ class MiddlewareMessageWriter implements MessageWriter {
15+ constructor ( private delegate : MessageWriter , private middleware : MessageMiddleware ) { }
16+
17+ onError : Event < [ Error , Message | undefined , number | undefined ] > = ( cb ) => {
18+ return this . delegate . onError ( cb )
19+ }
20+
21+ onClose : Event < void > = ( cb ) => {
22+ return this . delegate . onClose ( cb )
23+ }
24+
25+ dispose ( ) : void {
26+ this . delegate . dispose ( )
27+ }
28+
29+ write ( msg : Message ) : Promise < void > {
30+ return this . delegate . write ( this . middleware ( msg ) )
31+ }
32+
33+ end ( ) : void {
34+ return this . delegate . end ( )
35+ }
36+ }
37+ class MiddlewareMessageReader implements MessageReader {
38+ constructor ( private delegate : MessageReader , private middleware : MessageMiddleware ) { }
39+
40+ onError : Event < Error > = ( cb ) => {
41+ return this . delegate . onError ( cb )
42+ }
43+
44+ onClose : Event < void > = ( cb ) => {
45+ return this . delegate . onClose ( cb )
46+ }
47+
48+ onPartialMessage : Event < PartialMessageInfo > = ( cb ) => {
49+ return this . delegate . onPartialMessage ( cb )
50+ }
1451
52+ listen ( callback : DataCallback ) : Disposable {
53+ return this . delegate . listen ( message => {
54+ callback ( this . middleware ( message ) )
55+ } )
56+ }
57+
58+ dispose ( ) : void {
59+ this . delegate . dispose ( )
60+ }
61+ }
62+
63+ function hackTransports ( transports : MessageTransports ) : MessageTransports {
1564 const existingRegistrations = new Set < string > ( )
16- const fixedConnection : IConnection = {
17- ...connection ,
18- initialize : ( params : InitializeParams ) => {
19- // Hack to fix url converted from /toto/tata to \\toto\tata in windows
20- const rootPath = params . rootPath ?. replace ( / \\ / g, '/' )
21- const fixedParams : InitializeParams = {
22- ...params ,
23- rootPath,
24- rootUri : rootPath != null ? Uri . from ( { scheme : 'file' , path : rootPath } ) . toString ( ) : null
25- }
26- return connection . initialize ( fixedParams )
27- } ,
28- onRequest ( ...args : Parameters < typeof connection . onRequest > ) {
29- return connection . onRequest ( args [ 0 ] , ( ...params ) => {
30- // Hack for https://github.com/OmniSharp/omnisharp-roslyn/issues/2119
31- const method = ( args [ 0 ] as MessageSignature ) . method
32- if ( method === RegistrationRequest . type . method ) {
33- const registrationParams = params [ 0 ] as unknown as RegistrationParams
34- registrationParams . registrations = registrationParams . registrations . filter ( registration => {
65+ return {
66+ reader : new MiddlewareMessageReader ( transports . reader , message => {
67+ if ( Message . isRequest ( message ) ) {
68+ if ( message . method === RegistrationRequest . type . method ) {
69+ const registrationParams = message . params as RegistrationParams
70+ const filteredRegistrations = registrationParams . registrations . filter ( registration => {
3571 const alreadyExisting = existingRegistrations . has ( registration . id )
3672 if ( alreadyExisting ) {
3773 console . warn ( 'Registration already existing' , registration . id , registration . method )
@@ -41,64 +77,53 @@ async function messageConnectionToConnection (messageConnection: MessageConnecti
4177 registrationParams . registrations . forEach ( registration => {
4278 existingRegistrations . add ( registration . id )
4379 } )
80+ const fixedParams : RegistrationParams = {
81+ ...registrationParams ,
82+ registrations : filteredRegistrations
83+ }
84+ return {
85+ ...message ,
86+ params : fixedParams
87+ }
4488 }
45- if ( method === UnregistrationRequest . type . method ) {
46- const unregistrationParams = params [ 0 ] as unknown as UnregistrationParams
89+ if ( message . method === UnregistrationRequest . type . method ) {
90+ const unregistrationParams = message . params as UnregistrationParams
4791 for ( const unregistration of unregistrationParams . unregisterations ) {
4892 existingRegistrations . delete ( unregistration . id )
4993 }
5094 }
51- return args [ 1 ] ( ...params )
52- } )
53- } ,
54- dispose : ( ) => {
55- try {
56- connection . dispose ( )
57- } catch ( error ) {
58- // The dispose should NEVER fail or the lsp client is not properly cleaned
59- // see https://github.com/microsoft/vscode-languageserver-node/blob/master/client/src/client.ts#L3105
60- console . warn ( '[LSP]' , 'Error while disposing connection' , error )
6195 }
62- // Hack, when the language client is removed, the connection is disposed but the closeHandler is not always properly called
63- // The language client is then still active but without a proper connection and errors will occurs
64- closeHandler ( )
65- } ,
66- shutdown : async ( ) => {
67- // The shutdown should NEVER fail or the connection is not closed and the lsp client is not properly cleaned
68- // see https://github.com/microsoft/vscode-languageserver-node/blob/master/client/src/client.ts#L3103
69- try {
70- await connection . shutdown ( )
71- } catch ( error ) {
72- console . warn ( '[LSP]' , 'Error while shutdown lsp' , error )
96+ return message
97+ } ) ,
98+ writer : new MiddlewareMessageWriter ( transports . writer , message => {
99+ if ( Message . isRequest ( message ) && message . method === InitializeRequest . type . method ) {
100+ const params = message . params as InitializeParams
101+ // Hack to fix url converted from /toto/tata to \\toto\tata in windows
102+ const rootPath = params . rootPath ?. replace ( / \\ / g, '/' )
103+ const fixedParams : InitializeParams = {
104+ ...params ,
105+ rootPath,
106+ rootUri : rootPath != null ? Uri . from ( { scheme : 'file' , path : rootPath } ) . toString ( ) : null
107+ }
108+ return {
109+ ...message ,
110+ params : fixedParams
111+ }
73112 }
74- }
113+ return message
114+ } )
75115 }
76-
77- return fixedConnection
78116}
79117
80- const RETRY_DELAY = 3000
81118class CGLSPConnectionProvider implements IConnectionProvider {
82119 constructor (
83120 private id : LanguageClientId ,
84121 private infrastructure : Infrastructure
85122 ) {
86123 }
87124
88- async get ( errorHandler : ConnectionErrorHandler , closeHandler : ConnectionCloseHandler ) {
89- const onceDelayedCloseHandler = once ( ( ) => {
90- setTimeout ( ( ) => {
91- closeHandler ( )
92- } , RETRY_DELAY )
93- } )
94- try {
95- const connection = await this . infrastructure . openConnection ( this . id )
96-
97- return await messageConnectionToConnection ( connection , errorHandler , onceDelayedCloseHandler )
98- } catch ( err ) {
99- onceDelayedCloseHandler ( )
100- throw err
101- }
125+ async get ( ) {
126+ return hackTransports ( await this . infrastructure . openConnection ( this . id ) )
102127 }
103128}
104129
0 commit comments