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+ /**
64+ * Add some hacks on transform for:
65+ * - Dedup server capability registrations (for omnisharp roslyn)
66+ * - Fix paths on windows
67+ * @param transports The original transports
68+ * @returns The transformed transports
69+ */
70+ function hackTransports ( transports : MessageTransports ) : MessageTransports {
1571 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 => {
72+ return {
73+ reader : new MiddlewareMessageReader ( transports . reader , message => {
74+ if ( Message . isRequest ( message ) ) {
75+ if ( message . method === RegistrationRequest . type . method ) {
76+ const registrationParams = message . params as RegistrationParams
77+ const filteredRegistrations = registrationParams . registrations . filter ( registration => {
3578 const alreadyExisting = existingRegistrations . has ( registration . id )
3679 if ( alreadyExisting ) {
3780 console . warn ( 'Registration already existing' , registration . id , registration . method )
@@ -41,64 +84,53 @@ async function messageConnectionToConnection (messageConnection: MessageConnecti
4184 registrationParams . registrations . forEach ( registration => {
4285 existingRegistrations . add ( registration . id )
4386 } )
87+ const fixedParams : RegistrationParams = {
88+ ...registrationParams ,
89+ registrations : filteredRegistrations
90+ }
91+ return {
92+ ...message ,
93+ params : fixedParams
94+ }
4495 }
45- if ( method === UnregistrationRequest . type . method ) {
46- const unregistrationParams = params [ 0 ] as unknown as UnregistrationParams
96+ if ( message . method === UnregistrationRequest . type . method ) {
97+ const unregistrationParams = message . params as UnregistrationParams
4798 for ( const unregistration of unregistrationParams . unregisterations ) {
4899 existingRegistrations . delete ( unregistration . id )
49100 }
50101 }
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 )
61102 }
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 )
103+ return message
104+ } ) ,
105+ writer : new MiddlewareMessageWriter ( transports . writer , message => {
106+ if ( Message . isRequest ( message ) && message . method === InitializeRequest . type . method ) {
107+ const params = message . params as InitializeParams
108+ // Hack to fix url converted from /toto/tata to \\toto\tata in windows
109+ const rootPath = params . rootPath ?. replace ( / \\ / g, '/' )
110+ const fixedParams : InitializeParams = {
111+ ...params ,
112+ rootPath,
113+ rootUri : rootPath != null ? Uri . from ( { scheme : 'file' , path : rootPath } ) . toString ( ) : null
114+ }
115+ return {
116+ ...message ,
117+ params : fixedParams
118+ }
73119 }
74- }
120+ return message
121+ } )
75122 }
76-
77- return fixedConnection
78123}
79124
80- const RETRY_DELAY = 3000
81125class CGLSPConnectionProvider implements IConnectionProvider {
82126 constructor (
83127 private id : LanguageClientId ,
84128 private infrastructure : Infrastructure
85129 ) {
86130 }
87131
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- }
132+ async get ( ) {
133+ return hackTransports ( await this . infrastructure . openConnection ( this . id ) )
102134 }
103135}
104136
0 commit comments