import { Node } from 'vscode-html-languageservice' ; import { Position , Range } from 'vscode-languageserver' ; import { URI } from 'vscode-uri' ; type Predicate < T > = ( x : T ) => boolean ; export function not < T > ( predicate ) { return ( x ) => ! predicate ( x ) ; } export function or < T > ( ... predicates ) { return ( x ) => predicates . some ( ( predicate ) => predicate ( x ) ) ; } export function and < T > ( ... predicates ) { return ( x ) => predicates . every ( ( predicate ) => predicate ( x ) ) ; } export function clamp ( num , min , max ) { return Math . max ( min , Math . min ( max , num ) ) ; } export function urlToPath ( stringUrl ) { const url = URI . parse ( stringUrl ) ; if ( url . scheme !== 'file' ) { return null ; } return url . fsPath . replace ( /\\/g , '/' ) ; } export function pathToUrl ( path ) { return URI . file ( path ) . toString ( ) ; } /**
 * Some paths (on windows) start with a upper case driver letter, some don't.
 * This is normalized here.
 */ export function normalizePath ( path ) { return URI . file ( path ) . fsPath . replace ( /\\/g , '/' ) ; } /**
 * URIs coming from the client could be encoded in a different
 * way than expected / than the internal services create them.
 * This normalizes them to be the same as the internally generated ones.
 */ export function normalizeUri ( uri ) { return URI . parse ( uri ) . toString ( ) ; } /**
 * Given a path like foo/bar or foo/bar.svelte , returns its last path
 * (bar or bar.svelte in this example).
 */ export function getLastPartOfPath ( path ) { return path . replace ( /\\/g , '/' ) . split ( '/' ) . pop ( ) || '' ; } export function flatten < T > ( arr ) { return arr . reduce ( ( all , item ) => ( Array . isArray ( item ) ? [ ... all , ... item ] : [ ... all , item ] ) , [ ] ) ; } /**
 * Map or keep original (passthrough) if the mapper returns undefined.
 */ export function passMap < T > ( array , mapper ) { return array . map ( ( x ) => { const mapped = mapper ( x ) ; return mapped === undefined ? x : mapped ; } ) ; } export function isInRange ( range , positionToTest ) { return ( isBeforeOrEqualToPosition ( range . end , positionToTest ) && isBeforeOrEqualToPosition ( positionToTest , range . start ) ) ; } export function isRangeStartAfterEnd ( range ) { return ( range . end . line < range . start . line || ( range . end . line === range . start . line && range . end . character < range . start . character ) ) ; } export function swapRangeStartEndIfNecessary ( range ) { if ( isRangeStartAfterEnd ( range ) ) { const start = range . start ; range . start = range . end ; range . end = start ; } return range ; } export function moveRangeStartToEndIfNecessary ( range ) { if ( isRangeStartAfterEnd ( range ) ) { range . start = range . end ; } return range ; } export function isBeforeOrEqualToPosition ( position , positionToTest ) { return ( positionToTest . line < position . line || ( positionToTest . line === position . line && positionToTest . character <= position . character ) ) ; } export function isNotNullOrUndefined < T > ( val ) { return val !== undefined && val !== null ; } param fn param determineIfSame param miliseconds export function debounceSameArg < T > ( fn , shouldCancelPrevious , miliseconds ) { let timeout ; let prevArg ; return ( arg ) => { if ( shouldCancelPrevious ( arg , prevArg ) ) { clearTimeout ( timeout ) ; } prevArg = arg ; timeout = setTimeout ( ( ) => { fn ( arg ) ; prevArg = undefined ; } , miliseconds ) ; } ; } param fn param miliseconds export function debounceThrottle < T extends ( ... args : any ) => void > ( fn , miliseconds ) { let timeout ; let lastInvocation = Date . now ( ) - miliseconds ; function maybeCall ( ... args ) { clearTimeout ( timeout ) ; timeout = setTimeout ( ( ) => { if ( Date . now ( ) - lastInvocation < miliseconds ) { maybeCall ( ... args ) ; return ; } fn ( ... args ) ; lastInvocation = Date . now ( ) ; } , miliseconds ) ; } return maybeCall as any ; } /**
 * Like str.lastIndexOf, but for regular expressions. Note that you need to provide the g-flag to your RegExp!
 */ export function regexLastIndexOf ( text , regex , endPos ? ) { if ( endPos === undefined ) { endPos = text . length ; } else if ( endPos < 0 ) { endPos = 0 ; } const stringToWorkWith = text . substring ( 0 , endPos + 1 ) ; let lastIndexOf = - 1 ; let result = null ; while ( ( result = regex . exec ( stringToWorkWith ) ) !== null ) { lastIndexOf = result . index ; } return lastIndexOf ; } /**
 * Like str.indexOf, but for regular expressions.
 */ export function regexIndexOf ( text , regex , startPos ? ) { if ( startPos === undefined || startPos < 0 ) { startPos = 0 ; } const stringToWorkWith = text . substring ( startPos ) ; const result = regex . exec ( stringToWorkWith ) ; return result ?. index ?? - 1 ; } /**
 * Get all matches of a regexp.
 */ export function getRegExpMatches ( regex , str ) { const matches = [ ] ; let match ; while ( ( match = regex . exec ( str ) ) ) { matches . push ( match ) ; } return matches ; } /**
 * Function to modify each line of a text, preserving the line break style (`\n` or `\r\n`)
 */ export function modifyLines ( text , replacementFn ) { let idx = 0 ; return text . split ( '\r\n' ) . map ( ( l1 ) => l1 . split ( '\n' ) . map ( ( line ) => replacementFn ( line , idx ++ ) ) . join ( '\n' ) ) . join ( '\r\n' ) ; } /**
 * Like array.filter, but asynchronous
 */ export async function filterAsync < T > ( array , predicate ) { const fail = Symbol ( ) ; return ( await Promise . all ( array . map ( async ( item , idx ) => ( ( await predicate ( item , idx ) ) ? item : fail ) ) ) ) . filter ( ( i ) => i !== fail ) as T [ ] ; } export function getIndent ( text ) { return /^[ |\t]+/ . exec ( text ) ?. [ 0 ] ?? '' ; } /**
 *
 * The html language service is case insensitive, and would provide
 * hover/ completion info for Svelte components like `Option` which have
 * the same name like a html tag.
 *
 * Also, svelte directives like action and event modifier only work
 * with element not component
 */ export function possiblyComponent ( node ) { return ! ! node . tag ?. [ 0 ] . match ( /[A-Z]/ ) ; } /**
 * If the object if it has entries, else undefined
 */ export function returnObjectIfHasKeys < T > ( obj ) { if ( Object . keys ( obj || { } ) . length > 0 ) { return obj ; } }