Skip to content

Commit 41498bd

Browse files
authored
Merge pull request #25 from CodinGame/coderpad-fixes
Fixes for coderpad
2 parents 6714a72 + d647a4e commit 41498bd

File tree

6 files changed

+70
-25
lines changed

6 files changed

+70
-25
lines changed

src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { WillShutdownParams } from './customRequests'
66
import { loadExtensionConfigurations } from './extensionConfiguration'
77
import './hacks'
88
import { createLanguageClientManager, LanguageClientManager, StatusChangeEvent } from './languageClient'
9-
import { LanguageClientId, registerLanguageClient } from './languageClientOptions'
9+
import { LanguageClientId, LanguageClientOptions, registerLanguageClient } from './languageClientOptions'
1010
import { StaticLanguageClientId } from './staticOptions'
1111

1212
export {
@@ -25,5 +25,6 @@ export type {
2525
Infrastructure,
2626
WorkspaceFolder,
2727
TextDocument,
28-
TextDocumentSaveReason
28+
TextDocumentSaveReason,
29+
LanguageClientOptions
2930
}

src/infrastructure.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as monaco from 'monaco-editor'
55
import type * as vscode from 'vscode'
66
import { getFile, updateFile } from './customRequests'
77
import { LanguageClientManager } from './languageClient'
8-
import { LanguageClientId } from './languageClientOptions'
8+
import { LanguageClientId, LanguageClientOptions } from './languageClientOptions'
99

1010
export interface Infrastructure {
1111
/**
@@ -22,10 +22,11 @@ export interface Infrastructure {
2222
* Workspace folders
2323
*/
2424
workspaceFolders?: typeof vscode.workspace.workspaceFolders
25+
2526
/**
26-
* The language server proxy is used, so we only need to load configurations for language servers which are not mutualized
27+
* Does a mutualization proxy will be used, it means we don't need to load configurations for this server
2728
*/
28-
useMutualizedProxy: boolean
29+
useMutualizedProxy (languageClientId: LanguageClientId, options: LanguageClientOptions): boolean
2930

3031
/**
3132
* Save a file on the filesystem
@@ -35,11 +36,11 @@ export interface Infrastructure {
3536
*/
3637
saveFileContent? (document: TextDocument, reason: TextDocumentSaveReason, languageClient: LanguageClientManager): Promise<void>
3738
/**
38-
* Get a text file content
39+
* Get a text file content as a model
3940
* @param resource the Uri of the file
4041
* @param languageClient The languageclient we're trying to get the file from
4142
*/
42-
getFileContent (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<string | null>
43+
getFileContent (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<monaco.editor.ITextModel | null>
4344

4445
/**
4546
* Open a connection to the language server
@@ -74,7 +75,7 @@ export abstract class CodinGameInfrastructure implements Infrastructure {
7475
* The domain of the server
7576
*/
7677
public serverAddress: string,
77-
public useMutualizedProxy: boolean,
78+
private _useMutualizedProxy: boolean,
7879
/**
7980
* An optional sessionId when connecting to the session-mutualized server
8081
*/
@@ -86,6 +87,10 @@ export abstract class CodinGameInfrastructure implements Infrastructure {
8687
) {
8788
}
8889

90+
useMutualizedProxy (languageClientId: LanguageClientId, options: LanguageClientOptions): boolean {
91+
return this._useMutualizedProxy && options.mutualizable
92+
}
93+
8994
public readonly automaticTextDocumentUpdate = false
9095
public readonly rootUri = 'file:///tmp/project'
9196
public readonly workspaceFolders: typeof vscode.workspace.workspaceFolders = [{
@@ -100,9 +105,10 @@ export abstract class CodinGameInfrastructure implements Infrastructure {
100105
}
101106
}
102107

103-
public async getFileContent (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<string | null> {
108+
public async getFileContent (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<monaco.editor.ITextModel | null> {
104109
try {
105-
return (await getFile(resource.toString(true), languageClient)).text
110+
const content = (await getFile(resource.toString(true), languageClient)).text
111+
return monaco.editor.createModel(content, undefined, resource)
106112
} catch (error) {
107113
console.error('File not found', resource.toString())
108114
return null

src/languageClient.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as monaco from 'monaco-editor'
22
import {
3-
CloseAction, ErrorAction, MonacoLanguageClient, Emitter, Event, TextDocument, Services, State, DisposableCollection, CancellationToken, RequestType, NotificationType, Disposable
3+
CloseAction, ErrorAction, MonacoLanguageClient, Emitter, Event, TextDocument, Services, State, DisposableCollection, CancellationToken, RequestType, NotificationType, Disposable, LogMessageNotification
44
} from 'monaco-languageclient'
55
import delay from 'delay'
66
import { Uri } from 'monaco-editor'
@@ -32,12 +32,14 @@ export class LanguageClientManager implements LanguageClient {
3232
protected readonly onWillCloseEmitter = new Emitter<void>()
3333
protected readonly onWillShutdownEmitter = new Emitter<WillShutdownParams>()
3434
protected currentStatus: Status = 'connecting'
35+
private useMutualizedProxy: boolean
3536

3637
constructor (
3738
private id: LanguageClientId,
3839
private languageServerOptions: LanguageClientOptions,
3940
private infrastructure: Infrastructure
4041
) {
42+
this.useMutualizedProxy = this.infrastructure.useMutualizedProxy(this.id, this.languageServerOptions)
4143
}
4244

4345
private updateStatus (status: Status) {
@@ -109,7 +111,7 @@ export class LanguageClientManager implements LanguageClient {
109111

110112
public async start (): Promise<void> {
111113
try {
112-
await loadExtensionConfigurations([this.id], this.infrastructure.useMutualizedProxy)
114+
await loadExtensionConfigurations([this.id], this.useMutualizedProxy)
113115
} catch (error) {
114116
console.error('Unable to load extension configuration', error)
115117
}
@@ -201,11 +203,26 @@ export class LanguageClientManager implements LanguageClient {
201203
this.updateStatus('connecting')
202204
readyPromise = languageClient.onReady().then(async () => {
203205
const disposableCollection = new DisposableCollection()
204-
await Promise.race([
205-
new Promise<void>(resolve => {
206+
207+
let readyPromise: Promise<void>
208+
const { maxInitializeDuration, readinessMessageMatcher } = this.languageServerOptions
209+
if (readinessMessageMatcher != null && !this.useMutualizedProxy) {
210+
readyPromise = new Promise<void>(resolve => {
211+
disposableCollection.push(languageClient.onNotification(LogMessageNotification.type, logMessage => {
212+
if (readinessMessageMatcher.exec(logMessage.message) != null) {
213+
resolve()
214+
}
215+
}))
216+
})
217+
} else {
218+
readyPromise = new Promise<void>(resolve => {
206219
disposableCollection.push(onServerResponse.event(resolve))
207-
}),
208-
delay(15000)
220+
})
221+
}
222+
223+
await Promise.race([
224+
readyPromise,
225+
delay(maxInitializeDuration ?? 15_000)
209226
])
210227
disposableCollection.dispose()
211228
}, error => {
@@ -268,7 +285,7 @@ function createLanguageClientManager (
268285
throw new Error(`Unknown ${id} language server`)
269286
}
270287

271-
if (infrastructure.useMutualizedProxy && languageServerOptions.mutualizable) {
288+
if (infrastructure.useMutualizedProxy(id, languageServerOptions) && languageServerOptions.mutualizable) {
272289
// When using the mutualized proxy, we don't need to synchronize the configuration nor send the initialization options
273290
languageServerOptions = {
274291
...languageServerOptions,
@@ -289,8 +306,7 @@ function createLanguageClientManager (
289306

290307
disposableCollection.push(registerTextModelContentProvider('file', {
291308
async provideTextContent (resource: Uri): Promise<monaco.editor.ITextModel | null> {
292-
const content = await infrastructure.getFileContent(resource, languageClientManager)
293-
return content != null ? monaco.editor.createModel(content, undefined, resource) : null
309+
return await infrastructure.getFileContent(resource, languageClientManager)
294310
}
295311
}))
296312
disposableCollection.push(getServices().workspace.registerSaveDocumentHandler({

src/languageClientOptions.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,20 @@ import staticOptions, { StaticLanguageClientId } from './staticOptions'
33

44
export type LanguageClientOptions = Pick<MonacoLanguageClientOptions, 'documentSelector' | 'synchronize' | 'initializationOptions' | 'middleware'> & {
55
vscodeExtensionIds?: string[]
6+
/**
7+
* Is this language server mutualizable by the CodinGame mutualized proxy
8+
*/
69
mutualizable: boolean
10+
/**
11+
* Maximum initialization duration. After this delay, the language server will be considered ready no matter what
12+
* default: 15_000
13+
*/
14+
maxInitializeDuration?: number
15+
16+
/**
17+
* The language server will only be considered ready after this log message was received
18+
*/
19+
readinessMessageMatcher?: RegExp
720
}
821

922
const dynamicOptions: Partial<Record<string, LanguageClientOptions>> = {}

src/staticOptions.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ const staticOptions = asLanguageClientOptionsById({
134134
synchronize: {
135135
configurationSection: 'kotlin'
136136
},
137-
mutualizable: false
137+
mutualizable: false,
138+
maxInitializeDuration: 60_000
138139
},
139140
lua: {
140141
documentSelector: [
@@ -272,7 +273,9 @@ const staticOptions = asLanguageClientOptionsById({
272273
configurationSection: 'metals'
273274
},
274275
mutualizable: false,
275-
vscodeExtensionIds: ['scalameta']
276+
vscodeExtensionIds: ['scalameta'],
277+
maxInitializeDuration: 60_000,
278+
readinessMessageMatcher: /compiled scala-project in/
276279
},
277280
sql: {
278281
documentSelector: [

src/tests/tools.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import {
66
_Connection,
77
_
88
} from 'vscode-languageserver/lib/common/api'
9+
import { monaco } from '@codingame/monaco-editor-wrapper'
910
import { getFile, updateFile } from '../customRequests'
10-
import { Infrastructure, LanguageClientManager, TextDocument, TextDocumentSaveReason } from '../'
11+
import { Infrastructure, LanguageClientId, LanguageClientManager, LanguageClientOptions, TextDocument, TextDocumentSaveReason } from '../'
1112

1213
class PipedMessageReader extends AbstractMessageReader {
1314
private callback: DataCallback | undefined
@@ -120,9 +121,13 @@ export class TestInfrastructure implements Infrastructure {
120121

121122
constructor (
122123
public automaticTextDocumentUpdate: boolean,
123-
public useMutualizedProxy: boolean
124+
public _useMutualizedProxy: boolean
124125
) {}
125126

127+
useMutualizedProxy (languageClientId: LanguageClientId, options: LanguageClientOptions): boolean {
128+
return this._useMutualizedProxy && options.mutualizable
129+
}
130+
126131
rootUri = 'file:///tmp/project'
127132
workspaceFolders = []
128133

@@ -131,9 +136,10 @@ export class TestInfrastructure implements Infrastructure {
131136
}
132137

133138
// use same method as CodinGameInfrastructure to be able to simply catch it
134-
async getFileContent (resource: Uri, languageClient: LanguageClientManager): Promise<string | null> {
139+
async getFileContent (resource: Uri, languageClient: LanguageClientManager): Promise<monaco.editor.ITextModel | null> {
135140
try {
136-
return (await getFile(resource.toString(true), languageClient)).text
141+
const content = (await getFile(resource.toString(true), languageClient)).text
142+
return monaco.editor.createModel(content, undefined, resource)
137143
} catch (error) {
138144
console.error('File not found', resource.toString())
139145
return null

0 commit comments

Comments
 (0)