1import { 2 __String, 3 CharacterCodes, 4 Comparer, 5 Comparison, 6 Debug, 7 EqualityComparer, 8 ESMap, 9 isWhiteSpaceLike, 10 Iterator, 11 Map, 12 MapLike, 13 Push, 14 Queue, 15 ReadonlyESMap, 16 ReadonlySet, 17 Set, 18 SortedArray, 19 SortedReadonlyArray, 20 TextSpan, 21 UnderscoreEscapedMap 22} from "./_namespaces/ts"; 23 24/** @internal */ 25export function getIterator<I extends readonly any[] | ReadonlySet<any> | ReadonlyESMap<any, any> | undefined>(iterable: I): Iterator< 26 I extends ReadonlyESMap<infer K, infer V> ? [K, V] : 27 I extends ReadonlySet<infer T> ? T : 28 I extends readonly (infer T)[] ? T : 29 I extends undefined ? undefined : 30 never>; 31/** @internal */ 32export function getIterator<K, V>(iterable: ReadonlyESMap<K, V>): Iterator<[K, V]>; 33/** @internal */ 34export function getIterator<K, V>(iterable: ReadonlyESMap<K, V> | undefined): Iterator<[K, V]> | undefined; 35/** @internal */ 36export function getIterator<T>(iterable: readonly T[] | ReadonlySet<T>): Iterator<T>; 37/** @internal */ 38export function getIterator<T>(iterable: readonly T[] | ReadonlySet<T> | undefined): Iterator<T> | undefined; 39/** @internal */ 40export function getIterator(iterable: readonly any[] | ReadonlySet<any> | ReadonlyESMap<any, any> | undefined): Iterator<any> | undefined { 41 if (iterable) { 42 if (isArray(iterable)) return arrayIterator(iterable); 43 if (iterable instanceof Map) return iterable.entries(); 44 if (iterable instanceof Set) return iterable.values(); 45 throw new Error("Iteration not supported."); 46 } 47} 48 49/** @internal */ 50export const emptyArray: never[] = [] as never[]; 51/** @internal */ 52export const emptyMap: ReadonlyESMap<never, never> = new Map<never, never>(); 53/** @internal */ 54export const emptySet: ReadonlySet<never> = new Set<never>(); 55 56/** @internal */ 57export function length(array: readonly any[] | undefined): number { 58 return array ? array.length : 0; 59} 60 61/** 62 * Iterates through 'array' by index and performs the callback on each element of array until the callback 63 * returns a truthy value, then returns that value. 64 * If no such value is found, the callback is applied to each element of array and undefined is returned. 65 * 66 * @internal 67 */ 68export function forEach<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { 69 if (array) { 70 for (let i = 0; i < array.length; i++) { 71 const result = callback(array[i], i); 72 if (result) { 73 return result; 74 } 75 } 76 } 77 return undefined; 78} 79 80/** 81 * Like `forEach`, but iterates in reverse order. 82 * 83 * @internal 84 */ 85export function forEachRight<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { 86 if (array) { 87 for (let i = array.length - 1; i >= 0; i--) { 88 const result = callback(array[i], i); 89 if (result) { 90 return result; 91 } 92 } 93 } 94 return undefined; 95} 96 97/** Like `forEach`, but suitable for use with numbers and strings (which may be falsy). 98 * 99 * @internal 100 */ 101export function firstDefined<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { 102 if (array === undefined) { 103 return undefined; 104 } 105 106 for (let i = 0; i < array.length; i++) { 107 const result = callback(array[i], i); 108 if (result !== undefined) { 109 return result; 110 } 111 } 112 return undefined; 113} 114 115/** @internal */ 116export function firstDefinedIterator<T, U>(iter: Iterator<T>, callback: (element: T) => U | undefined): U | undefined { 117 while (true) { 118 const iterResult = iter.next(); 119 if (iterResult.done) { 120 return undefined; 121 } 122 const result = callback(iterResult.value); 123 if (result !== undefined) { 124 return result; 125 } 126 } 127} 128 129/** @internal */ 130export function reduceLeftIterator<T, U>(iterator: Iterator<T> | undefined, f: (memo: U, value: T, i: number) => U, initial: U): U { 131 let result = initial; 132 if (iterator) { 133 for (let step = iterator.next(), pos = 0; !step.done; step = iterator.next(), pos++) { 134 result = f(result, step.value, pos); 135 } 136 } 137 return result; 138} 139 140/** @internal */ 141export function zipWith<T, U, V>(arrayA: readonly T[], arrayB: readonly U[], callback: (a: T, b: U, index: number) => V): V[] { 142 const result: V[] = []; 143 Debug.assertEqual(arrayA.length, arrayB.length); 144 for (let i = 0; i < arrayA.length; i++) { 145 result.push(callback(arrayA[i], arrayB[i], i)); 146 } 147 return result; 148} 149 150/** @internal */ 151export function zipToIterator<T, U>(arrayA: readonly T[], arrayB: readonly U[]): Iterator<[T, U]> { 152 Debug.assertEqual(arrayA.length, arrayB.length); 153 let i = 0; 154 return { 155 next() { 156 if (i === arrayA.length) { 157 return { value: undefined as never, done: true }; 158 } 159 i++; 160 return { value: [arrayA[i - 1], arrayB[i - 1]] as [T, U], done: false }; 161 } 162 }; 163} 164 165/** @internal */ 166export function zipToMap<K, V>(keys: readonly K[], values: readonly V[]): ESMap<K, V> { 167 Debug.assert(keys.length === values.length); 168 const map = new Map<K, V>(); 169 for (let i = 0; i < keys.length; ++i) { 170 map.set(keys[i], values[i]); 171 } 172 return map; 173} 174 175/** 176 * Creates a new array with `element` interspersed in between each element of `input` 177 * if there is more than 1 value in `input`. Otherwise, returns the existing array. 178 * 179 * @internal 180 */ 181export function intersperse<T>(input: T[], element: T): T[] { 182 if (input.length <= 1) { 183 return input; 184 } 185 const result: T[] = []; 186 for (let i = 0, n = input.length; i < n; i++) { 187 if (i) result.push(element); 188 result.push(input[i]); 189 } 190 return result; 191} 192 193/** 194 * Iterates through `array` by index and performs the callback on each element of array until the callback 195 * returns a falsey value, then returns false. 196 * If no such value is found, the callback is applied to each element of array and `true` is returned. 197 * 198 * @internal 199 */ 200export function every<T>(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean { 201 if (array) { 202 for (let i = 0; i < array.length; i++) { 203 if (!callback(array[i], i)) { 204 return false; 205 } 206 } 207 } 208 209 return true; 210} 211 212/** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. 213 * 214 * @internal 215 */ 216export function find<T, U extends T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => element is U, startIndex?: number): U | undefined; 217/** @internal */ 218export function find<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined; 219/** @internal */ 220export function find<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined { 221 if (array === undefined) return undefined; 222 for (let i = startIndex ?? 0; i < array.length; i++) { 223 const value = array[i]; 224 if (predicate(value, i)) { 225 return value; 226 } 227 } 228 return undefined; 229} 230 231/** @internal */ 232export function findLast<T, U extends T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => element is U, startIndex?: number): U | undefined; 233/** @internal */ 234export function findLast<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined; 235/** @internal */ 236export function findLast<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined { 237 if (array === undefined) return undefined; 238 for (let i = startIndex ?? array.length - 1; i >= 0; i--) { 239 const value = array[i]; 240 if (predicate(value, i)) { 241 return value; 242 } 243 } 244 return undefined; 245} 246 247/** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. 248 * 249 * @internal 250 */ 251export function findIndex<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): number { 252 if (array === undefined) return -1; 253 for (let i = startIndex ?? 0; i < array.length; i++) { 254 if (predicate(array[i], i)) { 255 return i; 256 } 257 } 258 return -1; 259} 260 261/** @internal */ 262export function findLastIndex<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): number { 263 if (array === undefined) return -1; 264 for (let i = startIndex ?? array.length - 1; i >= 0; i--) { 265 if (predicate(array[i], i)) { 266 return i; 267 } 268 } 269 return -1; 270} 271 272/** 273 * Returns the first truthy result of `callback`, or else fails. 274 * This is like `forEach`, but never returns undefined. 275 * 276 * @internal 277 */ 278export function findMap<T, U>(array: readonly T[], callback: (element: T, index: number) => U | undefined): U { 279 for (let i = 0; i < array.length; i++) { 280 const result = callback(array[i], i); 281 if (result) { 282 return result; 283 } 284 } 285 return Debug.fail(); 286} 287 288/** @internal */ 289export function contains<T>(array: readonly T[] | undefined, value: T, equalityComparer: EqualityComparer<T> = equateValues): boolean { 290 if (array) { 291 for (const v of array) { 292 if (equalityComparer(v, value)) { 293 return true; 294 } 295 } 296 } 297 return false; 298} 299 300/** @internal */ 301export function arraysEqual<T>(a: readonly T[], b: readonly T[], equalityComparer: EqualityComparer<T> = equateValues): boolean { 302 return a.length === b.length && a.every((x, i) => equalityComparer(x, b[i])); 303} 304 305/** @internal */ 306export function indexOfAnyCharCode(text: string, charCodes: readonly number[], start?: number): number { 307 for (let i = start || 0; i < text.length; i++) { 308 if (contains(charCodes, text.charCodeAt(i))) { 309 return i; 310 } 311 } 312 return -1; 313} 314 315/** @internal */ 316export function countWhere<T>(array: readonly T[] | undefined, predicate: (x: T, i: number) => boolean): number { 317 let count = 0; 318 if (array) { 319 for (let i = 0; i < array.length; i++) { 320 const v = array[i]; 321 if (predicate(v, i)) { 322 count++; 323 } 324 } 325 } 326 return count; 327} 328 329/** 330 * Filters an array by a predicate function. Returns the same array instance if the predicate is 331 * true for all elements, otherwise returns a new array instance containing the filtered subset. 332 * 333 * @internal 334 */ 335export function filter<T, U extends T>(array: T[], f: (x: T) => x is U): U[]; 336/** @internal */ 337export function filter<T>(array: T[], f: (x: T) => boolean): T[]; 338/** @internal */ 339export function filter<T, U extends T>(array: readonly T[], f: (x: T) => x is U): readonly U[]; 340/** @internal */ 341export function filter<T, U extends T>(array: readonly T[], f: (x: T) => boolean): readonly T[]; 342/** @internal */ 343export function filter<T, U extends T>(array: T[] | undefined, f: (x: T) => x is U): U[] | undefined; 344/** @internal */ 345export function filter<T>(array: T[] | undefined, f: (x: T) => boolean): T[] | undefined; 346/** @internal */ 347export function filter<T, U extends T>(array: readonly T[] | undefined, f: (x: T) => x is U): readonly U[] | undefined; 348/** @internal */ 349export function filter<T, U extends T>(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined; 350/** @internal */ 351export function filter<T>(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined { 352 if (array) { 353 const len = array.length; 354 let i = 0; 355 while (i < len && f(array[i])) i++; 356 if (i < len) { 357 const result = array.slice(0, i); 358 i++; 359 while (i < len) { 360 const item = array[i]; 361 if (f(item)) { 362 result.push(item); 363 } 364 i++; 365 } 366 return result; 367 } 368 } 369 return array; 370} 371 372/** @internal */ 373export function filterMutate<T>(array: T[], f: (x: T, i: number, array: T[]) => boolean): void { 374 let outIndex = 0; 375 for (let i = 0; i < array.length; i++) { 376 if (f(array[i], i, array)) { 377 array[outIndex] = array[i]; 378 outIndex++; 379 } 380 } 381 array.length = outIndex; 382} 383 384/** @internal */ 385export function clear(array: unknown[]): void { 386 array.length = 0; 387} 388 389/** @internal */ 390export function map<T, U>(array: readonly T[], f: (x: T, i: number) => U): U[]; 391/** @internal */ 392export function map<T, U>(array: readonly T[] | undefined, f: (x: T, i: number) => U): U[] | undefined; 393/** @internal */ 394export function map<T, U>(array: readonly T[] | undefined, f: (x: T, i: number) => U): U[] | undefined { 395 let result: U[] | undefined; 396 if (array) { 397 result = []; 398 for (let i = 0; i < array.length; i++) { 399 result.push(f(array[i], i)); 400 } 401 } 402 return result; 403} 404 405 406/** @internal */ 407export function mapIterator<T, U>(iter: Iterator<T>, mapFn: (x: T) => U): Iterator<U> { 408 return { 409 next() { 410 const iterRes = iter.next(); 411 return iterRes.done ? iterRes as { done: true, value: never } : { value: mapFn(iterRes.value), done: false }; 412 } 413 }; 414} 415 416/** 417 * Maps from T to T and avoids allocation if all elements map to themselves 418 * 419 * @internal */ 420export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[]; 421/** @internal */ 422export function sameMap<T>(array: readonly T[], f: (x: T, i: number) => T): readonly T[]; 423/** @internal */ 424export function sameMap<T>(array: T[] | undefined, f: (x: T, i: number) => T): T[] | undefined; 425/** @internal */ 426export function sameMap<T>(array: readonly T[] | undefined, f: (x: T, i: number) => T): readonly T[] | undefined; 427/** @internal */ 428export function sameMap<T>(array: readonly T[] | undefined, f: (x: T, i: number) => T): readonly T[] | undefined { 429 if (array) { 430 for (let i = 0; i < array.length; i++) { 431 const item = array[i]; 432 const mapped = f(item, i); 433 if (item !== mapped) { 434 const result = array.slice(0, i); 435 result.push(mapped); 436 for (i++; i < array.length; i++) { 437 result.push(f(array[i], i)); 438 } 439 return result; 440 } 441 } 442 } 443 return array; 444} 445 446/** 447 * Flattens an array containing a mix of array or non-array elements. 448 * 449 * @param array The array to flatten. 450 * 451 * @internal 452 */ 453export function flatten<T>(array: T[][] | readonly (T | readonly T[] | undefined)[]): T[] { 454 const result = []; 455 for (const v of array) { 456 if (v) { 457 if (isArray(v)) { 458 addRange(result, v); 459 } 460 else { 461 result.push(v); 462 } 463 } 464 } 465 return result; 466} 467 468/** 469 * Maps an array. If the mapped value is an array, it is spread into the result. 470 * 471 * @param array The array to map. 472 * @param mapfn The callback used to map the result into one or more values. 473 * 474 * @internal 475 */ 476export function flatMap<T, U>(array: readonly T[] | undefined, mapfn: (x: T, i: number) => U | readonly U[] | undefined): readonly U[] { 477 let result: U[] | undefined; 478 if (array) { 479 for (let i = 0; i < array.length; i++) { 480 const v = mapfn(array[i], i); 481 if (v) { 482 if (isArray(v)) { 483 result = addRange(result, v); 484 } 485 else { 486 result = append(result, v); 487 } 488 } 489 } 490 } 491 return result || emptyArray; 492} 493 494/** @internal */ 495export function flatMapToMutable<T, U>(array: readonly T[] | undefined, mapfn: (x: T, i: number) => U | readonly U[] | undefined): U[] { 496 const result: U[] = []; 497 if (array) { 498 for (let i = 0; i < array.length; i++) { 499 const v = mapfn(array[i], i); 500 if (v) { 501 if (isArray(v)) { 502 addRange(result, v); 503 } 504 else { 505 result.push(v); 506 } 507 } 508 } 509 } 510 return result; 511} 512 513/** @internal */ 514export function flatMapIterator<T, U>(iter: Iterator<T>, mapfn: (x: T) => readonly U[] | Iterator<U> | undefined): Iterator<U> { 515 const first = iter.next(); 516 if (first.done) { 517 return emptyIterator; 518 } 519 let currentIter = getIterator(first.value); 520 return { 521 next() { 522 while (true) { 523 const currentRes = currentIter.next(); 524 if (!currentRes.done) { 525 return currentRes; 526 } 527 const iterRes = iter.next(); 528 if (iterRes.done) { 529 return iterRes as { done: true, value: never }; 530 } 531 currentIter = getIterator(iterRes.value); 532 } 533 }, 534 }; 535 536 function getIterator(x: T): Iterator<U> { 537 const res = mapfn(x); 538 return res === undefined ? emptyIterator : isArray(res) ? arrayIterator(res) : res; 539 } 540} 541 542/** 543 * Maps an array. If the mapped value is an array, it is spread into the result. 544 * Avoids allocation if all elements map to themselves. 545 * 546 * @param array The array to map. 547 * @param mapfn The callback used to map the result into one or more values. 548 * 549 * @internal 550 */ 551export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | readonly T[]): T[]; 552/** @internal */ 553export function sameFlatMap<T>(array: readonly T[], mapfn: (x: T, i: number) => T | readonly T[]): readonly T[]; 554/** @internal */ 555export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] { 556 let result: T[] | undefined; 557 if (array) { 558 for (let i = 0; i < array.length; i++) { 559 const item = array[i]; 560 const mapped = mapfn(item, i); 561 if (result || item !== mapped || isArray(mapped)) { 562 if (!result) { 563 result = array.slice(0, i); 564 } 565 if (isArray(mapped)) { 566 addRange(result, mapped); 567 } 568 else { 569 result.push(mapped); 570 } 571 } 572 } 573 } 574 return result || array; 575} 576 577/** @internal */ 578export function mapAllOrFail<T, U>(array: readonly T[], mapFn: (x: T, i: number) => U | undefined): U[] | undefined { 579 const result: U[] = []; 580 for (let i = 0; i < array.length; i++) { 581 const mapped = mapFn(array[i], i); 582 if (mapped === undefined) { 583 return undefined; 584 } 585 result.push(mapped); 586 } 587 return result; 588} 589 590/** @internal */ 591export function mapDefined<T, U>(array: readonly T[] | undefined, mapFn: (x: T, i: number) => U | undefined): U[] { 592 const result: U[] = []; 593 if (array) { 594 for (let i = 0; i < array.length; i++) { 595 const mapped = mapFn(array[i], i); 596 if (mapped !== undefined) { 597 result.push(mapped); 598 } 599 } 600 } 601 return result; 602} 603 604/** @internal */ 605export function mapDefinedIterator<T, U>(iter: Iterator<T>, mapFn: (x: T) => U | undefined): Iterator<U> { 606 return { 607 next() { 608 while (true) { 609 const res = iter.next(); 610 if (res.done) { 611 return res as { done: true, value: never }; 612 } 613 const value = mapFn(res.value); 614 if (value !== undefined) { 615 return { value, done: false }; 616 } 617 } 618 } 619 }; 620} 621 622/** @internal */ 623export function mapDefinedEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1>, f: (key: K1, value: V1) => readonly [K2, V2] | undefined): ESMap<K2, V2>; 624/** @internal */ 625export function mapDefinedEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): ESMap<K2, V2> | undefined; 626/** @internal */ 627export function mapDefinedEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): ESMap<K2, V2> | undefined { 628 if (!map) { 629 return undefined; 630 } 631 632 const result = new Map<K2, V2>(); 633 map.forEach((value, key) => { 634 const entry = f(key, value); 635 if (entry !== undefined) { 636 const [newKey, newValue] = entry; 637 if (newKey !== undefined && newValue !== undefined) { 638 result.set(newKey, newValue); 639 } 640 } 641 }); 642 643 return result; 644} 645 646/** @internal */ 647export function mapDefinedValues<V1, V2>(set: ReadonlySet<V1>, f: (value: V1) => V2 | undefined): Set<V2>; 648/** @internal */ 649export function mapDefinedValues<V1, V2>(set: ReadonlySet<V1> | undefined, f: (value: V1) => V2 | undefined): Set<V2> | undefined; 650/** @internal */ 651export function mapDefinedValues<V1, V2>(set: ReadonlySet<V1> | undefined, f: (value: V1) => V2 | undefined): Set<V2> | undefined { 652 if (set) { 653 const result = new Set<V2>(); 654 set.forEach(value => { 655 const newValue = f(value); 656 if (newValue !== undefined) { 657 result.add(newValue); 658 } 659 }); 660 return result; 661 } 662} 663 664/** @internal */ 665export function getOrUpdate<K, V>(map: ESMap<K, V>, key: K, callback: () => V) { 666 if (map.has(key)) { 667 return map.get(key)!; 668 } 669 const value = callback(); 670 map.set(key, value); 671 return value; 672} 673 674/** @internal */ 675export function tryAddToSet<T>(set: Set<T>, value: T) { 676 if (!set.has(value)) { 677 set.add(value); 678 return true; 679 } 680 return false; 681} 682 683/** @internal */ 684export const emptyIterator: Iterator<never> = { next: () => ({ value: undefined as never, done: true }) }; 685 686/** @internal */ 687export function singleIterator<T>(value: T): Iterator<T> { 688 let done = false; 689 return { 690 next() { 691 const wasDone = done; 692 done = true; 693 return wasDone ? { value: undefined as never, done: true } : { value, done: false }; 694 } 695 }; 696} 697 698/** 699 * Maps contiguous spans of values with the same key. 700 * 701 * @param array The array to map. 702 * @param keyfn A callback used to select the key for an element. 703 * @param mapfn A callback used to map a contiguous chunk of values to a single value. 704 * 705 * @internal 706 */ 707export function spanMap<T, K, U>(array: readonly T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[]; 708/** @internal */ 709export function spanMap<T, K, U>(array: readonly T[] | undefined, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] | undefined; 710/** @internal */ 711export function spanMap<T, K, U>(array: readonly T[] | undefined, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] | undefined { 712 let result: U[] | undefined; 713 if (array) { 714 result = []; 715 const len = array.length; 716 let previousKey: K | undefined; 717 let key: K | undefined; 718 let start = 0; 719 let pos = 0; 720 while (start < len) { 721 while (pos < len) { 722 const value = array[pos]; 723 key = keyfn(value, pos); 724 if (pos === 0) { 725 previousKey = key; 726 } 727 else if (key !== previousKey) { 728 break; 729 } 730 731 pos++; 732 } 733 734 if (start < pos) { 735 const v = mapfn(array.slice(start, pos), previousKey!, start, pos); 736 if (v) { 737 result.push(v); 738 } 739 740 start = pos; 741 } 742 743 previousKey = key; 744 pos++; 745 } 746 } 747 748 return result; 749} 750 751/** @internal */ 752export function mapEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1>, f: (key: K1, value: V1) => readonly [K2, V2]): ESMap<K2, V2>; 753/** @internal */ 754export function mapEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): ESMap<K2, V2> | undefined; 755/** @internal */ 756export function mapEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): ESMap<K2, V2> | undefined { 757 if (!map) { 758 return undefined; 759 } 760 761 const result = new Map<K2, V2>(); 762 map.forEach((value, key) => { 763 const [newKey, newValue] = f(key, value); 764 result.set(newKey, newValue); 765 }); 766 return result; 767} 768 769/** @internal */ 770export function some<T>(array: readonly T[] | undefined): array is readonly T[]; 771/** @internal */ 772export function some<T>(array: readonly T[] | undefined, predicate: (value: T) => boolean): boolean; 773/** @internal */ 774export function some<T>(array: readonly T[] | undefined, predicate?: (value: T) => boolean): boolean { 775 if (array) { 776 if (predicate) { 777 for (const v of array) { 778 if (predicate(v)) { 779 return true; 780 } 781 } 782 } 783 else { 784 return array.length > 0; 785 } 786 } 787 return false; 788} 789 790/** Calls the callback with (start, afterEnd) index pairs for each range where 'pred' is true. 791 * 792 * @internal 793 */ 794export function getRangesWhere<T>(arr: readonly T[], pred: (t: T) => boolean, cb: (start: number, afterEnd: number) => void): void { 795 let start: number | undefined; 796 for (let i = 0; i < arr.length; i++) { 797 if (pred(arr[i])) { 798 start = start === undefined ? i : start; 799 } 800 else { 801 if (start !== undefined) { 802 cb(start, i); 803 start = undefined; 804 } 805 } 806 } 807 if (start !== undefined) cb(start, arr.length); 808} 809 810/** @internal */ 811export function concatenate<T>(array1: T[], array2: T[]): T[]; 812/** @internal */ 813export function concatenate<T>(array1: readonly T[], array2: readonly T[]): readonly T[]; 814/** @internal */ 815export function concatenate<T>(array1: T[] | undefined, array2: T[] | undefined): T[]; 816/** @internal */ 817export function concatenate<T>(array1: readonly T[] | undefined, array2: readonly T[] | undefined): readonly T[]; 818/** @internal */ 819export function concatenate<T>(array1: T[], array2: T[]): T[] { 820 if (!some(array2)) return array1; 821 if (!some(array1)) return array2; 822 return [...array1, ...array2]; 823} 824 825function selectIndex(_: unknown, i: number) { 826 return i; 827} 828 829/** @internal */ 830export function indicesOf(array: readonly unknown[]): number[] { 831 return array.map(selectIndex); 832} 833 834function deduplicateRelational<T>(array: readonly T[], equalityComparer: EqualityComparer<T>, comparer: Comparer<T>) { 835 // Perform a stable sort of the array. This ensures the first entry in a list of 836 // duplicates remains the first entry in the result. 837 const indices = indicesOf(array); 838 stableSortIndices(array, indices, comparer); 839 840 let last = array[indices[0]]; 841 const deduplicated: number[] = [indices[0]]; 842 for (let i = 1; i < indices.length; i++) { 843 const index = indices[i]; 844 const item = array[index]; 845 if (!equalityComparer(last, item)) { 846 deduplicated.push(index); 847 last = item; 848 } 849 } 850 851 // restore original order 852 deduplicated.sort(); 853 return deduplicated.map(i => array[i]); 854} 855 856function deduplicateEquality<T>(array: readonly T[], equalityComparer: EqualityComparer<T>) { 857 const result: T[] = []; 858 for (const item of array) { 859 pushIfUnique(result, item, equalityComparer); 860 } 861 return result; 862} 863 864/** 865 * Deduplicates an unsorted array. 866 * @param equalityComparer An `EqualityComparer` used to determine if two values are duplicates. 867 * @param comparer An optional `Comparer` used to sort entries before comparison, though the 868 * result will remain in the original order in `array`. 869 * 870 * @internal 871 */ 872export function deduplicate<T>(array: readonly T[], equalityComparer: EqualityComparer<T>, comparer?: Comparer<T>): T[] { 873 return array.length === 0 ? [] : 874 array.length === 1 ? array.slice() : 875 comparer ? deduplicateRelational(array, equalityComparer, comparer) : 876 deduplicateEquality(array, equalityComparer); 877} 878 879/** 880 * Deduplicates an array that has already been sorted. 881 */ 882function deduplicateSorted<T>(array: SortedReadonlyArray<T>, comparer: EqualityComparer<T> | Comparer<T>): SortedReadonlyArray<T> { 883 if (array.length === 0) return emptyArray as any as SortedReadonlyArray<T>; 884 885 let last = array[0]; 886 const deduplicated: T[] = [last]; 887 for (let i = 1; i < array.length; i++) { 888 const next = array[i]; 889 switch (comparer(next, last)) { 890 // equality comparison 891 case true: 892 893 // relational comparison 894 // falls through 895 case Comparison.EqualTo: 896 continue; 897 898 case Comparison.LessThan: 899 // If `array` is sorted, `next` should **never** be less than `last`. 900 return Debug.fail("Array is unsorted."); 901 } 902 903 deduplicated.push(last = next); 904 } 905 906 return deduplicated as any as SortedReadonlyArray<T>; 907} 908 909/** @internal */ 910export function createSortedArray<T>(): SortedArray<T> { 911 return [] as any as SortedArray<T>; // TODO: GH#19873 912} 913 914/** @internal */ 915export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>, allowDuplicates?: boolean): boolean { 916 if (array.length === 0) { 917 array.push(insert); 918 return true; 919 } 920 921 const insertIndex = binarySearch(array, insert, identity, compare); 922 if (insertIndex < 0) { 923 array.splice(~insertIndex, 0, insert); 924 return true; 925 } 926 927 if (allowDuplicates) { 928 array.splice(insertIndex, 0, insert); 929 return true; 930 } 931 932 return false; 933} 934 935/** @internal */ 936export function sortAndDeduplicate<T>(array: readonly string[]): SortedReadonlyArray<string>; 937/** @internal */ 938export function sortAndDeduplicate<T>(array: readonly T[], comparer: Comparer<T>, equalityComparer?: EqualityComparer<T>): SortedReadonlyArray<T>; 939/** @internal */ 940export function sortAndDeduplicate<T>(array: readonly T[], comparer?: Comparer<T>, equalityComparer?: EqualityComparer<T>): SortedReadonlyArray<T> { 941 return deduplicateSorted(sort(array, comparer), equalityComparer || comparer || compareStringsCaseSensitive as any as Comparer<T>); 942} 943 944/** @internal */ 945export function arrayIsSorted<T>(array: readonly T[], comparer: Comparer<T>) { 946 if (array.length < 2) return true; 947 let prevElement = array[0]; 948 for (const element of array.slice(1)) { 949 if (comparer(prevElement, element) === Comparison.GreaterThan) { 950 return false; 951 } 952 prevElement = element; 953 } 954 return true; 955} 956 957/** @internal */ 958export function arrayIsEqualTo<T>(array1: readonly T[] | undefined, array2: readonly T[] | undefined, equalityComparer: (a: T, b: T, index: number) => boolean = equateValues): boolean { 959 if (!array1 || !array2) { 960 return array1 === array2; 961 } 962 963 if (array1.length !== array2.length) { 964 return false; 965 } 966 967 for (let i = 0; i < array1.length; i++) { 968 if (!equalityComparer(array1[i], array2[i], i)) { 969 return false; 970 } 971 } 972 973 return true; 974} 975 976/** 977 * Compacts an array, removing any falsey elements. 978 * 979 * @internal 980 */ 981export function compact<T>(array: (T | undefined | null | false | 0 | "")[]): T[]; 982/** @internal */ 983export function compact<T>(array: readonly (T | undefined | null | false | 0 | "")[]): readonly T[]; 984// ESLint thinks these can be combined with the above - they cannot; they'd produce higher-priority inferences and prevent the falsey types from being stripped 985/** @internal */ 986export function compact<T>(array: T[]): T[]; // eslint-disable-line @typescript-eslint/unified-signatures 987/** @internal */ 988export function compact<T>(array: readonly T[]): readonly T[]; // eslint-disable-line @typescript-eslint/unified-signatures 989/** @internal */ 990export function compact<T>(array: T[]): T[] { 991 let result: T[] | undefined; 992 if (array) { 993 for (let i = 0; i < array.length; i++) { 994 const v = array[i]; 995 if (result || !v) { 996 if (!result) { 997 result = array.slice(0, i); 998 } 999 if (v) { 1000 result.push(v); 1001 } 1002 } 1003 } 1004 } 1005 return result || array; 1006} 1007 1008/** 1009 * Gets the relative complement of `arrayA` with respect to `arrayB`, returning the elements that 1010 * are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted 1011 * based on the provided comparer. 1012 * 1013 * @internal 1014 */ 1015export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer<T>): T[] | undefined { 1016 if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB; 1017 const result: T[] = []; 1018 loopB: for (let offsetA = 0, offsetB = 0; offsetB < arrayB.length; offsetB++) { 1019 if (offsetB > 0) { 1020 // Ensure `arrayB` is properly sorted. 1021 Debug.assertGreaterThanOrEqual(comparer(arrayB[offsetB], arrayB[offsetB - 1]), Comparison.EqualTo); 1022 } 1023 1024 loopA: for (const startA = offsetA; offsetA < arrayA.length; offsetA++) { 1025 if (offsetA > startA) { 1026 // Ensure `arrayA` is properly sorted. We only need to perform this check if 1027 // `offsetA` has changed since we entered the loop. 1028 Debug.assertGreaterThanOrEqual(comparer(arrayA[offsetA], arrayA[offsetA - 1]), Comparison.EqualTo); 1029 } 1030 1031 switch (comparer(arrayB[offsetB], arrayA[offsetA])) { 1032 case Comparison.LessThan: 1033 // If B is less than A, B does not exist in arrayA. Add B to the result and 1034 // move to the next element in arrayB without changing the current position 1035 // in arrayA. 1036 result.push(arrayB[offsetB]); 1037 continue loopB; 1038 case Comparison.EqualTo: 1039 // If B is equal to A, B exists in arrayA. Move to the next element in 1040 // arrayB without adding B to the result or changing the current position 1041 // in arrayA. 1042 continue loopB; 1043 case Comparison.GreaterThan: 1044 // If B is greater than A, we need to keep looking for B in arrayA. Move to 1045 // the next element in arrayA and recheck. 1046 continue loopA; 1047 } 1048 } 1049 } 1050 return result; 1051} 1052 1053/** @internal */ 1054export function sum<T extends Record<K, number>, K extends string>(array: readonly T[], prop: K): number { 1055 let result = 0; 1056 for (const v of array) { 1057 result += v[prop]; 1058 } 1059 return result; 1060} 1061 1062/** 1063 * Appends a value to an array, returning the array. 1064 * 1065 * @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array 1066 * is created if `value` was appended. 1067 * @param value The value to append to the array. If `value` is `undefined`, nothing is 1068 * appended. 1069 * 1070 * @internal 1071 */ 1072export function append<TArray extends any[] | undefined, TValue extends NonNullable<TArray>[number] | undefined>(to: TArray, value: TValue): [undefined, undefined] extends [TArray, TValue] ? TArray : NonNullable<TArray>[number][]; 1073/** @internal */ 1074export function append<T>(to: T[], value: T | undefined): T[]; 1075/** @internal */ 1076export function append<T>(to: T[] | undefined, value: T): T[]; 1077/** @internal */ 1078export function append<T>(to: T[] | undefined, value: T | undefined): T[] | undefined; 1079/** @internal */ 1080export function append<T>(to: Push<T>, value: T | undefined): void; 1081/** @internal */ 1082export function append<T>(to: T[], value: T | undefined): T[] | undefined { 1083 if (value === undefined) return to; 1084 if (to === undefined) return [value]; 1085 to.push(value); 1086 return to; 1087} 1088 1089/** 1090 * Combines two arrays, values, or undefineds into the smallest container that can accommodate the resulting set: 1091 * 1092 * ``` 1093 * undefined -> undefined -> undefined 1094 * T -> undefined -> T 1095 * T -> T -> T[] 1096 * T[] -> undefined -> T[] (no-op) 1097 * T[] -> T -> T[] (append) 1098 * T[] -> T[] -> T[] (concatenate) 1099 * ``` 1100 * 1101 * @internal 1102 */ 1103export function combine<T>(xs: T | readonly T[] | undefined, ys: T | readonly T[] | undefined): T | readonly T[] | undefined; 1104/** @internal */ 1105export function combine<T>(xs: T | T[] | undefined, ys: T | T[] | undefined): T | T[] | undefined; 1106/** @internal */ 1107export function combine<T>(xs: T | T[] | undefined, ys: T | T[] | undefined) { 1108 if (xs === undefined) return ys; 1109 if (ys === undefined) return xs; 1110 if (isArray(xs)) return isArray(ys) ? concatenate(xs, ys) : append(xs, ys); 1111 if (isArray(ys)) return append(ys, xs); 1112 return [xs, ys]; 1113} 1114 1115/** 1116 * Gets the actual offset into an array for a relative offset. Negative offsets indicate a 1117 * position offset from the end of the array. 1118 */ 1119function toOffset(array: readonly any[], offset: number) { 1120 return offset < 0 ? array.length + offset : offset; 1121} 1122 1123/** 1124 * Appends a range of value to an array, returning the array. 1125 * 1126 * @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array 1127 * is created if `value` was appended. 1128 * @param from The values to append to the array. If `from` is `undefined`, nothing is 1129 * appended. If an element of `from` is `undefined`, that element is not appended. 1130 * @param start The offset in `from` at which to start copying values. 1131 * @param end The offset in `from` at which to stop copying values (non-inclusive). 1132 * 1133 * @internal 1134 */ 1135export function addRange<T>(to: T[], from: readonly T[] | undefined, start?: number, end?: number): T[]; 1136/** @internal */ 1137export function addRange<T>(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined; 1138/** @internal */ 1139export function addRange<T>(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined { 1140 if (from === undefined || from.length === 0) return to; 1141 if (to === undefined) return from.slice(start, end); 1142 start = start === undefined ? 0 : toOffset(from, start); 1143 end = end === undefined ? from.length : toOffset(from, end); 1144 for (let i = start; i < end && i < from.length; i++) { 1145 if (from[i] !== undefined) { 1146 to.push(from[i]); 1147 } 1148 } 1149 return to; 1150} 1151 1152/** 1153 * @return Whether the value was added. 1154 * 1155 * @internal 1156 */ 1157export function pushIfUnique<T>(array: T[], toAdd: T, equalityComparer?: EqualityComparer<T>): boolean { 1158 if (contains(array, toAdd, equalityComparer)) { 1159 return false; 1160 } 1161 else { 1162 array.push(toAdd); 1163 return true; 1164 } 1165} 1166 1167/** 1168 * Unlike `pushIfUnique`, this can take `undefined` as an input, and returns a new array. 1169 * 1170 * @internal 1171 */ 1172export function appendIfUnique<T>(array: T[] | undefined, toAdd: T, equalityComparer?: EqualityComparer<T>): T[] { 1173 if (array) { 1174 pushIfUnique(array, toAdd, equalityComparer); 1175 return array; 1176 } 1177 else { 1178 return [toAdd]; 1179 } 1180} 1181 1182function stableSortIndices<T>(array: readonly T[], indices: number[], comparer: Comparer<T>) { 1183 // sort indices by value then position 1184 indices.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)); 1185} 1186 1187/** 1188 * Returns a new sorted array. 1189 * 1190 * @internal 1191 */ 1192export function sort<T>(array: readonly T[], comparer?: Comparer<T>): SortedReadonlyArray<T> { 1193 return (array.length === 0 ? array : array.slice().sort(comparer)) as SortedReadonlyArray<T>; 1194} 1195 1196/** @internal */ 1197export function arrayIterator<T>(array: readonly T[]): Iterator<T> { 1198 let i = 0; 1199 return { next: () => { 1200 if (i === array.length) { 1201 return { value: undefined as never, done: true }; 1202 } 1203 else { 1204 i++; 1205 return { value: array[i - 1], done: false }; 1206 } 1207 }}; 1208} 1209 1210/** @internal */ 1211export function arrayReverseIterator<T>(array: readonly T[]): Iterator<T> { 1212 let i = array.length; 1213 return { 1214 next: () => { 1215 if (i === 0) { 1216 return { value: undefined as never, done: true }; 1217 } 1218 else { 1219 i--; 1220 return { value: array[i], done: false }; 1221 } 1222 } 1223 }; 1224} 1225 1226/** 1227 * Stable sort of an array. Elements equal to each other maintain their relative position in the array. 1228 * 1229 * @internal 1230 */ 1231export function stableSort<T>(array: readonly T[], comparer: Comparer<T>): SortedReadonlyArray<T> { 1232 const indices = indicesOf(array); 1233 stableSortIndices(array, indices, comparer); 1234 return indices.map(i => array[i]) as SortedArray<T> as SortedReadonlyArray<T>; 1235} 1236 1237/** @internal */ 1238export function rangeEquals<T>(array1: readonly T[], array2: readonly T[], pos: number, end: number) { 1239 while (pos < end) { 1240 if (array1[pos] !== array2[pos]) { 1241 return false; 1242 } 1243 pos++; 1244 } 1245 return true; 1246} 1247 1248/** 1249 * Returns the element at a specific offset in an array if non-empty, `undefined` otherwise. 1250 * A negative offset indicates the element should be retrieved from the end of the array. 1251 * 1252 * @internal 1253 */ 1254export function elementAt<T>(array: readonly T[] | undefined, offset: number): T | undefined { 1255 if (array) { 1256 offset = toOffset(array, offset); 1257 if (offset < array.length) { 1258 return array[offset]; 1259 } 1260 } 1261 return undefined; 1262} 1263 1264/** 1265 * Returns the first element of an array if non-empty, `undefined` otherwise. 1266 * 1267 * @internal 1268 */ 1269export function firstOrUndefined<T>(array: readonly T[] | undefined): T | undefined { 1270 return array === undefined || array.length === 0 ? undefined : array[0]; 1271} 1272 1273/** @internal */ 1274export function first<T>(array: readonly T[]): T { 1275 Debug.assert(array.length !== 0); 1276 return array[0]; 1277} 1278 1279/** 1280 * Returns the last element of an array if non-empty, `undefined` otherwise. 1281 * 1282 * @internal 1283 */ 1284export function lastOrUndefined<T>(array: readonly T[] | undefined): T | undefined { 1285 return array === undefined || array.length === 0 ? undefined : array[array.length - 1]; 1286} 1287 1288/** @internal */ 1289export function last<T>(array: readonly T[]): T { 1290 Debug.assert(array.length !== 0); 1291 return array[array.length - 1]; 1292} 1293 1294/** 1295 * Returns the only element of an array if it contains only one element, `undefined` otherwise. 1296 * 1297 * @internal 1298 */ 1299export function singleOrUndefined<T>(array: readonly T[] | undefined): T | undefined { 1300 return array && array.length === 1 1301 ? array[0] 1302 : undefined; 1303} 1304 1305/** 1306 * Returns the only element of an array if it contains only one element; throws otherwise. 1307 * 1308 * @internal 1309 */ 1310export function single<T>(array: readonly T[]): T { 1311 return Debug.checkDefined(singleOrUndefined(array)); 1312} 1313 1314/** 1315 * Returns the only element of an array if it contains only one element; otherwise, returns the 1316 * array. 1317 * 1318 * @internal 1319 */ 1320export function singleOrMany<T>(array: T[]): T | T[]; 1321/** @internal */ 1322export function singleOrMany<T>(array: readonly T[]): T | readonly T[]; 1323/** @internal */ 1324export function singleOrMany<T>(array: T[] | undefined): T | T[] | undefined; 1325/** @internal */ 1326export function singleOrMany<T>(array: readonly T[] | undefined): T | readonly T[] | undefined; 1327/** @internal */ 1328export function singleOrMany<T>(array: readonly T[] | undefined): T | readonly T[] | undefined { 1329 return array && array.length === 1 1330 ? array[0] 1331 : array; 1332} 1333 1334/** @internal */ 1335export function replaceElement<T>(array: readonly T[], index: number, value: T): T[] { 1336 const result = array.slice(0); 1337 result[index] = value; 1338 return result; 1339} 1340 1341/** 1342 * Performs a binary search, finding the index at which `value` occurs in `array`. 1343 * If no such index is found, returns the 2's-complement of first index at which 1344 * `array[index]` exceeds `value`. 1345 * @param array A sorted array whose first element must be no larger than number 1346 * @param value The value to be searched for in the array. 1347 * @param keySelector A callback used to select the search key from `value` and each element of 1348 * `array`. 1349 * @param keyComparer A callback used to compare two keys in a sorted array. 1350 * @param offset An offset into `array` at which to start the search. 1351 * 1352 * @internal 1353 */ 1354export function binarySearch<T, U>(array: readonly T[], value: T, keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number { 1355 return binarySearchKey(array, keySelector(value), keySelector, keyComparer, offset); 1356} 1357 1358/** 1359 * Performs a binary search, finding the index at which an object with `key` occurs in `array`. 1360 * If no such index is found, returns the 2's-complement of first index at which 1361 * `array[index]` exceeds `key`. 1362 * @param array A sorted array whose first element must be no larger than number 1363 * @param key The key to be searched for in the array. 1364 * @param keySelector A callback used to select the search key from each element of `array`. 1365 * @param keyComparer A callback used to compare two keys in a sorted array. 1366 * @param offset An offset into `array` at which to start the search. 1367 * 1368 * @internal 1369 */ 1370export function binarySearchKey<T, U>(array: readonly T[], key: U, keySelector: (v: T, i: number) => U, keyComparer: Comparer<U>, offset?: number): number { 1371 if (!some(array)) { 1372 return -1; 1373 } 1374 1375 let low = offset || 0; 1376 let high = array.length - 1; 1377 while (low <= high) { 1378 const middle = low + ((high - low) >> 1); 1379 const midKey = keySelector(array[middle], middle); 1380 switch (keyComparer(midKey, key)) { 1381 case Comparison.LessThan: 1382 low = middle + 1; 1383 break; 1384 case Comparison.EqualTo: 1385 return middle; 1386 case Comparison.GreaterThan: 1387 high = middle - 1; 1388 break; 1389 } 1390 } 1391 1392 return ~low; 1393} 1394 1395/** @internal */ 1396export function reduceLeft<T, U>(array: readonly T[] | undefined, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; 1397/** @internal */ 1398export function reduceLeft<T>(array: readonly T[], f: (memo: T, value: T, i: number) => T): T | undefined; 1399/** @internal */ 1400export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T | undefined { 1401 if (array && array.length > 0) { 1402 const size = array.length; 1403 if (size > 0) { 1404 let pos = start === undefined || start < 0 ? 0 : start; 1405 const end = count === undefined || pos + count > size - 1 ? size - 1 : pos + count; 1406 let result: T; 1407 if (arguments.length <= 2) { 1408 result = array[pos]; 1409 pos++; 1410 } 1411 else { 1412 result = initial!; 1413 } 1414 while (pos <= end) { 1415 result = f(result, array[pos], pos); 1416 pos++; 1417 } 1418 return result; 1419 } 1420 } 1421 return initial; 1422} 1423 1424const hasOwnProperty = Object.prototype.hasOwnProperty; 1425 1426/** 1427 * Indicates whether a map-like contains an own property with the specified key. 1428 * 1429 * @param map A map-like. 1430 * @param key A property key. 1431 * 1432 * @internal 1433 */ 1434export function hasProperty(map: MapLike<any>, key: string): boolean { 1435 return hasOwnProperty.call(map, key); 1436} 1437 1438/** 1439 * Gets the value of an owned property in a map-like. 1440 * 1441 * @param map A map-like. 1442 * @param key A property key. 1443 * 1444 * @internal 1445 */ 1446export function getProperty<T>(map: MapLike<T>, key: string): T | undefined { 1447 return hasOwnProperty.call(map, key) ? map[key] : undefined; 1448} 1449 1450/** 1451 * Gets the owned, enumerable property keys of a map-like. 1452 * 1453 * @internal 1454 */ 1455export function getOwnKeys<T>(map: MapLike<T>): string[] { 1456 const keys: string[] = []; 1457 for (const key in map) { 1458 if (hasOwnProperty.call(map, key)) { 1459 keys.push(key); 1460 } 1461 } 1462 1463 return keys; 1464} 1465 1466/** @internal */ 1467export function getAllKeys(obj: object): string[] { 1468 const result: string[] = []; 1469 do { 1470 const names = Object.getOwnPropertyNames(obj); 1471 for (const name of names) { 1472 pushIfUnique(result, name); 1473 } 1474 } while (obj = Object.getPrototypeOf(obj)); 1475 return result; 1476} 1477 1478/** @internal */ 1479export function getOwnValues<T>(collection: MapLike<T> | T[]): T[] { 1480 const values: T[] = []; 1481 for (const key in collection) { 1482 if (hasOwnProperty.call(collection, key)) { 1483 values.push((collection as MapLike<T>)[key]); 1484 } 1485 } 1486 1487 return values; 1488} 1489 1490const _entries = Object.entries || (<T>(obj: MapLike<T>) => { 1491 const keys = getOwnKeys(obj); 1492 const result: [string, T][] = Array(keys.length); 1493 for (let i = 0; i < keys.length; i++) { 1494 result[i] = [keys[i], obj[keys[i]]]; 1495 } 1496 return result; 1497}); 1498 1499/** @internal */ 1500export function getEntries<T>(obj: MapLike<T>): [string, T][] { 1501 return obj ? _entries(obj) : []; 1502} 1503 1504/** @internal */ 1505export function arrayOf<T>(count: number, f: (index: number) => T): T[] { 1506 const result = new Array(count); 1507 for (let i = 0; i < count; i++) { 1508 result[i] = f(i); 1509 } 1510 return result; 1511} 1512 1513/** Shims `Array.from`. 1514 * 1515 * @internal 1516 */ 1517export function arrayFrom<T, U>(iterator: Iterator<T> | IterableIterator<T>, map: (t: T) => U): U[]; 1518/** @internal */ 1519export function arrayFrom<T>(iterator: Iterator<T> | IterableIterator<T>): T[]; 1520/** @internal */ 1521export function arrayFrom<T, U>(iterator: Iterator<T> | IterableIterator<T>, map?: (t: T) => U): (T | U)[] { 1522 const result: (T | U)[] = []; 1523 for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { 1524 result.push(map ? map(iterResult.value) : iterResult.value); 1525 } 1526 return result; 1527} 1528 1529 1530/** @internal */ 1531export function assign<T extends object>(t: T, ...args: (T | undefined)[]) { 1532 for (const arg of args) { 1533 if (arg === undefined) continue; 1534 for (const p in arg) { 1535 if (hasProperty(arg, p)) { 1536 t[p] = arg[p]; 1537 } 1538 } 1539 } 1540 return t; 1541} 1542 1543/** 1544 * Performs a shallow equality comparison of the contents of two map-likes. 1545 * 1546 * @param left A map-like whose properties should be compared. 1547 * @param right A map-like whose properties should be compared. 1548 * 1549 * @internal 1550 */ 1551export function equalOwnProperties<T>(left: MapLike<T> | undefined, right: MapLike<T> | undefined, equalityComparer: EqualityComparer<T> = equateValues) { 1552 if (left === right) return true; 1553 if (!left || !right) return false; 1554 for (const key in left) { 1555 if (hasOwnProperty.call(left, key)) { 1556 if (!hasOwnProperty.call(right, key)) return false; 1557 if (!equalityComparer(left[key], right[key])) return false; 1558 } 1559 } 1560 1561 for (const key in right) { 1562 if (hasOwnProperty.call(right, key)) { 1563 if (!hasOwnProperty.call(left, key)) return false; 1564 } 1565 } 1566 1567 return true; 1568} 1569 1570/** 1571 * Creates a map from the elements of an array. 1572 * 1573 * @param array the array of input elements. 1574 * @param makeKey a function that produces a key for a given element. 1575 * 1576 * This function makes no effort to avoid collisions; if any two elements produce 1577 * the same key with the given 'makeKey' function, then the element with the higher 1578 * index in the array will be the one associated with the produced key. 1579 * 1580 * @internal 1581 */ 1582export function arrayToMap<K, V>(array: readonly V[], makeKey: (value: V) => K | undefined): ESMap<K, V>; 1583/** @internal */ 1584export function arrayToMap<K, V1, V2>(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V2): ESMap<K, V2>; 1585/** @internal */ 1586export function arrayToMap<T>(array: readonly T[], makeKey: (value: T) => string | undefined): ESMap<string, T>; 1587/** @internal */ 1588export function arrayToMap<T, U>(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): ESMap<string, U>; 1589/** @internal */ 1590export function arrayToMap<K, V1, V2>(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V1 | V2 = identity): ESMap<K, V1 | V2> { 1591 const result = new Map<K, V1 | V2>(); 1592 for (const value of array) { 1593 const key = makeKey(value); 1594 if (key !== undefined) result.set(key, makeValue(value)); 1595 } 1596 return result; 1597} 1598 1599/** @internal */ 1600export function arrayToNumericMap<T>(array: readonly T[], makeKey: (value: T) => number): T[]; 1601/** @internal */ 1602export function arrayToNumericMap<T, U>(array: readonly T[], makeKey: (value: T) => number, makeValue: (value: T) => U): U[]; 1603/** @internal */ 1604export function arrayToNumericMap<T, U>(array: readonly T[], makeKey: (value: T) => number, makeValue: (value: T) => T | U = identity): (T | U)[] { 1605 const result: (T | U)[] = []; 1606 for (const value of array) { 1607 result[makeKey(value)] = makeValue(value); 1608 } 1609 return result; 1610} 1611 1612/** @internal */ 1613export function arrayToMultiMap<K, V>(values: readonly V[], makeKey: (value: V) => K): MultiMap<K, V>; 1614/** @internal */ 1615export function arrayToMultiMap<K, V, U>(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => U): MultiMap<K, U>; 1616/** @internal */ 1617export function arrayToMultiMap<K, V, U>(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => V | U = identity): MultiMap<K, V | U> { 1618 const result = createMultiMap<K, V | U>(); 1619 for (const value of values) { 1620 result.add(makeKey(value), makeValue(value)); 1621 } 1622 return result; 1623} 1624 1625/** @internal */ 1626export function group<T, K>(values: readonly T[], getGroupId: (value: T) => K): readonly (readonly T[])[]; 1627/** @internal */ 1628export function group<T, K, R>(values: readonly T[], getGroupId: (value: T) => K, resultSelector: (values: readonly T[]) => R): R[]; 1629/** @internal */ 1630export function group<T>(values: readonly T[], getGroupId: (value: T) => string): readonly (readonly T[])[]; 1631/** @internal */ 1632export function group<T, R>(values: readonly T[], getGroupId: (value: T) => string, resultSelector: (values: readonly T[]) => R): R[]; 1633/** @internal */ 1634export function group<T, K>(values: readonly T[], getGroupId: (value: T) => K, resultSelector: (values: readonly T[]) => readonly T[] = identity): readonly (readonly T[])[] { 1635 return arrayFrom(arrayToMultiMap(values, getGroupId).values(), resultSelector); 1636} 1637 1638/** @internal */ 1639export function clone<T>(object: T): T { 1640 const result: any = {}; 1641 for (const id in object) { 1642 if (hasOwnProperty.call(object, id)) { 1643 result[id] = (object as any)[id]; 1644 } 1645 } 1646 return result; 1647} 1648 1649/** 1650 * Creates a new object by adding the own properties of `second`, then the own properties of `first`. 1651 * 1652 * NOTE: This means that if a property exists in both `first` and `second`, the property in `first` will be chosen. 1653 * 1654 * @internal 1655 */ 1656export function extend<T1, T2>(first: T1, second: T2): T1 & T2 { 1657 const result: T1 & T2 = {} as any; 1658 for (const id in second) { 1659 if (hasOwnProperty.call(second, id)) { 1660 (result as any)[id] = (second as any)[id]; 1661 } 1662 } 1663 1664 for (const id in first) { 1665 if (hasOwnProperty.call(first, id)) { 1666 (result as any)[id] = (first as any)[id]; 1667 } 1668 } 1669 1670 return result; 1671} 1672 1673/** @internal */ 1674export function copyProperties<T1 extends T2, T2>(first: T1, second: T2) { 1675 for (const id in second) { 1676 if (hasOwnProperty.call(second, id)) { 1677 (first as any)[id] = second[id]; 1678 } 1679 } 1680} 1681 1682/** @internal */ 1683export function maybeBind<T, A extends any[], R>(obj: T, fn: ((this: T, ...args: A) => R) | undefined): ((...args: A) => R) | undefined { 1684 return fn ? fn.bind(obj) : undefined; 1685} 1686 1687/** @internal */ 1688export interface MultiMap<K, V> extends ESMap<K, V[]> { 1689 /** 1690 * Adds the value to an array of values associated with the key, and returns the array. 1691 * Creates the array if it does not already exist. 1692 */ 1693 add(key: K, value: V): V[]; 1694 /** 1695 * Removes a value from an array of values associated with the key. 1696 * Does not preserve the order of those values. 1697 * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. 1698 */ 1699 remove(key: K, value: V): void; 1700} 1701 1702/** @internal */ 1703export function createMultiMap<K, V>(): MultiMap<K, V>; 1704/** @internal */ 1705export function createMultiMap<V>(): MultiMap<string, V>; 1706/** @internal */ 1707export function createMultiMap<K, V>(): MultiMap<K, V> { 1708 const map = new Map<K, V[]>() as MultiMap<K, V>; 1709 map.add = multiMapAdd; 1710 map.remove = multiMapRemove; 1711 return map; 1712} 1713function multiMapAdd<K, V>(this: MultiMap<K, V>, key: K, value: V) { 1714 let values = this.get(key); 1715 if (values) { 1716 values.push(value); 1717 } 1718 else { 1719 this.set(key, values = [value]); 1720 } 1721 return values; 1722} 1723function multiMapRemove<K, V>(this: MultiMap<K, V>, key: K, value: V) { 1724 const values = this.get(key); 1725 if (values) { 1726 unorderedRemoveItem(values, value); 1727 if (!values.length) { 1728 this.delete(key); 1729 } 1730 } 1731} 1732 1733/** @internal */ 1734export interface UnderscoreEscapedMultiMap<T> extends UnderscoreEscapedMap<T[]> { 1735 /** 1736 * Adds the value to an array of values associated with the key, and returns the array. 1737 * Creates the array if it does not already exist. 1738 */ 1739 add(key: __String, value: T): T[]; 1740 /** 1741 * Removes a value from an array of values associated with the key. 1742 * Does not preserve the order of those values. 1743 * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. 1744 */ 1745 remove(key: __String, value: T): void; 1746} 1747 1748/** @internal */ 1749export function createUnderscoreEscapedMultiMap<T>(): UnderscoreEscapedMultiMap<T> { 1750 return createMultiMap() as UnderscoreEscapedMultiMap<T>; 1751} 1752 1753/** @internal */ 1754export function createQueue<T>(items?: readonly T[]): Queue<T> { 1755 const elements: (T | undefined)[] = items?.slice() || []; 1756 let headIndex = 0; 1757 1758 function isEmpty() { 1759 return headIndex === elements.length; 1760 } 1761 1762 function enqueue(...items: T[]) { 1763 elements.push(...items); 1764 } 1765 1766 function dequeue(): T { 1767 if (isEmpty()) { 1768 throw new Error("Queue is empty"); 1769 } 1770 1771 const result = elements[headIndex] as T; 1772 elements[headIndex] = undefined; // Don't keep referencing dequeued item 1773 headIndex++; 1774 1775 // If more than half of the queue is empty, copy the remaining elements to the 1776 // front and shrink the array (unless we'd be saving fewer than 100 slots) 1777 if (headIndex > 100 && headIndex > (elements.length >> 1)) { 1778 const newLength = elements.length - headIndex; 1779 elements.copyWithin(/*target*/ 0, /*start*/ headIndex); 1780 1781 elements.length = newLength; 1782 headIndex = 0; 1783 } 1784 1785 return result; 1786 } 1787 1788 return { 1789 enqueue, 1790 dequeue, 1791 isEmpty, 1792 }; 1793} 1794 1795/** 1796 * Creates a Set with custom equality and hash code functionality. This is useful when you 1797 * want to use something looser than object identity - e.g. "has the same span". 1798 * 1799 * If `equals(a, b)`, it must be the case that `getHashCode(a) === getHashCode(b)`. 1800 * The converse is not required. 1801 * 1802 * To facilitate a perf optimization (lazy allocation of bucket arrays), `TElement` is 1803 * assumed not to be an array type. 1804 * 1805 * @internal 1806 */ 1807export function createSet<TElement, THash = number>(getHashCode: (element: TElement) => THash, equals: EqualityComparer<TElement>): Set<TElement> { 1808 const multiMap = new Map<THash, TElement | TElement[]>(); 1809 let size = 0; 1810 1811 function getElementIterator(): Iterator<TElement> { 1812 const valueIt = multiMap.values(); 1813 let arrayIt: Iterator<TElement> | undefined; 1814 return { 1815 next: () => { 1816 while (true) { 1817 if (arrayIt) { 1818 const n = arrayIt.next(); 1819 if (!n.done) { 1820 return { value: n.value }; 1821 } 1822 arrayIt = undefined; 1823 } 1824 else { 1825 const n = valueIt.next(); 1826 if (n.done) { 1827 return { value: undefined, done: true }; 1828 } 1829 if (!isArray(n.value)) { 1830 return { value: n.value }; 1831 } 1832 arrayIt = arrayIterator(n.value); 1833 } 1834 } 1835 } 1836 }; 1837 } 1838 1839 const set: Set<TElement> = { 1840 has(element: TElement): boolean { 1841 const hash = getHashCode(element); 1842 if (!multiMap.has(hash)) return false; 1843 const candidates = multiMap.get(hash)!; 1844 if (!isArray(candidates)) return equals(candidates, element); 1845 1846 for (const candidate of candidates) { 1847 if (equals(candidate, element)) { 1848 return true; 1849 } 1850 } 1851 return false; 1852 }, 1853 add(element: TElement): Set<TElement> { 1854 const hash = getHashCode(element); 1855 if (multiMap.has(hash)) { 1856 const values = multiMap.get(hash)!; 1857 if (isArray(values)) { 1858 if (!contains(values, element, equals)) { 1859 values.push(element); 1860 size++; 1861 } 1862 } 1863 else { 1864 const value = values; 1865 if (!equals(value, element)) { 1866 multiMap.set(hash, [ value, element ]); 1867 size++; 1868 } 1869 } 1870 } 1871 else { 1872 multiMap.set(hash, element); 1873 size++; 1874 } 1875 1876 return this; 1877 }, 1878 delete(element: TElement): boolean { 1879 const hash = getHashCode(element); 1880 if (!multiMap.has(hash)) return false; 1881 const candidates = multiMap.get(hash)!; 1882 if (isArray(candidates)) { 1883 for (let i = 0; i < candidates.length; i++) { 1884 if (equals(candidates[i], element)) { 1885 if (candidates.length === 1) { 1886 multiMap.delete(hash); 1887 } 1888 else if (candidates.length === 2) { 1889 multiMap.set(hash, candidates[1 - i]); 1890 } 1891 else { 1892 unorderedRemoveItemAt(candidates, i); 1893 } 1894 size--; 1895 return true; 1896 } 1897 } 1898 } 1899 else { 1900 const candidate = candidates; 1901 if (equals(candidate, element)) { 1902 multiMap.delete(hash); 1903 size--; 1904 return true; 1905 } 1906 } 1907 1908 return false; 1909 }, 1910 clear(): void { 1911 multiMap.clear(); 1912 size = 0; 1913 }, 1914 get size() { 1915 return size; 1916 }, 1917 forEach(action: (value: TElement, key: TElement) => void): void { 1918 for (const elements of arrayFrom(multiMap.values())) { 1919 if (isArray(elements)) { 1920 for (const element of elements) { 1921 action(element, element); 1922 } 1923 } 1924 else { 1925 const element = elements; 1926 action(element, element); 1927 } 1928 } 1929 }, 1930 keys(): Iterator<TElement> { 1931 return getElementIterator(); 1932 }, 1933 values(): Iterator<TElement> { 1934 return getElementIterator(); 1935 }, 1936 entries(): Iterator<[TElement, TElement]> { 1937 const it = getElementIterator(); 1938 return { 1939 next: () => { 1940 const n = it.next(); 1941 return n.done ? n : { value: [ n.value, n.value ] }; 1942 } 1943 }; 1944 }, 1945 }; 1946 1947 return set; 1948} 1949 1950/** 1951 * Tests whether a value is an array. 1952 * 1953 * @internal 1954 */ 1955export function isArray(value: any): value is readonly unknown[] { 1956 return Array.isArray ? Array.isArray(value) : value instanceof Array; 1957} 1958 1959/** @internal */ 1960export function toArray<T>(value: T | T[]): T[]; 1961/** @internal */ 1962export function toArray<T>(value: T | readonly T[]): readonly T[]; 1963/** @internal */ 1964export function toArray<T>(value: T | T[]): T[] { 1965 return isArray(value) ? value : [value]; 1966} 1967 1968/** 1969 * Tests whether a value is string 1970 * 1971 * @internal 1972 */ 1973export function isString(text: unknown): text is string { 1974 return typeof text === "string"; 1975} 1976/** @internal */ 1977export function isNumber(x: unknown): x is number { 1978 return typeof x === "number"; 1979} 1980 1981/** @internal */ 1982export function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined; 1983/** @internal */ 1984export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined; 1985/** @internal */ 1986export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined { 1987 return value !== undefined && test(value) ? value : undefined; 1988} 1989 1990/** @internal */ 1991export function cast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut { 1992 if (value !== undefined && test(value)) return value; 1993 1994 return Debug.fail(`Invalid cast. The supplied value ${value} did not pass the test '${Debug.getFunctionName(test)}'.`); 1995} 1996 1997/** Does nothing. 1998 * 1999 * @internal 2000 */ 2001export function noop(_?: unknown): void { } 2002 2003/** @internal */ 2004export const noopPush: Push<any> = { 2005 push: noop, 2006 length: 0 2007}; 2008 2009/** Do nothing and return false 2010 * 2011 * @internal 2012 */ 2013export function returnFalse(): false { 2014 return false; 2015} 2016 2017/** Do nothing and return true 2018 * 2019 * @internal 2020 */ 2021export function returnTrue(): true { 2022 return true; 2023} 2024 2025/** Do nothing and return undefined 2026 * 2027 * @internal 2028 */ 2029export function returnUndefined(): undefined { 2030 return undefined; 2031} 2032 2033/** Returns its argument. 2034 * 2035 * @internal 2036 */ 2037export function identity<T>(x: T) { 2038 return x; 2039} 2040 2041/** Returns lower case string 2042 * 2043 * @internal 2044 */ 2045export function toLowerCase(x: string) { 2046 return x.toLowerCase(); 2047} 2048 2049// We convert the file names to lower case as key for file name on case insensitive file system 2050// While doing so we need to handle special characters (eg \u0130) to ensure that we dont convert 2051// it to lower case, fileName with its lowercase form can exist along side it. 2052// Handle special characters and make those case sensitive instead 2053// 2054// |-#--|-Unicode--|-Char code-|-Desc-------------------------------------------------------------------| 2055// | 1. | i | 105 | Ascii i | 2056// | 2. | I | 73 | Ascii I | 2057// |-------- Special characters ------------------------------------------------------------------------| 2058// | 3. | \u0130 | 304 | Upper case I with dot above | 2059// | 4. | i,\u0307 | 105,775 | i, followed by 775: Lower case of (3rd item) | 2060// | 5. | I,\u0307 | 73,775 | I, followed by 775: Upper case of (4th item), lower case is (4th item) | 2061// | 6. | \u0131 | 305 | Lower case i without dot, upper case is I (2nd item) | 2062// | 7. | \u00DF | 223 | Lower case sharp s | 2063// 2064// Because item 3 is special where in its lowercase character has its own 2065// upper case form we cant convert its case. 2066// Rest special characters are either already in lower case format or 2067// they have corresponding upper case character so they dont need special handling 2068// 2069// But to avoid having to do string building for most common cases, also ignore 2070// a-z, 0-9, \u0131, \u00DF, \, /, ., : and space 2071const fileNameLowerCaseRegExp = /[^\u0130\u0131\u00DFa-z0-9\\/:\-_\. ]+/g; 2072/** 2073 * Case insensitive file systems have descripencies in how they handle some characters (eg. turkish Upper case I with dot on top - \u0130) 2074 * This function is used in places where we want to make file name as a key on these systems 2075 * It is possible on mac to be able to refer to file name with I with dot on top as a fileName with its lower case form 2076 * But on windows we cannot. Windows can have fileName with I with dot on top next to its lower case and they can not each be referred with the lowercase forms 2077 * Technically we would want this function to be platform sepcific as well but 2078 * our api has till now only taken caseSensitive as the only input and just for some characters we dont want to update API and ensure all customers use those api 2079 * We could use upper case and we would still need to deal with the descripencies but 2080 * we want to continue using lower case since in most cases filenames are lowercasewe and wont need any case changes and avoid having to store another string for the key 2081 * So for this function purpose, we go ahead and assume character I with dot on top it as case sensitive since its very unlikely to use lower case form of that special character 2082 * 2083 * @internal 2084 */ 2085export function toFileNameLowerCase(x: string) { 2086 return fileNameLowerCaseRegExp.test(x) ? 2087 x.replace(fileNameLowerCaseRegExp, toLowerCase) : 2088 x; 2089} 2090 2091/** Throws an error because a function is not implemented. 2092 * 2093 * @internal 2094 */ 2095export function notImplemented(): never { 2096 throw new Error("Not implemented"); 2097} 2098 2099/** @internal */ 2100export function memoize<T>(callback: () => T): () => T { 2101 let value: T; 2102 return () => { 2103 if (callback) { 2104 value = callback(); 2105 callback = undefined!; 2106 } 2107 return value; 2108 }; 2109} 2110 2111/** A version of `memoize` that supports a single primitive argument 2112 * 2113 * @internal 2114 */ 2115export function memoizeOne<A extends string | number | boolean | undefined, T>(callback: (arg: A) => T): (arg: A) => T { 2116 const map = new Map<string, T>(); 2117 return (arg: A) => { 2118 const key = `${typeof arg}:${arg}`; 2119 let value = map.get(key); 2120 if (value === undefined && !map.has(key)) { 2121 value = callback(arg); 2122 map.set(key, value); 2123 } 2124 return value!; 2125 }; 2126} 2127 2128/** 2129 * High-order function, composes functions. Note that functions are composed inside-out; 2130 * for example, `compose(a, b)` is the equivalent of `x => b(a(x))`. 2131 * 2132 * @param args The functions to compose. 2133 * 2134 * @internal 2135 */ 2136export function compose<T>(...args: ((t: T) => T)[]): (t: T) => T; 2137/** @internal */ 2138export function compose<T>(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T { 2139 if (!!e) { 2140 const args: ((t: T) => T)[] = []; 2141 for (let i = 0; i < arguments.length; i++) { 2142 args[i] = arguments[i]; 2143 } 2144 2145 return t => reduceLeft(args, (u, f) => f(u), t); 2146 } 2147 else if (d) { 2148 return t => d(c(b(a(t)))); 2149 } 2150 else if (c) { 2151 return t => c(b(a(t))); 2152 } 2153 else if (b) { 2154 return t => b(a(t)); 2155 } 2156 else if (a) { 2157 return t => a(t); 2158 } 2159 else { 2160 return t => t; 2161 } 2162} 2163 2164/** @internal */ 2165export const enum AssertionLevel { 2166 None = 0, 2167 Normal = 1, 2168 Aggressive = 2, 2169 VeryAggressive = 3, 2170} 2171 2172/** 2173 * Safer version of `Function` which should not be called. 2174 * Every function should be assignable to this, but this should not be assignable to every function. 2175 * 2176 * @internal 2177 */ 2178export type AnyFunction = (...args: never[]) => void; 2179/** @internal */ 2180export type AnyConstructor = new (...args: unknown[]) => unknown; 2181 2182/** @internal */ 2183export function equateValues<T>(a: T, b: T) { 2184 return a === b; 2185} 2186 2187/** 2188 * Compare the equality of two strings using a case-sensitive ordinal comparison. 2189 * 2190 * Case-sensitive comparisons compare both strings one code-point at a time using the integer 2191 * value of each code-point after applying `toUpperCase` to each string. We always map both 2192 * strings to their upper-case form as some unicode characters do not properly round-trip to 2193 * lowercase (such as `ẞ` (German sharp capital s)). 2194 * 2195 * @internal 2196 */ 2197export function equateStringsCaseInsensitive(a: string, b: string) { 2198 return a === b 2199 || a !== undefined 2200 && b !== undefined 2201 && a.toUpperCase() === b.toUpperCase(); 2202} 2203 2204/** 2205 * Compare the equality of two strings using a case-sensitive ordinal comparison. 2206 * 2207 * Case-sensitive comparisons compare both strings one code-point at a time using the 2208 * integer value of each code-point. 2209 * 2210 * @internal 2211 */ 2212export function equateStringsCaseSensitive(a: string, b: string) { 2213 return equateValues(a, b); 2214} 2215 2216function compareComparableValues(a: string | undefined, b: string | undefined): Comparison; 2217function compareComparableValues(a: number | undefined, b: number | undefined): Comparison; 2218function compareComparableValues(a: string | number | undefined, b: string | number | undefined) { 2219 return a === b ? Comparison.EqualTo : 2220 a === undefined ? Comparison.LessThan : 2221 b === undefined ? Comparison.GreaterThan : 2222 a < b ? Comparison.LessThan : 2223 Comparison.GreaterThan; 2224} 2225 2226/** 2227 * Compare two numeric values for their order relative to each other. 2228 * To compare strings, use any of the `compareStrings` functions. 2229 * 2230 * @internal 2231 */ 2232export function compareValues(a: number | undefined, b: number | undefined): Comparison { 2233 return compareComparableValues(a, b); 2234} 2235 2236/** 2237 * Compare two TextSpans, first by `start`, then by `length`. 2238 * 2239 * @internal 2240 */ 2241export function compareTextSpans(a: Partial<TextSpan> | undefined, b: Partial<TextSpan> | undefined): Comparison { 2242 return compareValues(a?.start, b?.start) || compareValues(a?.length, b?.length); 2243} 2244 2245/** @internal */ 2246export function min<T>(items: readonly [T, ...T[]], compare: Comparer<T>): T; 2247/** @internal */ 2248export function min<T>(items: readonly T[], compare: Comparer<T>): T | undefined; 2249/** @internal */ 2250export function min<T>(items: readonly T[], compare: Comparer<T>): T | undefined { 2251 return reduceLeft(items, (x, y) => compare(x, y) === Comparison.LessThan ? x : y); 2252} 2253 2254/** 2255 * Compare two strings using a case-insensitive ordinal comparison. 2256 * 2257 * Ordinal comparisons are based on the difference between the unicode code points of both 2258 * strings. Characters with multiple unicode representations are considered unequal. Ordinal 2259 * comparisons provide predictable ordering, but place "a" after "B". 2260 * 2261 * Case-insensitive comparisons compare both strings one code-point at a time using the integer 2262 * value of each code-point after applying `toUpperCase` to each string. We always map both 2263 * strings to their upper-case form as some unicode characters do not properly round-trip to 2264 * lowercase (such as `ẞ` (German sharp capital s)). 2265 * 2266 * @internal 2267 */ 2268export function compareStringsCaseInsensitive(a: string, b: string) { 2269 if (a === b) return Comparison.EqualTo; 2270 if (a === undefined) return Comparison.LessThan; 2271 if (b === undefined) return Comparison.GreaterThan; 2272 a = a.toUpperCase(); 2273 b = b.toUpperCase(); 2274 return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo; 2275} 2276 2277/** 2278 * Compare two strings using a case-sensitive ordinal comparison. 2279 * 2280 * Ordinal comparisons are based on the difference between the unicode code points of both 2281 * strings. Characters with multiple unicode representations are considered unequal. Ordinal 2282 * comparisons provide predictable ordering, but place "a" after "B". 2283 * 2284 * Case-sensitive comparisons compare both strings one code-point at a time using the integer 2285 * value of each code-point. 2286 * 2287 * @internal 2288 */ 2289export function compareStringsCaseSensitive(a: string | undefined, b: string | undefined): Comparison { 2290 return compareComparableValues(a, b); 2291} 2292 2293/** @internal */ 2294export function getStringComparer(ignoreCase?: boolean) { 2295 return ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive; 2296} 2297 2298/** 2299 * Creates a string comparer for use with string collation in the UI. 2300 */ 2301const createUIStringComparer = (() => { 2302 let defaultComparer: Comparer<string> | undefined; 2303 let enUSComparer: Comparer<string> | undefined; 2304 2305 const stringComparerFactory = getStringComparerFactory(); 2306 return createStringComparer; 2307 2308 function compareWithCallback(a: string | undefined, b: string | undefined, comparer: (a: string, b: string) => number) { 2309 if (a === b) return Comparison.EqualTo; 2310 if (a === undefined) return Comparison.LessThan; 2311 if (b === undefined) return Comparison.GreaterThan; 2312 const value = comparer(a, b); 2313 return value < 0 ? Comparison.LessThan : value > 0 ? Comparison.GreaterThan : Comparison.EqualTo; 2314 } 2315 2316 function createIntlCollatorStringComparer(locale: string | undefined): Comparer<string> { 2317 // Intl.Collator.prototype.compare is bound to the collator. See NOTE in 2318 // http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare 2319 const comparer = new Intl.Collator(locale, { usage: "sort", sensitivity: "variant" }).compare; 2320 return (a, b) => compareWithCallback(a, b, comparer); 2321 } 2322 2323 function createLocaleCompareStringComparer(locale: string | undefined): Comparer<string> { 2324 // if the locale is not the default locale (`undefined`), use the fallback comparer. 2325 if (locale !== undefined) return createFallbackStringComparer(); 2326 2327 return (a, b) => compareWithCallback(a, b, compareStrings); 2328 2329 function compareStrings(a: string, b: string) { 2330 return a.localeCompare(b); 2331 } 2332 } 2333 2334 function createFallbackStringComparer(): Comparer<string> { 2335 // An ordinal comparison puts "A" after "b", but for the UI we want "A" before "b". 2336 // We first sort case insensitively. So "Aaa" will come before "baa". 2337 // Then we sort case sensitively, so "aaa" will come before "Aaa". 2338 // 2339 // For case insensitive comparisons we always map both strings to their 2340 // upper-case form as some unicode characters do not properly round-trip to 2341 // lowercase (such as `ẞ` (German sharp capital s)). 2342 return (a, b) => compareWithCallback(a, b, compareDictionaryOrder); 2343 2344 function compareDictionaryOrder(a: string, b: string) { 2345 return compareStrings(a.toUpperCase(), b.toUpperCase()) || compareStrings(a, b); 2346 } 2347 2348 function compareStrings(a: string, b: string) { 2349 return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo; 2350 } 2351 } 2352 2353 function getStringComparerFactory() { 2354 // If the host supports Intl, we use it for comparisons using the default locale. 2355 if (typeof Intl === "object" && typeof Intl.Collator === "function") { 2356 return createIntlCollatorStringComparer; 2357 } 2358 2359 // If the host does not support Intl, we fall back to localeCompare. 2360 // localeCompare in Node v0.10 is just an ordinal comparison, so don't use it. 2361 if (typeof String.prototype.localeCompare === "function" && 2362 typeof String.prototype.toLocaleUpperCase === "function" && 2363 "a".localeCompare("B") < 0) { 2364 return createLocaleCompareStringComparer; 2365 } 2366 2367 // Otherwise, fall back to ordinal comparison: 2368 return createFallbackStringComparer; 2369 } 2370 2371 function createStringComparer(locale: string | undefined) { 2372 // Hold onto common string comparers. This avoids constantly reallocating comparers during 2373 // tests. 2374 if (locale === undefined) { 2375 return defaultComparer || (defaultComparer = stringComparerFactory(locale)); 2376 } 2377 else if (locale === "en-US") { 2378 return enUSComparer || (enUSComparer = stringComparerFactory(locale)); 2379 } 2380 else { 2381 return stringComparerFactory(locale); 2382 } 2383 } 2384})(); 2385 2386let uiComparerCaseSensitive: Comparer<string> | undefined; 2387let uiLocale: string | undefined; 2388 2389/** @internal */ 2390export function getUILocale() { 2391 return uiLocale; 2392} 2393 2394/** @internal */ 2395export function setUILocale(value: string | undefined) { 2396 if (uiLocale !== value) { 2397 uiLocale = value; 2398 uiComparerCaseSensitive = undefined; 2399 } 2400} 2401 2402/** 2403 * Compare two strings in a using the case-sensitive sort behavior of the UI locale. 2404 * 2405 * Ordering is not predictable between different host locales, but is best for displaying 2406 * ordered data for UI presentation. Characters with multiple unicode representations may 2407 * be considered equal. 2408 * 2409 * Case-sensitive comparisons compare strings that differ in base characters, or 2410 * accents/diacritic marks, or case as unequal. 2411 * 2412 * @internal 2413 */ 2414export function compareStringsCaseSensitiveUI(a: string, b: string) { 2415 const comparer = uiComparerCaseSensitive || (uiComparerCaseSensitive = createUIStringComparer(uiLocale)); 2416 return comparer(a, b); 2417} 2418 2419/** @internal */ 2420export function compareProperties<T extends object, K extends keyof T>(a: T | undefined, b: T | undefined, key: K, comparer: Comparer<T[K]>): Comparison { 2421 return a === b ? Comparison.EqualTo : 2422 a === undefined ? Comparison.LessThan : 2423 b === undefined ? Comparison.GreaterThan : 2424 comparer(a[key], b[key]); 2425} 2426 2427/** True is greater than false. 2428 * 2429 * @internal 2430 */ 2431export function compareBooleans(a: boolean, b: boolean): Comparison { 2432 return compareValues(a ? 1 : 0, b ? 1 : 0); 2433} 2434 2435/** 2436 * Given a name and a list of names that are *not* equal to the name, return a spelling suggestion if there is one that is close enough. 2437 * Names less than length 3 only check for case-insensitive equality. 2438 * 2439 * find the candidate with the smallest Levenshtein distance, 2440 * except for candidates: 2441 * * With no name 2442 * * Whose length differs from the target name by more than 0.34 of the length of the name. 2443 * * Whose levenshtein distance is more than 0.4 of the length of the name 2444 * (0.4 allows 1 substitution/transposition for every 5 characters, 2445 * and 1 insertion/deletion at 3 characters) 2446 * 2447 * @internal 2448 */ 2449export function getSpellingSuggestion<T>(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined { 2450 const maximumLengthDifference = Math.max(2, Math.floor(name.length * 0.34)); 2451 let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result is worse than this, don't bother. 2452 let bestCandidate: T | undefined; 2453 for (const candidate of candidates) { 2454 const candidateName = getName(candidate); 2455 if (candidateName !== undefined && Math.abs(candidateName.length - name.length) <= maximumLengthDifference) { 2456 if (candidateName === name) { 2457 continue; 2458 } 2459 // Only consider candidates less than 3 characters long when they differ by case. 2460 // Otherwise, don't bother, since a user would usually notice differences of a 2-character name. 2461 if (candidateName.length < 3 && candidateName.toLowerCase() !== name.toLowerCase()) { 2462 continue; 2463 } 2464 2465 const distance = levenshteinWithMax(name, candidateName, bestDistance - 0.1); 2466 if (distance === undefined) { 2467 continue; 2468 } 2469 2470 Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined 2471 bestDistance = distance; 2472 bestCandidate = candidate; 2473 } 2474 } 2475 return bestCandidate; 2476} 2477 2478function levenshteinWithMax(s1: string, s2: string, max: number): number | undefined { 2479 let previous = new Array(s2.length + 1); 2480 let current = new Array(s2.length + 1); 2481 /** Represents any value > max. We don't care about the particular value. */ 2482 const big = max + 0.01; 2483 2484 for (let i = 0; i <= s2.length; i++) { 2485 previous[i] = i; 2486 } 2487 2488 for (let i = 1; i <= s1.length; i++) { 2489 const c1 = s1.charCodeAt(i - 1); 2490 const minJ = Math.ceil(i > max ? i - max : 1); 2491 const maxJ = Math.floor(s2.length > max + i ? max + i : s2.length); 2492 current[0] = i; 2493 /** Smallest value of the matrix in the ith column. */ 2494 let colMin = i; 2495 for (let j = 1; j < minJ; j++) { 2496 current[j] = big; 2497 } 2498 for (let j = minJ; j <= maxJ; j++) { 2499 // case difference should be significantly cheaper than other differences 2500 const substitutionDistance = s1[i - 1].toLowerCase() === s2[j-1].toLowerCase() 2501 ? (previous[j - 1] + 0.1) 2502 : (previous[j - 1] + 2); 2503 const dist = c1 === s2.charCodeAt(j - 1) 2504 ? previous[j - 1] 2505 : Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ substitutionDistance); 2506 current[j] = dist; 2507 colMin = Math.min(colMin, dist); 2508 } 2509 for (let j = maxJ + 1; j <= s2.length; j++) { 2510 current[j] = big; 2511 } 2512 if (colMin > max) { 2513 // Give up -- everything in this column is > max and it can't get better in future columns. 2514 return undefined; 2515 } 2516 2517 const temp = previous; 2518 previous = current; 2519 current = temp; 2520 } 2521 2522 const res = previous[s2.length]; 2523 return res > max ? undefined : res; 2524} 2525 2526/** @internal */ 2527export function endsWith(str: string, suffix: string): boolean { 2528 const expectedPos = str.length - suffix.length; 2529 return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos; 2530} 2531 2532/** @internal */ 2533export function removeSuffix(str: string, suffix: string): string { 2534 return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : str; 2535} 2536 2537/** @internal */ 2538export function tryRemoveSuffix(str: string, suffix: string): string | undefined { 2539 return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : undefined; 2540} 2541 2542/** @internal */ 2543export function stringContains(str: string, substring: string): boolean { 2544 return str.indexOf(substring) !== -1; 2545} 2546 2547/** 2548 * Takes a string like "jquery-min.4.2.3" and returns "jquery" 2549 * 2550 * @internal 2551 */ 2552export function removeMinAndVersionNumbers(fileName: string) { 2553 // We used to use the regex /[.-]((min)|(\d+(\.\d+)*))$/ and would just .replace it twice. 2554 // Unfortunately, that regex has O(n^2) performance because v8 doesn't match from the end of the string. 2555 // Instead, we now essentially scan the filename (backwards) ourselves. 2556 2557 let end: number = fileName.length; 2558 2559 for (let pos = end - 1; pos > 0; pos--) { 2560 let ch: number = fileName.charCodeAt(pos); 2561 if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) { 2562 // Match a \d+ segment 2563 do { 2564 --pos; 2565 ch = fileName.charCodeAt(pos); 2566 } while (pos > 0 && ch >= CharacterCodes._0 && ch <= CharacterCodes._9); 2567 } 2568 else if (pos > 4 && (ch === CharacterCodes.n || ch === CharacterCodes.N)) { 2569 // Looking for "min" or "min" 2570 // Already matched the 'n' 2571 --pos; 2572 ch = fileName.charCodeAt(pos); 2573 if (ch !== CharacterCodes.i && ch !== CharacterCodes.I) { 2574 break; 2575 } 2576 --pos; 2577 ch = fileName.charCodeAt(pos); 2578 if (ch !== CharacterCodes.m && ch !== CharacterCodes.M) { 2579 break; 2580 } 2581 --pos; 2582 ch = fileName.charCodeAt(pos); 2583 } 2584 else { 2585 // This character is not part of either suffix pattern 2586 break; 2587 } 2588 2589 if (ch !== CharacterCodes.minus && ch !== CharacterCodes.dot) { 2590 break; 2591 } 2592 2593 end = pos; 2594 } 2595 2596 // end might be fileName.length, in which case this should internally no-op 2597 return end === fileName.length ? fileName : fileName.slice(0, end); 2598} 2599 2600/** Remove an item from an array, moving everything to its right one space left. 2601 * 2602 * @internal 2603 */ 2604export function orderedRemoveItem<T>(array: T[], item: T): boolean { 2605 for (let i = 0; i < array.length; i++) { 2606 if (array[i] === item) { 2607 orderedRemoveItemAt(array, i); 2608 return true; 2609 } 2610 } 2611 return false; 2612} 2613 2614/** Remove an item by index from an array, moving everything to its right one space left. 2615 * 2616 * @internal 2617 */ 2618export function orderedRemoveItemAt<T>(array: T[], index: number): void { 2619 // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`. 2620 for (let i = index; i < array.length - 1; i++) { 2621 array[i] = array[i + 1]; 2622 } 2623 array.pop(); 2624} 2625 2626/** @internal */ 2627export function unorderedRemoveItemAt<T>(array: T[], index: number): void { 2628 // Fill in the "hole" left at `index`. 2629 array[index] = array[array.length - 1]; 2630 array.pop(); 2631} 2632 2633/** Remove the *first* occurrence of `item` from the array. 2634 * 2635 * @internal 2636 */ 2637export function unorderedRemoveItem<T>(array: T[], item: T) { 2638 return unorderedRemoveFirstItemWhere(array, element => element === item); 2639} 2640 2641/** Remove the *first* element satisfying `predicate`. */ 2642function unorderedRemoveFirstItemWhere<T>(array: T[], predicate: (element: T) => boolean) { 2643 for (let i = 0; i < array.length; i++) { 2644 if (predicate(array[i])) { 2645 unorderedRemoveItemAt(array, i); 2646 return true; 2647 } 2648 } 2649 return false; 2650} 2651 2652/** @internal */ 2653export type GetCanonicalFileName = (fileName: string) => string; 2654/** @internal */ 2655export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): GetCanonicalFileName { 2656 return useCaseSensitiveFileNames ? identity : toFileNameLowerCase; 2657} 2658 2659/** Represents a "prefix*suffix" pattern. 2660 * 2661 * @internal 2662 */ 2663export interface Pattern { 2664 prefix: string; 2665 suffix: string; 2666} 2667 2668/** @internal */ 2669export function patternText({ prefix, suffix }: Pattern): string { 2670 return `${prefix}*${suffix}`; 2671} 2672 2673/** 2674 * Given that candidate matches pattern, returns the text matching the '*'. 2675 * E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar" 2676 * 2677 * @internal 2678 */ 2679export function matchedText(pattern: Pattern, candidate: string): string { 2680 Debug.assert(isPatternMatch(pattern, candidate)); 2681 return candidate.substring(pattern.prefix.length, candidate.length - pattern.suffix.length); 2682} 2683 2684/** Return the object corresponding to the best pattern to match `candidate`. 2685 * 2686 * @internal 2687 */ 2688export function findBestPatternMatch<T>(values: readonly T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { 2689 let matchedValue: T | undefined; 2690 // use length of prefix as betterness criteria 2691 let longestMatchPrefixLength = -1; 2692 2693 for (const v of values) { 2694 const pattern = getPattern(v); 2695 if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) { 2696 longestMatchPrefixLength = pattern.prefix.length; 2697 matchedValue = v; 2698 } 2699 } 2700 2701 return matchedValue; 2702} 2703 2704/** @internal */ 2705export function startsWith(str: string, prefix: string): boolean { 2706 return str.lastIndexOf(prefix, 0) === 0; 2707} 2708 2709/** @internal */ 2710export function removePrefix(str: string, prefix: string): string { 2711 return startsWith(str, prefix) ? str.substr(prefix.length) : str; 2712} 2713 2714/** @internal */ 2715export function tryRemovePrefix(str: string, prefix: string, getCanonicalFileName: GetCanonicalFileName = identity): string | undefined { 2716 return startsWith(getCanonicalFileName(str), getCanonicalFileName(prefix)) ? str.substring(prefix.length) : undefined; 2717} 2718 2719/** @internal */ 2720export function isPatternMatch({ prefix, suffix }: Pattern, candidate: string) { 2721 return candidate.length >= prefix.length + suffix.length && 2722 startsWith(candidate, prefix) && 2723 endsWith(candidate, suffix); 2724} 2725 2726/** @internal */ 2727export function and<T>(f: (arg: T) => boolean, g: (arg: T) => boolean) { 2728 return (arg: T) => f(arg) && g(arg); 2729} 2730 2731/** @internal */ 2732export function or<P, R1 extends P, R2 extends P>(f1: (p1: P) => p1 is R1, f2: (p2: P) => p2 is R2): (p: P) => p is R1 | R2; 2733/** @internal */ 2734export function or<P, R1 extends P, R2 extends P, R3 extends P>(f1: (p1: P) => p1 is R1, f2: (p2: P) => p2 is R2, f3: (p3: P) => p3 is R3): (p: P) => p is R1 | R2 | R3; 2735/** @internal */ 2736export function or<T extends unknown[], U>(...fs: ((...args: T) => U)[]): (...args: T) => U; 2737/** @internal */ 2738export function or<T extends unknown[], U>(...fs: ((...args: T) => U)[]): (...args: T) => U { 2739 return (...args) => { 2740 let lastResult: U; 2741 for (const f of fs) { 2742 lastResult = f(...args); 2743 if (lastResult) { 2744 return lastResult; 2745 } 2746 } 2747 return lastResult!; 2748 }; 2749} 2750 2751/** @internal */ 2752export function not<T extends unknown[]>(fn: (...args: T) => boolean): (...args: T) => boolean { 2753 return (...args) => !fn(...args); 2754} 2755 2756/** @internal */ 2757export function assertType<T>(_: T): void { } 2758 2759/** @internal */ 2760export function singleElementArray<T>(t: T | undefined): T[] | undefined { 2761 return t === undefined ? undefined : [t]; 2762} 2763 2764/** @internal */ 2765export function enumerateInsertsAndDeletes<T, U>(newItems: readonly T[], oldItems: readonly U[], comparer: (a: T, b: U) => Comparison, inserted: (newItem: T) => void, deleted: (oldItem: U) => void, unchanged?: (oldItem: U, newItem: T) => void) { 2766 unchanged = unchanged || noop; 2767 let newIndex = 0; 2768 let oldIndex = 0; 2769 const newLen = newItems.length; 2770 const oldLen = oldItems.length; 2771 let hasChanges = false; 2772 while (newIndex < newLen && oldIndex < oldLen) { 2773 const newItem = newItems[newIndex]; 2774 const oldItem = oldItems[oldIndex]; 2775 const compareResult = comparer(newItem, oldItem); 2776 if (compareResult === Comparison.LessThan) { 2777 inserted(newItem); 2778 newIndex++; 2779 hasChanges = true; 2780 } 2781 else if (compareResult === Comparison.GreaterThan) { 2782 deleted(oldItem); 2783 oldIndex++; 2784 hasChanges = true; 2785 } 2786 else { 2787 unchanged(oldItem, newItem); 2788 newIndex++; 2789 oldIndex++; 2790 } 2791 } 2792 while (newIndex < newLen) { 2793 inserted(newItems[newIndex++]); 2794 hasChanges = true; 2795 } 2796 while (oldIndex < oldLen) { 2797 deleted(oldItems[oldIndex++]); 2798 hasChanges = true; 2799 } 2800 return hasChanges; 2801} 2802 2803/** @internal */ 2804export function fill<T>(length: number, cb: (index: number) => T): T[] { 2805 const result = Array<T>(length); 2806 for (let i = 0; i < length; i++) { 2807 result[i] = cb(i); 2808 } 2809 return result; 2810} 2811 2812/** @internal */ 2813export function cartesianProduct<T>(arrays: readonly T[][]) { 2814 const result: T[][] = []; 2815 cartesianProductWorker(arrays, result, /*outer*/ undefined, 0); 2816 return result; 2817} 2818 2819function cartesianProductWorker<T>(arrays: readonly (readonly T[])[], result: (readonly T[])[], outer: readonly T[] | undefined, index: number) { 2820 for (const element of arrays[index]) { 2821 let inner: T[]; 2822 if (outer) { 2823 inner = outer.slice(); 2824 inner.push(element); 2825 } 2826 else { 2827 inner = [element]; 2828 } 2829 if (index === arrays.length - 1) { 2830 result.push(inner); 2831 } 2832 else { 2833 cartesianProductWorker(arrays, result, inner, index + 1); 2834 } 2835 } 2836} 2837 2838 2839/** 2840 * Returns string left-padded with spaces or zeros until it reaches the given length. 2841 * 2842 * @param s String to pad. 2843 * @param length Final padded length. If less than or equal to 's.length', returns 's' unchanged. 2844 * @param padString Character to use as padding (default " "). 2845 * 2846 * @internal 2847 */ 2848export function padLeft(s: string, length: number, padString: " " | "0" = " ") { 2849 return length <= s.length ? s : padString.repeat(length - s.length) + s; 2850} 2851 2852/** 2853 * Returns string right-padded with spaces until it reaches the given length. 2854 * 2855 * @param s String to pad. 2856 * @param length Final padded length. If less than or equal to 's.length', returns 's' unchanged. 2857 * @param padString Character to use as padding (default " "). 2858 * 2859 * @internal 2860 */ 2861export function padRight(s: string, length: number, padString: " " = " ") { 2862 return length <= s.length ? s : s + padString.repeat(length - s.length); 2863} 2864 2865/** @internal */ 2866export function takeWhile<T, U extends T>(array: readonly T[], predicate: (element: T) => element is U): U[]; 2867/** @internal */ 2868export function takeWhile<T>(array: readonly T[], predicate: (element: T) => boolean): T[] { 2869 const len = array.length; 2870 let index = 0; 2871 while (index < len && predicate(array[index])) { 2872 index++; 2873 } 2874 return array.slice(0, index); 2875} 2876 2877/** 2878 * Removes the leading and trailing white space and line terminator characters from a string. 2879 * 2880 * @internal 2881 */ 2882export const trimString = !!String.prototype.trim ? ((s: string) => s.trim()) : (s: string) => trimStringEnd(trimStringStart(s)); 2883 2884/** 2885 * Returns a copy with trailing whitespace removed. 2886 * 2887 * @internal 2888 */ 2889export const trimStringEnd = !!String.prototype.trimEnd ? ((s: string) => s.trimEnd()) : trimEndImpl; 2890 2891/** 2892 * Returns a copy with leading whitespace removed. 2893 * 2894 * @internal 2895 */ 2896export const trimStringStart = !!String.prototype.trimStart ? ((s: string) => s.trimStart()) : (s: string) => s.replace(/^\s+/g, ""); 2897 2898/** 2899 * https://jsbench.me/gjkoxld4au/1 2900 * The simple regex for this, /\s+$/g is O(n^2) in v8. 2901 * The native .trimEnd method is by far best, but since that's technically ES2019, 2902 * we provide a (still much faster than the simple regex) fallback. 2903 */ 2904function trimEndImpl(s: string) { 2905 let end = s.length - 1; 2906 while (end >= 0) { 2907 if (!isWhiteSpaceLike(s.charCodeAt(end))) break; 2908 end--; 2909 } 2910 return s.slice(0, end + 1); 2911} 2912 2913declare const process: any; 2914 2915/** @internal */ 2916export function isNodeLikeSystem(): boolean { 2917 // This is defined here rather than in sys.ts to prevent a cycle from its 2918 // use in performanceCore.ts. 2919 return typeof process !== "undefined" 2920 && process.nextTick 2921 && !process.browser 2922 && typeof require !== "undefined"; 2923} 2924