comparers.js 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { Lazy } from './lazy.js';
  6. // When comparing large numbers of strings it's better for performance to create an
  7. // Intl.Collator object and use the function provided by its compare property
  8. // than it is to use String.prototype.localeCompare()
  9. // A collator with numeric sorting enabled, and no sensitivity to case, accents or diacritics.
  10. const intlFileNameCollatorBaseNumeric = new Lazy(() => {
  11. const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
  12. return {
  13. collator,
  14. collatorIsNumeric: collator.resolvedOptions().numeric
  15. };
  16. });
  17. // A collator with numeric sorting enabled.
  18. const intlFileNameCollatorNumeric = new Lazy(() => {
  19. const collator = new Intl.Collator(undefined, { numeric: true });
  20. return {
  21. collator
  22. };
  23. });
  24. // A collator with numeric sorting enabled, and sensitivity to accents and diacritics but not case.
  25. const intlFileNameCollatorNumericCaseInsensitive = new Lazy(() => {
  26. const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'accent' });
  27. return {
  28. collator
  29. };
  30. });
  31. /** Compares filenames without distinguishing the name from the extension. Disambiguates by unicode comparison. */
  32. export function compareFileNames(one, other, caseSensitive = false) {
  33. const a = one || '';
  34. const b = other || '';
  35. const result = intlFileNameCollatorBaseNumeric.value.collator.compare(a, b);
  36. // Using the numeric option will make compare(`foo1`, `foo01`) === 0. Disambiguate.
  37. if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && result === 0 && a !== b) {
  38. return a < b ? -1 : 1;
  39. }
  40. return result;
  41. }
  42. export function compareAnything(one, other, lookFor) {
  43. const elementAName = one.toLowerCase();
  44. const elementBName = other.toLowerCase();
  45. // Sort prefix matches over non prefix matches
  46. const prefixCompare = compareByPrefix(one, other, lookFor);
  47. if (prefixCompare) {
  48. return prefixCompare;
  49. }
  50. // Sort suffix matches over non suffix matches
  51. const elementASuffixMatch = elementAName.endsWith(lookFor);
  52. const elementBSuffixMatch = elementBName.endsWith(lookFor);
  53. if (elementASuffixMatch !== elementBSuffixMatch) {
  54. return elementASuffixMatch ? -1 : 1;
  55. }
  56. // Understand file names
  57. const r = compareFileNames(elementAName, elementBName);
  58. if (r !== 0) {
  59. return r;
  60. }
  61. // Compare by name
  62. return elementAName.localeCompare(elementBName);
  63. }
  64. export function compareByPrefix(one, other, lookFor) {
  65. const elementAName = one.toLowerCase();
  66. const elementBName = other.toLowerCase();
  67. // Sort prefix matches over non prefix matches
  68. const elementAPrefixMatch = elementAName.startsWith(lookFor);
  69. const elementBPrefixMatch = elementBName.startsWith(lookFor);
  70. if (elementAPrefixMatch !== elementBPrefixMatch) {
  71. return elementAPrefixMatch ? -1 : 1;
  72. }
  73. // Same prefix: Sort shorter matches to the top to have those on top that match more precisely
  74. else if (elementAPrefixMatch && elementBPrefixMatch) {
  75. if (elementAName.length < elementBName.length) {
  76. return -1;
  77. }
  78. if (elementAName.length > elementBName.length) {
  79. return 1;
  80. }
  81. }
  82. return 0;
  83. }