Skip to content

Commit 0bfa9da

Browse files
committed
feat: client-side support for incremental diagnostics
Resolve conflict in leanclient.ts
1 parent 988871a commit 0bfa9da

File tree

3 files changed

+121
-14
lines changed

3 files changed

+121
-14
lines changed

vscode-lean4/src/diagnostics.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { DiagnosticCollection, Disposable, EventEmitter } from 'vscode'
2+
import { LeanDiagnostic, LeanPublishDiagnosticsParams, p2cConverter } from './utils/converters'
3+
import { DocumentUri } from 'vscode-languageserver-protocol'
4+
5+
export type DiagnosticChangeKind = 'replace' | 'append'
6+
7+
export class DiagnosticChangeEvent {
8+
readonly kind: DiagnosticChangeKind
9+
readonly params: LeanPublishDiagnosticsParams
10+
private readonly accumulated: LeanDiagnostic[]
11+
private readonly amountAccumulated: number
12+
13+
constructor(kind: DiagnosticChangeKind, params: LeanPublishDiagnosticsParams, accumulated: LeanDiagnostic[]) {
14+
this.kind = kind
15+
this.params = params
16+
this.accumulated = accumulated
17+
this.amountAccumulated = accumulated.length
18+
}
19+
20+
private accumulatedDiagnostics(): LeanDiagnostic[] {
21+
return this.accumulated.slice(0, this.amountAccumulated)
22+
}
23+
24+
accumulatedParams(): LeanPublishDiagnosticsParams {
25+
return { ...this.params, diagnostics: this.accumulatedDiagnostics() }
26+
}
27+
}
28+
29+
export class LeanClientDiagnosticCollection implements Disposable {
30+
readonly vsCodeCollection: DiagnosticCollection
31+
private diags: Map<DocumentUri, LeanPublishDiagnosticsParams> = new Map()
32+
33+
private diagnosticsChangedEmitter = new EventEmitter<DiagnosticChangeEvent>()
34+
onDidChangeDiagnostics = this.diagnosticsChangedEmitter.event
35+
36+
constructor(vsCodeCollection: DiagnosticCollection) {
37+
this.vsCodeCollection = vsCodeCollection
38+
}
39+
40+
private static determineChangeKind(
41+
prev: LeanPublishDiagnosticsParams | undefined,
42+
next: LeanPublishDiagnosticsParams,
43+
): DiagnosticChangeKind | 'drop' {
44+
if (prev === undefined) {
45+
return 'replace'
46+
}
47+
if (next.version === undefined || prev.version === undefined) {
48+
return 'replace'
49+
}
50+
if (next.version > prev.version) {
51+
return 'replace'
52+
}
53+
if (next.version < prev.version) {
54+
return 'drop'
55+
}
56+
if (!next.isIncremental) {
57+
return 'replace'
58+
}
59+
return 'append'
60+
}
61+
62+
publishDiagnostics(params: LeanPublishDiagnosticsParams): void {
63+
const prev = this.diags.get(params.uri)
64+
const kind = LeanClientDiagnosticCollection.determineChangeKind(prev, params)
65+
if (kind === 'drop') {
66+
return
67+
}
68+
69+
let accumulated: LeanDiagnostic[]
70+
if (kind === 'append') {
71+
accumulated = prev!.diagnostics
72+
accumulated.push(...params.diagnostics)
73+
} else {
74+
accumulated = [...params.diagnostics]
75+
}
76+
77+
const accumulatedParams = { ...params, diagnostics: accumulated }
78+
79+
this.diags.set(accumulatedParams.uri, accumulatedParams)
80+
void this.syncToCollection(accumulatedParams)
81+
this.diagnosticsChangedEmitter.fire(new DiagnosticChangeEvent(kind, params, accumulated))
82+
}
83+
84+
private async syncToCollection(p: LeanPublishDiagnosticsParams): Promise<void> {
85+
const nonSilent = p.diagnostics.filter(d => !d.isSilent)
86+
const uri = p2cConverter.asUri(p.uri)
87+
const vsCodeDiags = await p2cConverter.asDiagnostics(nonSilent)
88+
this.vsCodeCollection.set(uri, vsCodeDiags)
89+
}
90+
91+
dispose(): void {
92+
this.diagnosticsChangedEmitter.dispose()
93+
}
94+
}

vscode-lean4/src/leanclient.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Disposable,
55
EventEmitter,
66
FileSystemWatcher,
7+
languages,
78
OutputChannel,
89
Progress,
910
ProgressLocation,
@@ -32,7 +33,6 @@ import {
3233
} from 'vscode-languageclient/node'
3334

3435
import {
35-
LeanDiagnostic,
3636
LeanFileProgressParams,
3737
LeanFileProgressProcessingInfo,
3838
LeanServerCapabilities,
@@ -52,6 +52,7 @@ import { logger } from './utils/logger'
5252
import * as fs from 'fs'
5353
import { glob } from 'glob'
5454
import * as semver from 'semver'
55+
import { DiagnosticChangeEvent, LeanClientDiagnosticCollection } from './diagnostics'
5556
import { formatCommandExecutionOutput } from './utils/batch'
5657
import {
5758
c2pConverter,
@@ -98,11 +99,13 @@ function logConfig(): LogConfig | undefined {
9899
}
99100

100101
interface LeanClientCapabilties {
102+
incrementalDiagnosticSupport?: boolean
101103
silentDiagnosticSupport?: boolean
102104
rpcWireFormat?: RpcWireFormat
103105
}
104106

105107
const leanClientCapabilities: LeanClientCapabilties = {
108+
incrementalDiagnosticSupport: true,
106109
silentDiagnosticSupport: true,
107110
rpcWireFormat: 'v1'
108111
}
@@ -140,6 +143,8 @@ export class LeanClient implements Disposable {
140143
private didChangeEmitter = new EventEmitter<DidChangeTextDocumentParams>()
141144
didChange = this.didChangeEmitter.event
142145

146+
private diagnosticCollection: LeanClientDiagnosticCollection | undefined
147+
143148
private diagnosticsEmitter = new EventEmitter<LeanPublishDiagnosticsParams>()
144149
diagnostics = this.diagnosticsEmitter.event
145150

@@ -540,7 +545,18 @@ export class LeanClient implements Disposable {
540545
}
541546
}
542547
})
548+
this.diagnosticCollection?.dispose()
549+
const vsCodeCollection = languages.createDiagnosticCollection('lean4')
550+
this.diagnosticCollection = new LeanClientDiagnosticCollection(vsCodeCollection)
551+
this.diagnosticCollection.onDidChangeDiagnostics((e: DiagnosticChangeEvent) => {
552+
this.diagnosticsEmitter.fire(e.accumulatedParams())
553+
})
554+
this.client.onNotification('textDocument/publishDiagnostics', (params: LeanPublishDiagnosticsParams) => {
555+
this.diagnosticCollection?.publishDiagnostics(params)
556+
})
557+
543558
await this.client.start()
559+
544560
const rawVersion = this.client.initializeResult?.serverInfo?.version
545561
const version = rawVersion !== undefined ? semver.parse(rawVersion) : null
546562
if (version !== null && version.compare('0.2.0') < 0) {
@@ -709,6 +725,9 @@ export class LeanClient implements Disposable {
709725

710726
this.noPrompt = false
711727
this.progress = new Map()
728+
this.diagnosticCollection?.vsCodeCollection.dispose()
729+
this.diagnosticCollection?.dispose()
730+
this.diagnosticCollection = undefined
712731
this.client = undefined
713732
this.openServerDocuments = new Set()
714733
this.running = false
@@ -780,7 +799,7 @@ export class LeanClient implements Disposable {
780799
}
781800

782801
getDiagnostics(): DiagnosticCollection | undefined {
783-
return this.running ? this.client?.diagnostics : undefined
802+
return this.running ? this.diagnosticCollection?.vsCodeCollection : undefined
784803
}
785804

786805
get initializeResult(): InitializeResult | undefined {
@@ -874,18 +893,11 @@ export class LeanClient implements Disposable {
874893
}
875894
return next(type, param)
876895
},
877-
handleDiagnostics: (uri, diagnostics, next) => {
878-
const diagnosticsInVsCode = diagnostics.filter(d => !('isSilent' in d && d.isSilent))
879-
next(uri, diagnosticsInVsCode)
880-
const uri_ = c2pConverter.asUri(uri)
881-
const diagnostics_: LeanDiagnostic[] = []
882-
for (const d of diagnostics) {
883-
const d_: LeanDiagnostic = {
884-
...c2pConverter.asDiagnostic(d),
885-
}
886-
diagnostics_.push(d_)
887-
}
888-
this.diagnosticsEmitter.fire({ uri: uri_, diagnostics: diagnostics_ })
896+
handleDiagnostics: () => {
897+
// Suppress the default diagnostics handling by the LanguageClient.
898+
// We handle diagnostics ourselves via a custom notification handler
899+
// so that we can access the full LeanPublishDiagnosticsParams
900+
// (including version and isIncremental).
889901
},
890902

891903
didOpen: async (doc, next) => {

vscode-lean4/src/utils/converters.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface LeanDiagnostic extends ls.Diagnostic {
3232
export interface LeanPublishDiagnosticsParams {
3333
uri: ls.DocumentUri
3434
version?: ls.integer
35+
isIncremental?: boolean
3536
diagnostics: LeanDiagnostic[]
3637
}
3738

0 commit comments

Comments
 (0)