import * as ts from "typescript" ; function getStartSafe ( node , sourceFile ) { const jsDocs = ( node as any ) . jsDoc as ts . Node [ ] | undefined ; if ( jsDocs && jsDocs . length > 0 ) { return jsDocs [ 0 ] . getStart ( sourceFile ) ; } return node . getStart ( sourceFile ) ; } function allChildren ( node ) { return node . getChildren ( ) ; } export function getDescendantAtRange ( sourceFile , range ) { const syntaxKinds = ts . SyntaxKind ; let bestMatch = { node : sourceFile , start : sourceFile . getStart ( sourceFile ) , } ; searchDescendants ( sourceFile ) ; return bestMatch . node ; function searchDescendants ( node ) { const children = allChildren ( node ) ; for ( const child of children ) { if ( child . kind !== syntaxKinds . SyntaxList ) { if ( isBeforeRange ( child . end ) ) { continue ; } const childStart = getStartSafe ( child , sourceFile ) ; if ( isAfterRange ( childStart ) ) { return ; } const isEndOfFileToken = child . kind === syntaxKinds . EndOfFileToken ; if ( isEndOfFileToken ) { return ; } const hasSameStart = bestMatch . start === childStart && range [ 0 ] === childStart ; if ( ! hasSameStart && Math . abs ( range [ 0 ] - bestMatch . start ) > Math . abs ( range [ 0 ] - childStart ) ) { bestMatch = { node : child , start : childStart } ; } } searchDescendants ( child ) ; } } function isBeforeRange ( pos ) { return pos < range [ 0 ] ; } function isAfterRange ( nodeEnd ) { return nodeEnd >= range [ 0 ] && nodeEnd > range [ 1 ] ; } } export function lineCharacterPositionInText ( lineChar , text ) { const rows = text . split ( "\n" ) ; const allLines = rows . slice ( 0 , lineChar . line + 1 ) ; allLines [ allLines . length - 1 ] = allLines [ allLines . length - 1 ] . substr ( 0 , lineChar . character ) ; return allLines . join ( "\n" ) . length ; } export function unique < T > ( value , index , self ) { return self . indexOf ( value ) === index ; } export function omit < T extends { } > ( original , key ) { const { [ key ] : value , ... withoutKey } = original ; return withoutKey ; }