Skip to content

Commit 1b5c49a

Browse files
committed
feat: abstract lean client setup for websocket clients
1 parent fd8ad21 commit 1b5c49a

4 files changed

Lines changed: 85 additions & 84 deletions

File tree

vscode-lean4/src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { PreconditionCheckResult, SetupNotificationOptions } from './diagnostics
1010
import { AlwaysEnabledFeatures, Exports, Lean4EnabledFeatures } from './exports'
1111
import { InfoProvider } from './infoview'
1212
import { LeanClient } from './leanclient'
13+
import { setupClient } from './leanclientsetup'
1314
import { LoogleView } from './loogleview'
1415
import { ManualView } from './manualview'
1516
import { MoogleView } from './moogleview'
@@ -177,7 +178,7 @@ async function activateLean4Features(
177178
installer: LeanInstaller,
178179
elanCommandProvider: ElanCommandProvider,
179180
): Promise<Lean4EnabledFeatures> {
180-
const clientProvider = new LeanClientProvider(installer, installer.getOutputChannel())
181+
const clientProvider = new LeanClientProvider(installer, installer.getOutputChannel(), setupClient)
181182
elanCommandProvider.setClientProvider(clientProvider)
182183
context.subscriptions.push(clientProvider)
183184

vscode-lean4/src/leanclient.ts

Lines changed: 14 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,15 @@ import {
1313
WorkspaceFolder,
1414
} from 'vscode'
1515
import {
16-
ClientCapabilities,
16+
BaseLanguageClient,
1717
DiagnosticSeverity,
1818
DidChangeTextDocumentParams,
1919
DidCloseTextDocumentParams,
2020
DocumentFilter,
2121
InitializeResult,
22-
LanguageClient,
2322
LanguageClientOptions,
2423
RevealOutputChannelOn,
25-
ServerOptions,
2624
State,
27-
StaticFeature,
2825
} from 'vscode-languageclient/node'
2926

3027
import {
@@ -33,25 +30,12 @@ import {
3330
LeanFileProgressProcessingInfo,
3431
ServerStoppedReason,
3532
} from '@leanprover/infoview-api'
36-
import {
37-
getElaborationDelay,
38-
getFallBackToStringOccurrenceHighlighting,
39-
serverArgs,
40-
serverLoggingEnabled,
41-
serverLoggingPath,
42-
shouldAutofocusOutput,
43-
} from './config'
33+
import { getElaborationDelay, getFallBackToStringOccurrenceHighlighting, shouldAutofocusOutput } from './config'
4434
import { logger } from './utils/logger'
4535
// @ts-ignore
4636
import path from 'path'
4737
import { SemVer } from 'semver'
48-
import {
49-
c2pConverter,
50-
LeanPublishDiagnosticsParams,
51-
p2cConverter,
52-
patchConverters,
53-
setDependencyBuildMode,
54-
} from './utils/converters'
38+
import { c2pConverter, LeanPublishDiagnosticsParams, p2cConverter, setDependencyBuildMode } from './utils/converters'
5539
import { elanInstalledToolchains } from './utils/elan'
5640
import { ExtUri, parseExtUri, toExtUri } from './utils/exturi'
5741
import { leanRunner } from './utils/leanCmdRunner'
@@ -61,7 +45,6 @@ import {
6145
displayNotificationWithOptionalInput,
6246
displayNotificationWithOutput,
6347
} from './utils/notifs'
64-
import { willUseLakeServer } from './utils/projectInfo'
6548

6649
interface LeanClientCapabilties {
6750
silentDiagnosticSupport?: boolean | undefined
@@ -77,7 +60,7 @@ export type ServerProgress = Map<ExtUri, LeanFileProgressProcessingInfo[]>
7760

7861
export class LeanClient implements Disposable {
7962
running: boolean
80-
private client: LanguageClient | undefined
63+
private client: BaseLanguageClient | undefined
8164
private outputChannel: OutputChannel
8265
folderUri: ExtUri
8366
private subscriptions: Disposable[] = []
@@ -125,7 +108,15 @@ export class LeanClient implements Disposable {
125108
private serverFailedEmitter = new EventEmitter<string>()
126109
serverFailed = this.serverFailedEmitter.event
127110

128-
constructor(folderUri: ExtUri, outputChannel: OutputChannel) {
111+
constructor(
112+
folderUri: ExtUri,
113+
outputChannel: OutputChannel,
114+
private setupClient: (
115+
toolchainOverride: string | undefined,
116+
clientOptions: LanguageClientOptions,
117+
folderUri: ExtUri,
118+
) => Promise<BaseLanguageClient>,
119+
) {
129120
this.outputChannel = outputChannel
130121
this.folderUri = folderUri
131122
this.subscriptions.push(new Disposable(() => this.staleDepNotifier?.dispose()))
@@ -272,7 +263,7 @@ export class LeanClient implements Disposable {
272263
const toolchainOverride: string | undefined =
273264
toolchainOverrideResult.kind === 'Override' ? toolchainOverrideResult.toolchain : undefined
274265

275-
this.client = await this.setupClient(toolchainOverride)
266+
this.client = await this.setupClient(toolchainOverride, this.obtainClientOptions(), this.folderUri)
276267

277268
let insideRestart = true
278269
try {
@@ -521,43 +512,6 @@ export class LeanClient implements Disposable {
521512
return this.running ? this.client?.initializeResult : undefined
522513
}
523514

524-
private async determineServerOptions(toolchainOverride: string | undefined): Promise<ServerOptions> {
525-
const env = Object.assign({}, process.env)
526-
if (serverLoggingEnabled()) {
527-
env.LEAN_SERVER_LOG_DIR = serverLoggingPath()
528-
}
529-
530-
const [serverExecutable, options] = await this.determineExecutable()
531-
if (toolchainOverride) {
532-
options.unshift('+' + toolchainOverride)
533-
}
534-
535-
const cwd = this.folderUri.scheme === 'file' ? this.folderUri.fsPath : undefined
536-
if (cwd) {
537-
// Add folder name to command-line so that it shows up in `ps aux`.
538-
options.push(cwd)
539-
} else {
540-
options.push('untitled')
541-
}
542-
543-
return {
544-
command: serverExecutable,
545-
args: options.concat(serverArgs()),
546-
options: {
547-
cwd,
548-
env,
549-
},
550-
}
551-
}
552-
553-
private async determineExecutable(): Promise<[string, string[]]> {
554-
if (await willUseLakeServer(this.folderUri)) {
555-
return ['lake', ['serve', '--']]
556-
} else {
557-
return ['lean', ['--server']]
558-
}
559-
}
560-
561515
private obtainClientOptions(): LanguageClientOptions {
562516
const documentSelector: DocumentFilter = {
563517
language: 'lean4',
@@ -688,25 +642,4 @@ export class LeanClient implements Disposable {
688642
},
689643
}
690644
}
691-
692-
private async setupClient(toolchainOverride: string | undefined): Promise<LanguageClient> {
693-
const serverOptions: ServerOptions = await this.determineServerOptions(toolchainOverride)
694-
const clientOptions: LanguageClientOptions = this.obtainClientOptions()
695-
696-
const client = new LanguageClient('lean4', 'Lean 4', serverOptions, clientOptions)
697-
const leanCapabilityFeature: StaticFeature = {
698-
initialize(_1, _2) {},
699-
getState() {
700-
return { kind: 'static' }
701-
},
702-
fillClientCapabilities(capabilities: ClientCapabilities & { lean?: LeanClientCapabilties | undefined }) {
703-
capabilities.lean = leanClientCapabilities
704-
},
705-
dispose() {},
706-
}
707-
client.registerFeature(leanCapabilityFeature)
708-
709-
patchConverters(client.protocol2CodeConverter, client.code2ProtocolConverter)
710-
return client
711-
}
712645
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'
2+
import { serverArgs, serverLoggingEnabled, serverLoggingPath } from './config'
3+
import { patchConverters } from './utils/converters'
4+
import { ExtUri } from './utils/exturi'
5+
import { willUseLakeServer } from './utils/projectInfo'
6+
7+
async function determineExecutable(folderUri: ExtUri): Promise<[string, string[]]> {
8+
if (await willUseLakeServer(folderUri)) {
9+
return ['lake', ['serve', '--']]
10+
} else {
11+
return ['lean', ['--server']]
12+
}
13+
}
14+
15+
async function determineServerOptions(
16+
toolchainOverride: string | undefined,
17+
folderUri: ExtUri,
18+
): Promise<ServerOptions> {
19+
const env = Object.assign({}, process.env)
20+
if (serverLoggingEnabled()) {
21+
env.LEAN_SERVER_LOG_DIR = serverLoggingPath()
22+
}
23+
24+
const [serverExecutable, options] = await determineExecutable(folderUri)
25+
if (toolchainOverride) {
26+
options.unshift('+' + toolchainOverride)
27+
}
28+
29+
const cwd = folderUri.scheme === 'file' ? folderUri.fsPath : undefined
30+
if (cwd) {
31+
// Add folder name to command-line so that it shows up in `ps aux`.
32+
options.push(cwd)
33+
} else {
34+
options.push('untitled')
35+
}
36+
37+
return {
38+
command: serverExecutable,
39+
args: options.concat(serverArgs()),
40+
options: {
41+
cwd,
42+
env,
43+
},
44+
}
45+
}
46+
47+
export async function setupClient(
48+
toolchainOverride: string | undefined,
49+
clientOptions: LanguageClientOptions,
50+
folderUri: ExtUri,
51+
): Promise<LanguageClient> {
52+
const serverOptions: ServerOptions = await determineServerOptions(toolchainOverride, folderUri)
53+
54+
const client = new LanguageClient('lean4', 'Lean 4', serverOptions, clientOptions)
55+
56+
patchConverters(client.protocol2CodeConverter, client.code2ProtocolConverter)
57+
return client
58+
}

vscode-lean4/src/utils/clientProvider.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { LeanFileProgressProcessingInfo, ServerStoppedReason } from '@leanprover/infoview-api'
22
import path from 'path'
33
import { Disposable, EventEmitter, OutputChannel, commands, workspace } from 'vscode'
4+
import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient/node'
45
import { SetupDiagnostics, checkAll } from '../diagnostics/setupDiagnostics'
56
import { PreconditionCheckResult, SetupNotificationOptions } from '../diagnostics/setupNotifs'
67
import { LeanClient } from '../leanclient'
@@ -62,7 +63,15 @@ export class LeanClientProvider implements Disposable {
6263
private clientStoppedEmitter = new EventEmitter<[LeanClient, boolean, ServerStoppedReason]>()
6364
clientStopped = this.clientStoppedEmitter.event
6465

65-
constructor(installer: LeanInstaller, outputChannel: OutputChannel) {
66+
constructor(
67+
installer: LeanInstaller,
68+
outputChannel: OutputChannel,
69+
private setupClient: (
70+
toolchainOverride: string | undefined,
71+
clientOptions: LanguageClientOptions,
72+
folderUri: ExtUri,
73+
) => Promise<BaseLanguageClient>,
74+
) {
6675
this.outputChannel = outputChannel
6776
this.installer = installer
6877

@@ -273,7 +282,7 @@ export class LeanClientProvider implements Disposable {
273282
}
274283

275284
logger.log('[ClientProvider] Creating LeanClient for ' + folderUri.toString())
276-
client = new LeanClient(folderUri, this.outputChannel)
285+
client = new LeanClient(folderUri, this.outputChannel, this.setupClient)
277286
this.subscriptions.push(client)
278287
this.clients.set(key, client)
279288

0 commit comments

Comments
 (0)