Блокировка кода VS `editor.edit` на длительный период во время потоковой передачи. Разница в расширении.Javascript

Форум по Javascript
Ответить Пред. темаСлед. тема
Anonymous
 Блокировка кода VS `editor.edit` на длительный период во время потоковой передачи. Разница в расширении.

Сообщение Anonymous »

Тело:

Я разрабатываю расширение VS Code, которое выполняет потоковую проверку выделенного диапазона текста. Расширение получает новые строки, предназначенные для замены выделенного текста, из соединения WebSocket, генерирует строки различий («те же», «старые», «новые») и применяет их к активному редактору в режиме реального времени.Проблема:
Функция editor.edit, которую я использую для вставки новых строк, блокирует основную поток в течение неожиданно длительного времени (в некоторых случаях до 40 секунд) во время процесс потоковой передачи. Это происходит даже несмотря на то, что editor.edit должен быть асинхронным. Проблема кажется более выраженной ближе к концу операции сравнения при вставке «новых» строк. Я просмотрел API vscode в editor.edit, чтобы увидеть, не сделал ли я плохую реализацию или что-то в этом роде, но никакого прогресса в этом нет.
Структура кода:
Мой код структурирован следующим образом:
  • WebSocketClient получает строки сравнения с сервера .
  • setMessageCallback клиента помещает новые строки в очередь строк.
  • Код: Выделить всё

    processLinesQueue
    обрабатывает строки в очереди, вызывая handleStreamData для каждой строки.
  • Код: Выделить всё

    handleStreamData
    использует функцию-генератор showInlineDiffForSelectedRangeV2 для получения объектов DiffLine на основе пользовательской функции matchLine (которая использует расстояние Левенштейна для нечеткого сопоставления).
    < ли>

    Код: Выделить всё

    insertLineAboveIndex
    использует editor.edit с editBuilder.insert для вставки новых строк в документ.
  • Код: Выделить всё

    reapplyWithMeyersDiff
    применяет разницу Майерса после завершения потоковой передачи для обеспечения точности.
Соответствующие фрагменты кода:[/b]

Код: Выделить всё

// ... (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)) для передачи управления циклу событий.
[*]Пакетное редактирование в editor.edit< /code>.
[*]Использование setInterval для периодической обработки строк вместо цикла while.
[*]Я пробовал использовать рабочие потоки, которые работают, но создают проблему diffLines не следуют правильному порядку
[/list]
[b]Что я ищу:[/b]
[list]
[*]Понимание того, почему файл editor.edit может блокироваться, несмотря на его асинхронный характер.
[*]Предложения по дальнейшей отладке или методам профилирования.
[*]Рекомендации по альтернативным подходам или оптимизации, позволяющие избежать замедления сохраняя при этом функциональность потокового сравнения (если это возможно).
[*]Любая соответствующая информация об известных ограничениях или соображениях производительности, связанных с editor.edit в API VS Code.
[/list]
 

Подробнее здесь: [url]https://stackoverflow.com/questions/79358248/vs-code-editor-edit-blocking-for-extended-period-during-streaming-diff-in-exte[/url]
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Javascript»