VS-Code „editor.edit“ blockiert für längere Zeit während des Streaming-Differenz in der ErweiterungJavaScript

Javascript-Forum
Guest
 VS-Code „editor.edit“ blockiert für längere Zeit während des Streaming-Differenz in der Erweiterung

Post by Guest »

Ich entwickle eine VS-Code-Erweiterung, die einen Streaming-Diff für einen ausgewählten Textbereich durchführt. Die Erweiterung empfängt neue Zeilen, die den ausgewählten Text ersetzen sollen, über eine WebSocket-Verbindung, generiert Diff-Zeilen („same“, „old“, „new“) und wendet sie in Echtzeit auf den aktiven Editor an.Problem:
Die Funktion editor.edit, die ich zum Einfügen neuer Zeilen verwende, blockiert die Hauptfunktion Der Thread wird während des Streaming-Vorgangs unerwartet lange (in manchen Fällen bis zu 40 Sekunden) unterbrochen. Dies geschieht, obwohl editor.edit asynchron sein soll. Das Problem scheint gegen Ende des Differenzierungsvorgangs beim Einfügen „neuer“ Zeilen stärker ausgeprägt zu sein. Ich habe die vscode-API auf editor.edit durchgesehen, um zu sehen, ob ich eine schlechte Implementierung oder ähnliches gemacht habe, aber keine Fortschritte dabei gemacht.
Codestruktur:
Mein Code ist wie folgt aufgebaut:
  • Ein WebSocketClient empfängt Diff-Zeilen von einem Server .
  • Der setMessageCallback des Clients pusht neue Zeilen zu einerlinesQueue.
  • Code: Select all

    processLinesQueue
    verarbeitet die Zeilen in der Warteschlange und ruft handleStreamData für jede Zeile auf.
  • Code: Select all

    handleStreamData
    verwendet eine Generatorfunktion showInlineDiffForSelectedRangeV2, um DiffLine-Objekte basierend auf einer benutzerdefinierten matchLine-Funktion zu erzeugen (die den Levenshtein-Abstand für Fuzzy-Matching verwendet).
    < li>

    Code: Select all

    insertLineAboveIndex
    verwendet editor.edit mit editBuilder.insert, um neue Zeilen in das Dokument einzufügen.
  • Code: Select all

    reapplyWithMeyersDiff
    wendet nach Abschluss des Streamings einen Myers-Diff an, um die Genauigkeit sicherzustellen.
Relevante Codeausschnitte:[/b]

Code: Select all

// ... (Other imports) ...
import { diffLines, Change } from 'diff';

// ... (Other type definitions) ...

type DiffLine = {
type: string;
line: string;
};

export type MatchLineResult = {
matchIndex: number;
isPerfectMatch: boolean;
newDiffedLine: string;
};

export class inlineChatProvider {
// ...  (Properties) ...

private async processLinesQueue() {
if (this.processingQueue || this.linesQueue.length === 0) {
return;
}

this.processingQueue = true;
try {
while (this.linesQueue.length > 0) {
if (this.stopQueueProcessing) {
this.processingQueue = false;
return; // Exit the loop immediately
}

const newLine = this.linesQueue.shift()!;
console.time("handleStreamData");
await this.handleStreamData(newLine);
console.timeEnd("handleStreamData");
}
} finally {
this.processingQueue = false;
}
}

private async handleStreamData(newLine: string) {
const editor = vscode.window.activeTextEditor;
if (!editor || !this.oldRange) {
return;
}
if (this.stopQueueProcessing) {
this.processingQueue = false;
return; // Exit immediately if stopped
}

for await (const diffLine of this.showInlineDiffForSelectedRangeV2(newLine, this.showCodelens)) {
console.log("Processing diffLine:", diffLine);
if (this.stopQueueProcessing) {
return; // Exit if processing is stopped
}

switch (diffLine.type) {
case "same":
await this.insertDeletionBuffer();
this.incrementCurrentLineIndex();
break;
case "old":
this.deletionBuffer.push(diffLine.line);
await this.deleteLinesAt(this.currentLineIndex);
this.deletedLinesOffset++;
break;
case "new":
console.time("insertLineAboveIndex");
await this.insertLineAboveIndex(this.currentLineIndex, diffLine.line);
console.timeEnd("insertLineAboveIndex");
this.incrementCurrentLineIndex();
this.insertedInCurrentBlock++;
this.addedLinesOffset++;

// Expand oldRange if necessary
if (this.currentLineIndex >= this.oldRange!.end.line) {
this.oldRange = new vscode.Selection(
this.oldRange!.start.line,
this.oldRange!.start.character,
this.currentLineIndex,
0
);
}
break;
}
}
}

private async insertTextAboveLine(index: number, text: string) {
console.time(`the start:insertLineAboveIndex${index}`);
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}

console.time(`editor await itself:insertLineAboveIndex${index}`);
await editor.edit(
(editBuilder) => {
const lineCount = editor.document.lineCount;
if (index >= lineCount) {
// Append to end of file
editBuilder.insert(
new vscode.Position(
lineCount,
editor.document.lineAt(lineCount - 1).text.length,
),
`\n${text}`,
);
} else {
console.time(`insertLineAboveIndex${index}`);
editBuilder.insert(new vscode.Position(index, 0), `${text}\n`);
console.timeEnd(`insertLineAboveIndex${index}`);
}
},
{
undoStopAfter: false,
undoStopBefore: false,
},
);
console.timeEnd(`editor await itself:insertLineAboveIndex${index}`);
console.timeEnd(`the start:insertLineAboveIndex${index}`);
}

private async deleteLinesAt(index: number) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const startLine = new vscode.Position(index, 0);
await editor.edit(
(editBuilder) =>  {
editBuilder.delete(
new vscode.Range(startLine, startLine.translate(1)),
);
},
{
undoStopAfter: false,
undoStopBefore: false,
},
);
}

private async insertLineAboveIndex(index: number, line: string) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
console.time(`let's see edit speed:${index}`);
await this.insertTextAboveLine(index, line);
console.timeEnd(`let's see edit speed:${index}`);
const addedRange = new vscode.Range(index, 0, index, line.length);
editor.setDecorations(this.addedDecoratorType, [addedRange]);
}

private incrementCurrentLineIndex() {
this.currentLineIndex++;
}

async reapplyWithMeyersDiff() {
// ... (Implementation using diffLines and editor.edit) ...
}

// ... (Other methods) ...

private async *showInlineDiffForSelectedRangeV2(
newLine: string,
loadCodeLens: boolean = false
): AsyncGenerator {
let consecutiveNewLines = 0;
let iterationCount = 0;
const yieldInterval = 100;

while (
this.oldLinesCopy.length > 0 &&
!loadCodeLens &&
consecutiveNewLines < 3
) {
const startTime = performance.now();
const { matchIndex, isPerfectMatch, newDiffedLine } = matchLine(
newLine,
this.oldLinesCopy,
this.seenIndentationMistake
);
const endTime = performance.now();
const elapsedTime = endTime - startTime;
console.log(`matchLine took ${elapsedTime.toFixed(2)} milliseconds`);

if (!this.seenIndentationMistake && newLine !== newDiffedLine) {
this.seenIndentationMistake = true;
}

let type: string;
const isNewLine = matchIndex === -1;
if (isNewLine) {
type = "new";
consecutiveNewLines++;
} else {
consecutiveNewLines = 0;

// Insert all deleted lines before match
for (let i = 0; i < matchIndex; i++) {
yield { type: 'old', line: this.oldLinesCopy.shift()! };
}

type = isPerfectMatch ? "same" : "old";
}

switch (type) {
case "new":
yield { type, line: newDiffedLine };
break;

case "same":
yield { type, line: this.oldLinesCopy.shift()! };
break;

case "old":
const oldLine = this.oldLinesCopy.shift()!;
if (isPerfectMatch) {
yield { type: "same", line: newDiffedLine };
} else {
yield { type: "old", line: oldLine };
}
break;

default:
console.error(
`Error streaming diff, unrecognized diff type: ${type}`
);
}

iterationCount++;
if (iterationCount % yieldInterval === 0) {
// Yield to the event loop without producing a value
await new Promise(resolve => setTimeout(resolve, 0));
}
}

// Yield any remaining lines as "new"
if (!loadCodeLens) {
yield { type: "new", line: newLine };
while (this.oldLinesCopy.length > 0) {
yield { type: "old", line: this.oldLinesCopy.shift()! };
}
}
}

// ...  (initSocketConnection, startEdit, etc.) ...
}

export function matchLine(
newLine: string,
oldCodeCopy: string[],
permissiveAboutIndentation = false
): MatchLineResult {
if (newLine.trim() === "" && oldCodeCopy[0]?.trim() === "") {
return {
matchIndex: 0,
isPerfectMatch: true,
newDiffedLine: newLine.trim(),
};
}

const isEndBracket = END_BRACKETS.includes(newLine.trim());
for (let i = 0; i < oldCodeCopy.length; i++) {
if (i > 4 && isEndBracket) {
return { matchIndex: -1, isPerfectMatch: false, newDiffedLine: newLine };
}

if (linesMatchPerfectly(newLine, oldCodeCopy[i])) {
return { matchIndex: i, isPerfectMatch: true, newDiffedLine: newLine };
}

// Use cached distance if available
const distanceKey = `${newLine}_${oldCodeCopy[i]}`;
if (linesMatch(newLine, oldCodeCopy[i], i)) {
// More permissive indentation handling
const newTrimmed = newLine.trim();
const oldTrimmed = oldCodeCopy[i].trim();

if (
newTrimmed === oldTrimmed ||
(permissiveAboutIndentation &&
newTrimmed.length > 0 &&
(newLine.length - newTrimmed.length) - (oldCodeCopy[i].length - oldTrimmed.length)  setTimeout(resolve, 0))“, um die Kontrolle über die Ereignisschleife zu erhalten.
[*]Batchbearbeitung von Änderungen innerhalb von editor.edit< /code>.
[*]setInterval verwenden, um Zeilen regelmäßig zu verarbeiten, statt einer while--Schleife.
[*]Ich habe es versucht Worker-Threads, was funktioniert, aber ein Problem mit sich bringt diffLines folgen nicht der richtigen Reihenfolge
[/list]
[b]Wonach ich suche:[/b]
[list]
[*]Einblicke darüber, warum editor.edit trotz seiner asynchronen Natur möglicherweise blockiert.
[*]Vorschläge für weitere Debugging- oder Profiling-Techniken.
[*]Empfehlungen für alternative Ansätze oder Optimierungen, um die Verlangsamung bei gleichzeitiger Erhaltung zu vermeiden die Streaming-Diff-Funktionalität (falls möglich).
[*]Alle relevanten Informationen zu bekannten Einschränkungen oder Leistungsüberlegungen im Zusammenhang mit editor.edit in der VS Code API.
[/list]

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post