codekingpro/portable-devtools
115k
1export function longestCommonPrefix(str1, str2) {
2 let i;
3 for (i = 0; i < str1.length && i < str2.length; i++) {
4 if (str1[i] != str2[i]) {
5 return str1.slice(0, i);
6 }
7 }
8 return str1.slice(0, i);
9}
10export function longestCommonSuffix(str1, str2) {
11 let i;
12 // Unlike longestCommonPrefix, we need a special case to handle all scenarios
13 // where we return the empty string since str1.slice(-0) will return the
14 // entire string.
15 if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) {
16 return '';
17 }
18 for (i = 0; i < str1.length && i < str2.length; i++) {
19 if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) {
20 return str1.slice(-i);
21 }
22 }
23 return str1.slice(-i);
24}
25export function replacePrefix(string, oldPrefix, newPrefix) {
26 if (string.slice(0, oldPrefix.length) != oldPrefix) {
27 throw Error(`string ${JSON.stringify(string)} doesn't start with prefix ${JSON.stringify(oldPrefix)}; this is a bug`);
28 }
29 return newPrefix + string.slice(oldPrefix.length);
30}
31export function replaceSuffix(string, oldSuffix, newSuffix) {
32 if (!oldSuffix) {
33 return string + newSuffix;
34 }
35 if (string.slice(-oldSuffix.length) != oldSuffix) {
36 throw Error(`string ${JSON.stringify(string)} doesn't end with suffix ${JSON.stringify(oldSuffix)}; this is a bug`);
37 }
38 return string.slice(0, -oldSuffix.length) + newSuffix;
39}
40export function removePrefix(string, oldPrefix) {
41 return replacePrefix(string, oldPrefix, '');
42}
43export function removeSuffix(string, oldSuffix) {
44 return replaceSuffix(string, oldSuffix, '');
45}
46export function maximumOverlap(string1, string2) {
47 return string2.slice(0, overlapCount(string1, string2));
48}
49// Nicked from https://stackoverflow.com/a/60422853/1709587
50function overlapCount(a, b) {
51 // Deal with cases where the strings differ in length
52 let startA = 0;
53 if (a.length > b.length) {
54 startA = a.length - b.length;
55 }
56 let endB = b.length;
57 if (a.length < b.length) {
58 endB = a.length;
59 }
60 // Create a back-reference for each index
61 // that should be followed in case of a mismatch.
62 // We only need B to make these references:
63 const map = Array(endB);
64 let k = 0; // Index that lags behind j
65 map[0] = 0;
66 for (let j = 1; j < endB; j++) {
67 if (b[j] == b[k]) {
68 map[j] = map[k]; // skip over the same character (optional optimisation)
69 }
70 else {
71 map[j] = k;
72 }
73 while (k > 0 && b[j] != b[k]) {
74 k = map[k];
75 }
76 if (b[j] == b[k]) {
77 k++;
78 }
79 }
80 // Phase 2: use these references while iterating over A
81 k = 0;
82 for (let i = startA; i < a.length; i++) {
83 while (k > 0 && a[i] != b[k]) {
84 k = map[k];
85 }
86 if (a[i] == b[k]) {
87 k++;
88 }
89 }
90 return k;
91}
92/**
93 * Returns true if the string consistently uses Windows line endings.
94 */
95export function hasOnlyWinLineEndings(string) {
96 return string.includes('\r\n') && !string.startsWith('\n') && !string.match(/[^\r]\n/);
97}
98/**
99 * Returns true if the string consistently uses Unix line endings.
100 */
101export function hasOnlyUnixLineEndings(string) {
102 return !string.includes('\r\n') && string.includes('\n');
103}
104export function trailingWs(string) {
105 // Yes, this looks overcomplicated and dumb - why not replace the whole function with
106 // return string.match(/\s*$/)[0]
107 // you ask? Because:
108 // 1. the trap described at https://markamery.com/blog/quadratic-time-regexes/ would mean doing
109 // this would cause this function to take O(n²) time in the worst case (specifically when
110 // there is a massive run of NON-TRAILING whitespace in `string`), and
111 // 2. the fix proposed in the same blog post, of using a negative lookbehind, is incompatible
112 // with old Safari versions that we'd like to not break if possible (see
113 // https://github.com/kpdecker/jsdiff/pull/550)
114 // It feels absurd to do this with an explicit loop instead of a regex, but I really can't see a
115 // better way that doesn't result in broken behaviour.
116 let i;
117 for (i = string.length - 1; i >= 0; i--) {
118 if (!string[i].match(/\s/)) {
119 break;
120 }
121 }
122 return string.substring(i + 1);
123}
124export function leadingWs(string) {
125 // Thankfully the annoying considerations described in trailingWs don't apply here:
126 const match = string.match(/^\s*/);
127 return match ? match[0] : '';
128}
129 