From 30980f189552a0885d2300ab1396b32a1c989671 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 22 Jun 2022 10:38:46 -0700 Subject: [PATCH 1/2] Reuse start position in binarySearchKey --- src/services/utilities.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 2bca2ab977df2..38d1e7526e9d2 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1153,7 +1153,7 @@ namespace ts { } // first element whose start position is before the input and whose end position is after or equal to the input - if (nodeContainsPosition(children[middle])) { + if (nodeContainsPosition(children[middle], start)) { if (children[middle - 1]) { // we want the _first_ element that contains the position, so left-recur if the prior node also contains the position if (nodeContainsPosition(children[middle - 1])) { @@ -1181,8 +1181,8 @@ namespace ts { return current; } - function nodeContainsPosition(node: Node) { - const start = allowPositionInLeadingTrivia ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true); + function nodeContainsPosition(node: Node, start?: number) { + start ??= allowPositionInLeadingTrivia ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true); if (start > position) { // If this child begins after position, then all subsequent children will as well. return false; From 5e505d86751f980a776bfabb63ac7ab0b1744862 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 23 Jun 2022 10:16:52 -0700 Subject: [PATCH 2/2] Kick out early if end < position --- src/services/utilities.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 38d1e7526e9d2..a061a5ca7c5b5 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1147,13 +1147,20 @@ namespace ts { // position and whose end is greater than the position. + // There are more sophisticated end tests later, but this one is very fast + // and allows us to skip a bunch of work + const end = children[middle].getEnd(); + if (end < position) { + return Comparison.LessThan; + } + const start = allowPositionInLeadingTrivia ? children[middle].getFullStart() : children[middle].getStart(sourceFile, /*includeJsDoc*/ true); if (start > position) { return Comparison.GreaterThan; } // first element whose start position is before the input and whose end position is after or equal to the input - if (nodeContainsPosition(children[middle], start)) { + if (nodeContainsPosition(children[middle], start, end)) { if (children[middle - 1]) { // we want the _first_ element that contains the position, so left-recur if the prior node also contains the position if (nodeContainsPosition(children[middle - 1])) { @@ -1181,13 +1188,16 @@ namespace ts { return current; } - function nodeContainsPosition(node: Node, start?: number) { + function nodeContainsPosition(node: Node, start?: number, end?: number) { + end ??= node.getEnd(); + if (end < position) { + return false; + } start ??= allowPositionInLeadingTrivia ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true); if (start > position) { // If this child begins after position, then all subsequent children will as well. return false; } - const end = node.getEnd(); if (position < end || (position === end && (node.kind === SyntaxKind.EndOfFileToken || includeEndPosition))) { return true; }