namespace ts . SmartSelectionRange { export function getSmartSelectionRange ( pos , sourceFile ) { let selectionRange = { textSpan : createTextSpanFromBounds ( sourceFile . getFullStart ( ) , sourceFile . getEnd ( ) ) } ; let parentNode = sourceFile ; outer : while ( true ) { const children = getSelectionChildren ( parentNode ) ; if ( ! children . length ) break ; for ( let i = 0 ; i < children . length ; i ++ ) { const prevNode = children [ i - 1 ] ; const node = children [ i ] ; const nextNode = children [ i + 1 ] ; if ( getTokenPosOfNode ( node , sourceFile , true ) > pos ) { break outer ; } const comment = singleOrUndefined ( getTrailingCommentRanges ( sourceFile . text , node . end ) ) ; if ( comment && comment . kind === SyntaxKind . SingleLineCommentTrivia ) { pushSelectionCommentRange ( comment . pos , comment . end ) ; } if ( positionShouldSnapToNode ( sourceFile , pos , node ) ) { if ( isBlock ( node ) || isTemplateSpan ( node ) || isTemplateHead ( node ) || isTemplateTail ( node ) || prevNode && isTemplateHead ( prevNode ) || isVariableDeclarationList ( node ) && isVariableStatement ( parentNode ) || isSyntaxList ( node ) && isVariableDeclarationList ( parentNode ) || isVariableDeclaration ( node ) && isSyntaxList ( parentNode ) && children . length === 1 || isJSDocTypeExpression ( node ) || isJSDocSignature ( node ) || isJSDocTypeLiteral ( node ) ) { parentNode = node ; break ; } if ( isTemplateSpan ( parentNode ) && nextNode && isTemplateMiddleOrTemplateTail ( nextNode ) ) { const start = node . getFullStart ( ) - "${" . length ; const end = nextNode . getStart ( ) + "}" . length ; pushSelectionRange ( start , end ) ; } const isBetweenMultiLineBookends = isSyntaxList ( node ) && isListOpener ( prevNode ) && isListCloser ( nextNode ) && ! positionsAreOnSameLine ( prevNode . getStart ( ) , nextNode . getStart ( ) , sourceFile ) ; const start = isBetweenMultiLineBookends ? prevNode . getEnd ( ) : node . getStart ( ) ; const end = isBetweenMultiLineBookends ? nextNode . getStart ( ) : getEndPos ( sourceFile , node ) ; if ( hasJSDocNodes ( node ) && node . jsDoc ?. length ) { pushSelectionRange ( first ( node . jsDoc ) . getStart ( ) , end ) ; } pushSelectionRange ( start , end ) ; if ( isStringLiteral ( node ) || isTemplateLiteral ( node ) ) { pushSelectionRange ( start + 1 , end - 1 ) ; } parentNode = node ; break ; } if ( i === children . length - 1 ) { break outer ; } } } return selectionRange ; function pushSelectionRange ( start , end ) { if ( start !== end ) { const textSpan = createTextSpanFromBounds ( start , end ) ; if ( ! selectionRange || ( ! textSpansEqual ( textSpan , selectionRange . textSpan ) && textSpanIntersectsWithPosition ( textSpan , pos ) ) ) { selectionRange = { textSpan , ... selectionRange && { parent : selectionRange } } ; } } } function pushSelectionCommentRange ( start , end ) { pushSelectionRange ( start , end ) ; let pos = start ; while ( sourceFile . text . charCodeAt ( pos ) === CharacterCodes . slash ) { pos ++ ; } pushSelectionRange ( pos , end ) ; } } param sourceFile param pos param node function positionShouldSnapToNode ( sourceFile , pos , node ) { Debug . assert ( node . pos <= pos ) ; if ( pos < node . end ) { return true ; } const nodeEnd = node . getEnd ( ) ; if ( nodeEnd === pos ) { return getTouchingPropertyName ( sourceFile , pos ) . pos < node . end ; } return false ; } const isImport = or ( isImportDeclaration , isImportEqualsDeclaration ) ; /**
     * Gets the children of a node to be considered for selection ranging,
     * transforming them into an artificial tree according to their intuitive
     * grouping where no grouping actually exists in the parse tree. For example,
     * top-level imports are grouped into their own SyntaxList so they can be
     * selected all together, even though in the AST they’re just siblings of each
     * other as well as of other top-level statements and declarations.
     */ function getSelectionChildren ( node ) { if ( isSourceFile ( node ) ) { return groupChildren ( node . getChildAt ( 0 ) . getChildren ( ) , isImport ) ; } if ( isMappedTypeNode ( node ) ) { const [ openBraceToken , ... children ] = node . getChildren ( ) ; const closeBraceToken = Debug . checkDefined ( children . pop ( ) ) ; Debug . assertEqual ( openBraceToken . kind , SyntaxKind . OpenBraceToken ) ; Debug . assertEqual ( closeBraceToken . kind , SyntaxKind . CloseBraceToken ) ; const groupedWithPlusMinusTokens = groupChildren ( children , child => child === node . readonlyToken || child . kind === SyntaxKind . ReadonlyKeyword || child === node . questionToken || child . kind === SyntaxKind . QuestionToken ) ; const groupedWithBrackets = groupChildren ( groupedWithPlusMinusTokens , ( { kind } ) => kind === SyntaxKind . OpenBracketToken || kind === SyntaxKind . TypeParameter || kind === SyntaxKind . CloseBracketToken ) ; return [ openBraceToken , createSyntaxList ( splitChildren ( groupedWithBrackets , ( { kind } ) => kind === SyntaxKind . ColonToken ) ) , closeBraceToken , ] ; } if ( isPropertySignature ( node ) ) { const children = groupChildren ( node . getChildren ( ) , child => child === node . name || contains ( node . modifiers , child ) ) ; return splitChildren ( children , ( { kind } ) => kind === SyntaxKind . ColonToken ) ; } if ( isParameter ( node ) ) { const groupedDotDotDotAndName = groupChildren ( node . getChildren ( ) , child => child === node . dotDotDotToken || child === node . name ) ; const groupedWithQuestionToken = groupChildren ( groupedDotDotDotAndName , child => child === groupedDotDotDotAndName [ 0 ] || child === node . questionToken ) ; return splitChildren ( groupedWithQuestionToken , ( { kind } ) => kind === SyntaxKind . EqualsToken ) ; } if ( isBindingElement ( node ) ) { return splitChildren ( node . getChildren ( ) , ( { kind } ) => kind === SyntaxKind . EqualsToken ) ; } return node . getChildren ( ) ; } /**
     * Groups sibling nodes together into their own SyntaxList if they
     * a) are adjacent, AND b) match a predicate function.
     */ function groupChildren ( children , groupOn ) { const result = [ ] ; let group ; for ( const child of children ) { if ( groupOn ( child ) ) { group = group || [ ] ; group . push ( child ) ; } else { if ( group ) { result . push ( createSyntaxList ( group ) ) ; group = undefined ; } result . push ( child ) ; } } if ( group ) { result . push ( createSyntaxList ( group ) ) ; } return result ; } param children param pivotOn param separateTrailingSemicolon function splitChildren ( children , pivotOn , separateTrailingSemicolon = true ) { if ( children . length < 2 ) { return children ; } const splitTokenIndex = findIndex ( children , pivotOn ) ; if ( splitTokenIndex === - 1 ) { return children ; } const leftChildren = children . slice ( 0 , splitTokenIndex ) ; const splitToken = children [ splitTokenIndex ] ; const lastToken = last ( children ) ; const separateLastToken = separateTrailingSemicolon && lastToken . kind === SyntaxKind . SemicolonToken ; const rightChildren = children . slice ( splitTokenIndex + 1 , separateLastToken ? children . length - 1 : undefined ) ; const result = compact ( [ leftChildren . length ? createSyntaxList ( leftChildren ) : undefined , splitToken , rightChildren . length ? createSyntaxList ( rightChildren ) : undefined , ] ) ; return separateLastToken ? result . concat ( lastToken ) : result ; } function createSyntaxList ( children ) { Debug . assertGreaterThanOrEqual ( children . length , 1 ) ; return setTextRangePosEnd ( parseNodeFactory . createSyntaxList ( children ) , children [ 0 ] . pos , last ( children ) . end ) ; } function isListOpener ( token ) { const kind = token && token . kind ; return kind === SyntaxKind . OpenBraceToken || kind === SyntaxKind . OpenBracketToken || kind === SyntaxKind . OpenParenToken || kind === SyntaxKind . JsxOpeningElement ; } function isListCloser ( token ) { const kind = token && token . kind ; return kind === SyntaxKind . CloseBraceToken || kind === SyntaxKind . CloseBracketToken || kind === SyntaxKind . CloseParenToken || kind === SyntaxKind . JsxClosingElement ; } function getEndPos ( sourceFile , node ) { switch ( node . kind ) { case SyntaxKind . JSDocParameterTag : case SyntaxKind . JSDocCallbackTag : case SyntaxKind . JSDocPropertyTag : case SyntaxKind . JSDocTypedefTag : case SyntaxKind . JSDocThisTag : return sourceFile . getLineEndOfPosition ( node . getStart ( ) ) ; default : return node . getEnd ( ) ; } } }