diff --git a/.changeset/little-corners-open.md b/.changeset/little-corners-open.md new file mode 100644 index 000000000..3a128dd08 --- /dev/null +++ b/.changeset/little-corners-open.md @@ -0,0 +1,7 @@ +--- +'typescript-svelte-plugin': patch +'svelte-language-server': patch +'svelte-check': patch +--- + +perf: various optimization with ast walk diff --git a/packages/language-server/src/plugins/typescript/features/SemanticTokensProvider.ts b/packages/language-server/src/plugins/typescript/features/SemanticTokensProvider.ts index b5f991012..bb48885d5 100644 --- a/packages/language-server/src/plugins/typescript/features/SemanticTokensProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/SemanticTokensProvider.ts @@ -131,6 +131,7 @@ export class SemanticTokensProviderImpl implements SemanticTokensProvider { const startOffset = document.offsetAt(startPosition); const endOffset = document.offsetAt(endPosition); + const originalText = document.getText(); // Ensure components in the template get no semantic highlighting if ( @@ -139,9 +140,9 @@ export class SemanticTokensProviderImpl implements SemanticTokensProvider { classificationType === TokenType.parameter || classificationType === TokenType.variable || classificationType === TokenType.function) && - snapshot.svelteNodeAt(startOffset)?.type === 'InlineComponent' && - (document.getText().charCodeAt(startOffset - 1) === /* < */ 60 || - document.getText().charCodeAt(startOffset - 1) === /* / */ 47) + (originalText.charCodeAt(startOffset - 1) === /* < */ 60 || + originalText.charCodeAt(startOffset - 1) === /* / */ 47) && + snapshot.svelteNodeAt(startOffset)?.type === 'InlineComponent' ) { return; } diff --git a/packages/language-server/src/plugins/typescript/features/utils.ts b/packages/language-server/src/plugins/typescript/features/utils.ts index dffbbbd0a..12b43457e 100644 --- a/packages/language-server/src/plugins/typescript/features/utils.ts +++ b/packages/language-server/src/plugins/typescript/features/utils.ts @@ -189,23 +189,20 @@ export function findContainingNode( textSpan: ts.TextSpan, predicate: (node: ts.Node) => node is T ): T | undefined { - const children = node.getChildren(); - const end = textSpan.start + textSpan.length; - - for (const child of children) { - if (!(child.getStart() <= textSpan.start && child.getEnd() >= end)) { - continue; - } - - if (predicate(child)) { - return child; - } - - const foundInChildren = findContainingNode(child, textSpan, predicate); - if (foundInChildren) { - return foundInChildren; + // TypeScript will re-parse part of the file in getChildren() to include syntax tokens. + // But for the use cases of this function, we only need the actual nodes like Identifier. + // the forEachChild name is a bit misleading too because it function more like find than forEach + return node.forEachChild((child) => { + if (child.getStart() <= textSpan.start && child.getEnd() >= textSpan.start) { + if (predicate(child)) { + return child; + } + const foundInChildren = findContainingNode(child, textSpan, predicate); + if (foundInChildren) { + return foundInChildren; + } } - } + }); } export function findClosestContainingNode( @@ -242,7 +239,7 @@ export function findNodeAtSpan( const end = start + length; - for (const child of node.getChildren()) { + return node.forEachChild((child) => { const childStart = child.getStart(); if (end <= childStart) { return; @@ -250,7 +247,7 @@ export function findNodeAtSpan( const childEnd = child.getEnd(); if (start >= childEnd) { - continue; + return; } if (start === childStart && end === childEnd) { @@ -266,7 +263,7 @@ export function findNodeAtSpan( if (foundInChildren) { return foundInChildren; } - } + }); } function isSomeAncestor(node: ts.Node, predicate: NodePredicate) { @@ -349,9 +346,9 @@ export function gatherDescendants( if (predicate(node)) { dest.push(node); } else { - for (const child of node.getChildren()) { + node.forEachChild((child) => { gatherDescendants(child, predicate, dest); - } + }); } return dest; } @@ -416,6 +413,7 @@ export function getQuotePreference( : double; } export function findChildOfKind(node: ts.Node, kind: ts.SyntaxKind): ts.Node | undefined { + // this one we do want to use getChildren() because we also want to find syntax tokens, for (const child of node.getChildren()) { if (child.kind === kind) { return child; diff --git a/packages/typescript-plugin/src/utils.ts b/packages/typescript-plugin/src/utils.ts index 069ee8a1c..eacfba42e 100644 --- a/packages/typescript-plugin/src/utils.ts +++ b/packages/typescript-plugin/src/utils.ts @@ -109,7 +109,7 @@ export function findNodeAtSpan( const end = start + length; - for (const child of node.getChildren()) { + return node.forEachChild((child) => { const childStart = child.getStart(); if (end <= childStart) { return; @@ -117,7 +117,7 @@ export function findNodeAtSpan( const childEnd = child.getEnd(); if (start >= childEnd) { - continue; + return; } if (start === childStart && end === childEnd) { @@ -133,7 +133,7 @@ export function findNodeAtSpan( if (foundInChildren) { return foundInChildren; } - } + }); } /** @@ -144,7 +144,7 @@ export function findNodeAtPosition( pos: number, predicate?: NodeTypePredicate ): T | void { - for (const child of node.getChildren()) { + return node.forEachChild((child) => { const childStart = child.getStart(); if (pos < childStart) { return; @@ -152,7 +152,7 @@ export function findNodeAtPosition( const childEnd = child.getEnd(); if (pos > childEnd) { - continue; + return; } const foundInChildren = findNodeAtPosition(child, pos, predicate); @@ -166,7 +166,7 @@ export function findNodeAtPosition( if (predicate(child)) { return child; } - } + }); } /** @@ -204,9 +204,9 @@ export function gatherDescendants( if (predicate(node)) { dest.push(node); } else { - for (const child of node.getChildren()) { + node.forEachChild((child) => { gatherDescendants(child, predicate, dest); - } + }); } return dest; }