@@ -720,8 +720,14 @@ namespace ts {
720
720
}
721
721
}
722
722
723
- export function findPrecedingToken ( position : number , sourceFile : SourceFile , startNode ?: Node , includeJsDoc ?: boolean ) : Node {
724
- return find ( startNode || sourceFile ) ;
723
+ /**
724
+ * Finds the rightmost token satisfying `token.end <= position`,
725
+ * excluding `JsxText` tokens containing only whitespace.
726
+ */
727
+ export function findPrecedingToken ( position : number , sourceFile : SourceFile , startNode ?: Node , includeJsDoc ?: boolean ) : Node | undefined {
728
+ const result = find ( startNode || sourceFile ) ;
729
+ Debug . assert ( ! ( result && isWhiteSpaceOnlyJsxText ( result ) ) ) ;
730
+ return result ;
725
731
726
732
function findRightmostToken ( n : Node ) : Node {
727
733
if ( isToken ( n ) ) {
@@ -743,18 +749,16 @@ namespace ts {
743
749
for ( let i = 0 ; i < children . length ; i ++ ) {
744
750
const child = children [ i ] ;
745
751
// Note that the span of a node's tokens is [node.getStart(...), node.end).
746
- // Given that `position < child.end` and child has constiutent tokens* , we distinguish these cases:
752
+ // Given that `position < child.end` and child has constituent tokens, we distinguish these cases:
747
753
// 1) `position` precedes `child`'s tokens or `child` has no tokens (ie: in a comment or whitespace preceding `child`):
748
754
// we need to find the last token in a previous child.
749
755
// 2) `position` is within the same span: we recurse on `child`.
750
- // * JsxText is exceptional in that its tokens are (non-trivia) whitespace, which we do not want to return.
751
- // TODO(arozga): shouldn't `findRightmost...` need to handle JsxText?
752
756
if ( position < child . end ) {
753
757
const start = child . getStart ( sourceFile , includeJsDoc ) ;
754
758
const lookInPreviousChild =
755
759
( start >= position ) || // cursor in the leading trivia
756
760
! nodeHasTokens ( child ) ||
757
- ( child . kind === SyntaxKind . JsxText && start === child . end ) ; // whitespace only JsxText
761
+ isWhiteSpaceOnlyJsxText ( child ) ;
758
762
759
763
if ( lookInPreviousChild ) {
760
764
// actual start of the node is past the position - previous token should be at the end of previous child
@@ -781,11 +785,16 @@ namespace ts {
781
785
}
782
786
783
787
/**
784
- * Finds the rightmost child to the left of `children[exclusiveStartPosition]` which has constituent tokens.
788
+ * Finds the rightmost child to the left of `children[exclusiveStartPosition]` which is a non-all-whitespace token or has constituent tokens.
785
789
*/
786
790
function findRightmostChildNodeWithTokens ( children : Node [ ] , exclusiveStartPosition : number ) : Node {
787
791
for ( let i = exclusiveStartPosition - 1 ; i >= 0 ; i -- ) {
788
- if ( nodeHasTokens ( children [ i ] ) ) {
792
+ const child = children [ i ] ;
793
+
794
+ if ( isWhiteSpaceOnlyJsxText ( child ) ) {
795
+ Debug . assert ( i > 0 , "`JsxText` tokens should not be the first child of `JsxElement | JsxSelfClosingElement`" ) ;
796
+ }
797
+ else if ( nodeHasTokens ( children [ i ] ) ) {
789
798
return children [ i ] ;
790
799
}
791
800
}
@@ -853,6 +862,11 @@ namespace ts {
853
862
return false ;
854
863
}
855
864
865
+ export function isWhiteSpaceOnlyJsxText ( node : Node ) : node is JsxText {
866
+ return isJsxText ( node ) && node . containsOnlyWhiteSpaces ;
867
+ }
868
+
869
+
856
870
export function isInTemplateString ( sourceFile : SourceFile , position : number ) {
857
871
const token = getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ false ) ;
858
872
return isTemplateLiteralKind ( token . kind ) && position > token . getStart ( sourceFile ) ;
@@ -888,7 +902,7 @@ namespace ts {
888
902
889
903
function nodeHasTokens ( n : Node ) : boolean {
890
904
// If we have a token or node that has a non-zero width, it must have tokens.
891
- // Note, that getWidth() does not take trivia into account.
905
+ // Note: getWidth() does not take trivia into account.
892
906
return n . getWidth ( ) !== 0 ;
893
907
}
894
908
0 commit comments