refactor(lsp): cache analysis results and debounce diagnostics
build_and_puplish.yml / build_and_publish (release) Successful in 29s

This change adds cached and incremental analysis for the LSP
server to improve responsiveness. The client now debounces
diagnostic updates to avoid excessive recomputation. The
server introduces per-document line caches and various
caches for completions, inlay hints, and metadata to
support faster, incremental updates.

- Debounce diagnostics on text changes to reduce noise.
- Add caches for completions, inlay hints, and metadata.
- Introduce incremental analysis with per-document line caches.
This commit is contained in:
Christoph Brandau
2026-08-19 13:41:53 +02:00
parent ecb50be2b8
commit af195a577b
11 changed files with 616 additions and 181 deletions
+15 -8
View File
@@ -4,6 +4,8 @@ import {
createCdlEventHandlerSnippet
} from "./cdlEventHandler"
const MACHINE_HEADER_REGEX = /^MACHINE\s+\S+/
export function formatCdlFile(content: string): string {
let indentLevel = 0
const formattedLines = []
@@ -41,15 +43,19 @@ export function formatDefFile(content: string): string {
}
export function isFirstLineMachine(content: string): boolean {
const lines = content.split("\n").map((line) => line.trim())
for (const line of lines) {
console.log(line)
let lineStart = 0
while (lineStart <= content.length) {
const newline = content.indexOf("\n", lineStart)
const lineEnd = newline === -1 ? content.length : newline
const line = content.slice(lineStart, lineEnd).trim()
if (line === "" || line.startsWith("#")) {
console.log("skipping line")
if (newline === -1) {
return false
}
lineStart = newline + 1
continue
}
const machineRegex = /^MACHINE\s+\S+/
return machineRegex.test(line)
return MACHINE_HEADER_REGEX.test(line)
}
return false
}
@@ -57,10 +63,11 @@ export function isFirstLineMachine(content: string): boolean {
export function diagnosticHandler(document: vscode.TextDocument) {
const diagnostics: vscode.Diagnostic[] = []
if (document.languageId === "cdl" || document.languageId === "def") {
if (!isFirstLineMachine(document.getText())) {
const text = document.getText()
if (!isFirstLineMachine(text)) {
const range = new vscode.Range(
document.positionAt(0),
document.positionAt(document.getText().length)
document.positionAt(text.length)
)
const diagnostic = new vscode.Diagnostic(
range,
+50 -11
View File
@@ -246,28 +246,67 @@ export async function activate(context: vscode.ExtensionContext) {
const diagnosticCollectionDef = vscode.languages.createDiagnosticCollection("def")
context.subscriptions.push(diagnosticCollectionCdl, diagnosticCollectionDef)
const diagnosticTimers = new Map<string, ReturnType<typeof setTimeout>>()
const updateDiagnostics = (document: vscode.TextDocument) => {
if (document.languageId === "cdl") {
diagnosticCollectionCdl.set(document.uri, diagnosticHandler(document))
} else if (document.languageId === "def") {
diagnosticCollectionDef.set(document.uri, diagnosticHandler(document))
}
}
const scheduleDiagnostics = (document: vscode.TextDocument) => {
const key = document.uri.toString()
const previous = diagnosticTimers.get(key)
if (previous !== undefined) {
clearTimeout(previous)
}
diagnosticTimers.set(
key,
setTimeout(() => {
diagnosticTimers.delete(key)
updateDiagnostics(document)
}, 120)
)
}
// Check if the first line of the CDL file contains "MACHINE"
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument((document) => {
if (document.languageId === "cdl" || document.languageId === "def") {
if (document.languageId === "cdl") {
diagnosticCollectionCdl.set(document.uri, diagnosticHandler(document))
} else if (document.languageId === "def") {
diagnosticCollectionDef.set(document.uri, diagnosticHandler(document))
}
updateDiagnostics(document)
}
}),
vscode.workspace.onDidChangeTextDocument((event) => {
const document = event.document
if (document.languageId === "cdl" || document.languageId === "def") {
if (document.languageId === "cdl") {
diagnosticCollectionCdl.set(document.uri, diagnosticHandler(document))
} else if (document.languageId === "def") {
diagnosticCollectionDef.set(document.uri, diagnosticHandler(document))
}
scheduleDiagnostics(document)
}
})
}),
vscode.workspace.onDidCloseTextDocument((document) => {
const key = document.uri.toString()
const timer = diagnosticTimers.get(key)
if (timer !== undefined) {
clearTimeout(timer)
diagnosticTimers.delete(key)
}
diagnosticCollectionCdl.delete(document.uri)
diagnosticCollectionDef.delete(document.uri)
}),
{
dispose() {
for (const timer of diagnosticTimers.values()) {
clearTimeout(timer)
}
diagnosticTimers.clear()
}
}
)
for (const document of vscode.workspace.textDocuments) {
if (document.languageId === "cdl" || document.languageId === "def") {
updateDiagnostics(document)
}
}
}
export function deactivate(): Thenable<void> | undefined {