Skip to content

Commit 54040e1

Browse files
authored
Merge pull request #38 from CodinGame/update-lsp-3.17
Update the LSP to the v3.17
2 parents d6f367e + 2112f76 commit 54040e1

File tree

12 files changed

+2708
-2519
lines changed

12 files changed

+2708
-2519
lines changed

package-lock.json

Lines changed: 2442 additions & 2377 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,62 +23,60 @@
2323
"types": "dist/index.d.ts",
2424
"dependencies": {
2525
"@codingame/monaco-editor-wrapper": "^1.14.4",
26-
"@codingame/monaco-jsonrpc": "^0.3.1",
27-
"once": "^1.4.0",
26+
"@codingame/monaco-jsonrpc": "^0.4.0",
2827
"sweetalert": "^2.1.2",
29-
"vscode-languageserver-protocol": "^3.16.0"
28+
"vscode-languageserver-protocol": "^3.17.1"
3029
},
3130
"devDependencies": {
32-
"@babel/core": "7.17.9",
33-
"@babel/plugin-proposal-class-properties": "7.16.7",
34-
"@babel/plugin-proposal-optional-chaining": "7.16.7",
35-
"@babel/plugin-transform-modules-commonjs": "^7.17.9",
36-
"@babel/preset-env": "7.16.11",
37-
"@babel/preset-typescript": "7.16.7",
38-
"@babel/runtime": "7.17.9",
39-
"@codingame/monaco-jsonrpc": "^0.3.1",
40-
"@codingame/monaco-languageclient": "^0.17.4",
31+
"@babel/core": "7.18.0",
32+
"@babel/plugin-proposal-class-properties": "7.17.12",
33+
"@babel/plugin-proposal-optional-chaining": "7.17.12",
34+
"@babel/plugin-transform-modules-commonjs": "^7.18.0",
35+
"@babel/preset-env": "7.18.0",
36+
"@babel/preset-typescript": "7.17.12",
37+
"@babel/runtime": "7.18.0",
38+
"@codingame/monaco-jsonrpc": "^0.4.0",
4139
"@rollup/plugin-alias": "3.1.9",
4240
"@rollup/plugin-babel": "5.3.1",
4341
"@rollup/plugin-commonjs": "22.0.0",
4442
"@rollup/plugin-eslint": "8.0.2",
4543
"@rollup/plugin-json": "4.1.0",
46-
"@rollup/plugin-node-resolve": "13.2.1",
44+
"@rollup/plugin-node-resolve": "13.3.0",
4745
"@rollup/plugin-typescript": "^8.3.2",
48-
"@types/jest": "^27.4.1",
46+
"@types/jest": "^27.5.1",
4947
"@types/once": "^1.4.0",
5048
"@types/rollup-plugin-node-builtins": "^2.1.2",
51-
"@types/vscode": "^1.66.0",
52-
"@typescript-eslint/eslint-plugin": "5.21.0",
53-
"@typescript-eslint/parser": "5.21.0",
49+
"@types/vscode": "^1.67.0",
50+
"@typescript-eslint/eslint-plugin": "5.25.0",
51+
"@typescript-eslint/parser": "5.25.0",
5452
"canvas": "^2.9.1",
5553
"conventional-changelog-conventionalcommits": "^4.6.3",
5654
"delay": "^5.0.0",
57-
"eslint": "8.14.0",
55+
"eslint": "8.16.0",
5856
"eslint-config-standard": "17.0.0",
5957
"eslint-config-standard-jsx": "11.0.0",
6058
"eslint-plugin-import": "2.26.0",
61-
"eslint-plugin-jest": "^26.1.5",
59+
"eslint-plugin-jest": "^26.2.2",
6260
"eslint-plugin-node": "11.1.0",
6361
"eslint-plugin-promise": "6.0.0",
6462
"eslint-plugin-unused-imports": "2.0.0",
65-
"jest": "^28.0.3",
66-
"jest-environment-jsdom": "^28.0.2",
67-
"monaco-languageclient": "^0.19.0-next.1",
63+
"jest": "^28.1.0",
64+
"jest-environment-jsdom": "^28.1.0",
65+
"monaco-languageclient": "^1.0.1",
6866
"proxy-polyfill": "^0.3.2",
69-
"rollup": "2.70.2",
67+
"rollup": "2.74.1",
7068
"rollup-plugin-dts": "^4.2.1",
7169
"rollup-plugin-node-builtins": "^2.1.2",
7270
"rollup-plugin-postcss": "4.0.2",
7371
"rollup-plugin-visualizer": "5.6.0",
7472
"rollup-plugin-web-worker-loader": "1.6.1",
7573
"tslib": "^2.4.0",
7674
"typescript": "4.6.4",
77-
"vscode-languageserver": "^7.0.0",
78-
"vscode-languageserver-protocol": "^3.16.0"
75+
"vscode-languageserver": "^8.0.1",
76+
"vscode-languageserver-protocol": "^3.17.1"
7977
},
8078
"resolutions": {
81-
"eslint": "8.14.0"
79+
"eslint": "8.16.0"
8280
},
8381
"browserslist": [
8482
"defaults",

src/createLanguageClient.ts

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,80 @@
1-
import { MessageSignature, MessageConnection } from '@codingame/monaco-jsonrpc'
1+
import { MessageReader, MessageWriter, Message, Event, DataCallback, Disposable, PartialMessageInfo } from 'vscode-jsonrpc'
22
import { Uri } from 'monaco-editor'
33
import {
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'
86
import { registerExtensionFeatures } from './extensions'
97
import { LanguageClientId } from './languageClientOptions'
108
import { 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
81125
class 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

src/extensions.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { monaco, registerTextModelContentProvider } from '@codingame/monaco-editor-wrapper'
22
import {
33
Disposable,
4-
ServerCapabilities, DocumentSelector, MonacoLanguageClient, StaticFeature, Services,
4+
ServerCapabilities, DocumentSelector, MonacoLanguageClient, Services,
55
TextDocumentSyncOptions, TextDocument, DidSaveTextDocumentNotification, Emitter, DisposableCollection
66
} from 'monaco-languageclient'
7+
import { StaticFeature, FeatureState, ProtocolRequestType } from 'vscode-languageclient'
78
import { updateFile, willShutdownNotificationType, WillShutdownParams } from './customRequests'
89
import { Infrastructure } from './infrastructure'
910
import { LanguageClient, LanguageClientManager } from './languageClient'
@@ -45,11 +46,18 @@ export class InitializeTextDocumentFeature implements StaticFeature {
4546
Services.get().workspace.textDocuments.forEach(saveFile)
4647
}
4748

49+
getState (): FeatureState {
50+
return {
51+
kind: 'static'
52+
}
53+
}
54+
4855
dispose (): void {
4956
this.didOpenTextDocumentDisposable?.dispose()
5057
}
5158
}
5259

60+
export const ResolveCobolSubroutineRequestType = new ProtocolRequestType<string, string, never, void, void>('cobol/resolveSubroutine')
5361
class CobolResolveSubroutineFeature implements StaticFeature {
5462
private onRequestDisposable: Disposable | undefined
5563
constructor (private languageClient: MonacoLanguageClient) {
@@ -58,7 +66,7 @@ class CobolResolveSubroutineFeature implements StaticFeature {
5866
fillClientCapabilities (): void {}
5967

6068
initialize (capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
61-
this.onRequestDisposable = this.languageClient.onRequest('cobol/resolveSubroutine', (routineName: string) => {
69+
this.onRequestDisposable = this.languageClient.onRequest(ResolveCobolSubroutineRequestType, (routineName: string) => {
6270
const constantRoutinePaths: Partial<Record<string, string>> = {
6371
'assert-equals': `${Services.get().workspace.rootUri ?? 'file:/tmp/project'}/deps/assert-equals.cbl`
6472
}
@@ -73,6 +81,12 @@ class CobolResolveSubroutineFeature implements StaticFeature {
7381
})
7482
}
7583

84+
getState (): FeatureState {
85+
return {
86+
kind: 'static'
87+
}
88+
}
89+
7690
dispose (): void {
7791
this.onRequestDisposable?.dispose()
7892
}
@@ -93,6 +107,12 @@ export class WillDisposeFeature implements StaticFeature {
93107
})
94108
}
95109

110+
getState (): FeatureState {
111+
return {
112+
kind: 'static'
113+
}
114+
}
115+
96116
dispose (): void {}
97117
}
98118

@@ -112,7 +132,9 @@ export class FileSystemFeature implements StaticFeature {
112132
}))
113133
disposableCollection.push(getServices().workspace.registerSaveDocumentHandler({
114134
async saveTextContent (textDocument, reason) {
115-
await infrastructure.saveFileContent?.(textDocument, reason, languageClientManager)
135+
if (languageClientManager.isModelManaged(textDocument)) {
136+
await infrastructure.saveFileContent?.(textDocument, reason, languageClientManager)
137+
}
116138
}
117139
}))
118140
return disposableCollection
@@ -125,6 +147,12 @@ export class FileSystemFeature implements StaticFeature {
125147
this.disposable = this.registerFileHandlers()
126148
}
127149

150+
getState (): FeatureState {
151+
return {
152+
kind: 'static'
153+
}
154+
}
155+
128156
dispose (): void {
129157
this.disposable?.dispose()
130158
this.disposable = undefined

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TextDocument, TextDocumentSaveReason } from 'monaco-languageclient'
22
import 'proxy-polyfill'
3-
import { WorkspaceFolder } from 'vscode'
3+
import type { WorkspaceFolder } from 'vscode'
44
import { CodinGameInfrastructure, Infrastructure } from './infrastructure'
55
import { WillShutdownParams } from './customRequests'
66
import { loadExtensionConfigurations } from './extensionConfiguration'

0 commit comments

Comments
 (0)