diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 2bca2ab977df2..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])) { + 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) { - const start = allowPositionInLeadingTrivia ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true); + 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; }