From 32f8518b9efbf5f653c6af5bb9ba7d2cf2f57d06 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sun, 3 Oct 2021 11:20:01 +0200 Subject: [PATCH 1/3] (perf) WIP: TS incremental parsing Implement getChangeRange of the ScriptSnapshot interface so TS can incrementally parse the result of svelte2tsx. TODO: - Doesn't reliably work, gets into a broken state seemginly random after some time - Wasn't able to prove this is really faster (how to best test this?) --- .../plugins/typescript/DocumentSnapshot.ts | 77 +++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts b/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts index dd6e511f9..41030cb61 100644 --- a/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts +++ b/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts @@ -20,6 +20,7 @@ import { isSvelteFilePath, getTsCheckComment } from './utils'; +import { performance } from 'perf_hooks'; /** * An error which occured while trying to parse/preprocess the svelte file contents. @@ -162,6 +163,7 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions : ts.ScriptKind.JSX; try { + performance.mark('svelte2tsx'); const tsx = svelte2tsx(text, { filename: document.getFilePath() ?? undefined, isTsFile: scriptKind === ts.ScriptKind.TSX, @@ -171,6 +173,8 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions document.config?.compilerOptions?.accessors ?? document.config?.compilerOptions?.customElement }); + performance.mark('svelte2tsx-end'); + performance.measure('svelte2tsx', 'svelte2tsx', 'svelte2tsx-end'); text = tsx.code; tsxMap = tsx.map; exportedNames = tsx.exportedNames; @@ -187,7 +191,7 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions } catch (e: any) { // Error start/end logic is different and has different offsets for line, so we need to convert that const start: Position = { - line: (e.start?.line ?? 1) - 1, + line: e.start?.line - 1 ?? 0, character: e.start?.column ?? 0 }; const end: Position = e.end ? { line: e.end.line - 1, character: e.end.column } : start; @@ -247,8 +251,15 @@ export class SvelteDocumentSnapshot implements DocumentSnapshot { return this.text; } - getChangeRange() { - return undefined; + getChangeRange(oldSnapshot: SvelteDocumentSnapshot) { + if ( + (oldSnapshot.parserError && !this.parserError) || + (!oldSnapshot.parserError && this.parserError) + ) { + return undefined; + } + + return getChangeRange(oldSnapshot, this); } positionAt(offset: number) { @@ -328,8 +339,8 @@ export class JSOrTSDocumentSnapshot return this.text; } - getChangeRange() { - return undefined; + getChangeRange(oldSnapshot: JSOrTSDocumentSnapshot) { + return getChangeRange(oldSnapshot, this); } positionAt(offset: number) { @@ -423,3 +434,59 @@ export class SvelteSnapshotFragment implements SnapshotFragment { } } } + +function getChangeRange(oldSnapshot: DocumentSnapshot, currentSnapshot: DocumentSnapshot) { + // TODO problem: doesn't work reliably, gets into a bad state seemingly random after some time + console.trace('version', oldSnapshot.version, '->', currentSnapshot.version); + try { + if (oldSnapshot.version >= currentSnapshot.version) { + return undefined; + } + + performance.mark('oldnew'); + const oldText = oldSnapshot.getFullText(); + const currentText = currentSnapshot.getFullText(); + + let diffStart = 0; + for (let i = 0; i < currentText.length; i++) { + if (oldText.charAt(i) !== currentText.charAt(i)) { + diffStart = i; + break; + } + } + + let lengthDiff = currentText.length - oldText.length; + let diffEnd = diffStart + 1; + for (let i = currentText.length; i > diffStart; i--) { + if (oldText.charAt(i - lengthDiff) !== currentText.charAt(i)) { + diffEnd = i; + break; + } + } + + performance.mark('oldnewend'); + performance.measure('oldnew', 'oldnew', 'oldnewend'); + if (diffStart === 0 && diffEnd >= currentText.length - 1) { + return undefined; + } else { + console.log( + 'diff is from', + diffStart, + 'to', + diffEnd, + 'with lengthdiff', + lengthDiff, + '::\n' + currentText.substring(diffStart, diffEnd) + ); + if (oldSnapshot.version + 1 !== currentSnapshot.version) { + console.log('textnow:', currentText); + } + return ts.createTextChangeRange( + ts.createTextSpan(diffStart, diffEnd - diffStart), + diffEnd + lengthDiff - diffStart + ); + } + } catch (e) { + return undefined; + } +} From 7241080c0abed5ea8ba1a8df648a9bae960e87ec Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sun, 3 Oct 2021 11:20:50 +0200 Subject: [PATCH 2/3] fix --- .../language-server/src/plugins/typescript/DocumentSnapshot.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts b/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts index 41030cb61..336e43955 100644 --- a/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts +++ b/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts @@ -191,7 +191,7 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions } catch (e: any) { // Error start/end logic is different and has different offsets for line, so we need to convert that const start: Position = { - line: e.start?.line - 1 ?? 0, + line: (e.start?.line ?? 1) - 1, character: e.start?.column ?? 0 }; const end: Position = e.end ? { line: e.end.line - 1, character: e.end.column } : start; From 8319aa237cdf862bae34e1102cda7a3c1e2cc8e2 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 6 Jan 2022 16:44:34 +0100 Subject: [PATCH 3/3] fix logic, remove performance marks --- .../plugins/typescript/DocumentSnapshot.ts | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts b/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts index f163ecf64..bf0e7e4f3 100644 --- a/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts +++ b/packages/language-server/src/plugins/typescript/DocumentSnapshot.ts @@ -450,18 +450,15 @@ export class SvelteSnapshotFragment implements SnapshotFragment { } function getChangeRange(oldSnapshot: DocumentSnapshot, currentSnapshot: DocumentSnapshot) { - // TODO problem: doesn't work reliably, gets into a bad state seemingly random after some time - console.trace('version', oldSnapshot.version, '->', currentSnapshot.version); try { if (oldSnapshot.version >= currentSnapshot.version) { return undefined; } - performance.mark('oldnew'); const oldText = oldSnapshot.getFullText(); const currentText = currentSnapshot.getFullText(); - let diffStart = 0; + let diffStart = oldText.length - 1; for (let i = 0; i < currentText.length; i++) { if (oldText.charAt(i) !== currentText.charAt(i)) { diffStart = i; @@ -471,33 +468,19 @@ function getChangeRange(oldSnapshot: DocumentSnapshot, currentSnapshot: Document let lengthDiff = currentText.length - oldText.length; let diffEnd = diffStart + 1; - for (let i = currentText.length; i > diffStart; i--) { + for (let i = currentText.length - 1; i > diffStart; i--) { if (oldText.charAt(i - lengthDiff) !== currentText.charAt(i)) { - diffEnd = i; + diffEnd = i + 1; break; } } - performance.mark('oldnewend'); - performance.measure('oldnew', 'oldnew', 'oldnewend'); if (diffStart === 0 && diffEnd >= currentText.length - 1) { return undefined; } else { - console.log( - 'diff is from', - diffStart, - 'to', - diffEnd, - 'with lengthdiff', - lengthDiff, - '::\n' + currentText.substring(diffStart, diffEnd) - ); - if (oldSnapshot.version + 1 !== currentSnapshot.version) { - console.log('textnow:', currentText); - } return ts.createTextChangeRange( - ts.createTextSpan(diffStart, diffEnd - diffStart), - diffEnd + lengthDiff - diffStart + ts.createTextSpan(diffStart, diffEnd - diffStart - lengthDiff), + diffEnd - diffStart ); } } catch (e) {