refactor(lsp): cache analysis results and debounce diagnostics
build_and_puplish.yml / build_and_publish (release) Successful in 29s
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:
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user