337
338
export function matchesWords(word: string, target: string, contiguous: boolean = false): IMatch[] | null {
339
>
if (!target || target.length === 0) {
filters.ts
340
return null;
341
}
343
>
let result: IMatch[] | null = null;
344
>
let targetIndex = 0;
345
>
346
>
word = tryNormalizeToBase(word);
347
>
target = tryNormalizeToBase(target);
348
>
// Memoize recursive calls within a single top-level invocation. Because word
349
>
// separators are treated as an equivalence class by `charactersMatch`, the
350
>
// recursion in `_matchesWords` can otherwise explode exponentially for inputs
351
>
// like `editor.action` against targets that contain many separators.
352
>
const memo = new Map<number, IMatch[] | null>();
353
>
while (targetIndex < target.length) {
354
>
result = _matchesWords(word, target, 0, targetIndex, contiguous, memo);
355
>
if (result !== null) {
356
>
break;
357
>
}
358
>
targetIndex = nextWord(target, targetIndex + 1);
359
>
}
360
>
361
>
return result;
362
>
}
363
364
>
function cloneMatches(matches: IMatch[] | null): IMatch[] | null {
filters.ts
365
>
if (matches === null) {
366
>
return null;
367
>
}
368
>
const result: IMatch[] = [];
369
>
for (const m of matches) {
370
>
result.push({ start: m.start, end: m.end });
371
>
}
372
>
return result;
373
>
}
374
375
>
function _matchesWords(word: string, target: string, wordIndex: number, targetIndex: number, contiguous: boolean, memo: Map<number, IMatch[] | null>): IMatch[] | null {
filters.ts
376
>
if (wordIndex === word.length) {
377
>
return [];
378
>
} else if (targetIndex === target.length) {
379
return null;
380
}
382
>
const memoKey = wordIndex * (target.length + 1) + targetIndex;
383
>
const cached = memo.get(memoKey);
384
>
if (cached !== undefined) {
385
>
// Caller (`join`) mutates the returned array, so always return a clone.
386
>
return cloneMatches(cached);
387
>
}
388
>
389
>
const computed = _matchesWordsCompute(word, target, wordIndex, targetIndex, contiguous, memo);
390
>
memo.set(memoKey, cloneMatches(computed));
391
>
return computed;
392
>
}
393
394
>
function _matchesWordsCompute(word: string, target: string, wordIndex: number, targetIndex: number, contiguous: boolean, memo: Map<number, IMatch[] | null>): IMatch[] | null {
filters.ts
395
>
let targetIndexOffset = 0;
396
>
397
>
if (!charactersMatch(word.charCodeAt(wordIndex), target.charCodeAt(targetIndex))) {
398
>
// Verify alternate characters before exiting
399
>
const altChars = getAlternateCodes(word.charCodeAt(wordIndex));
400
>
if (!altChars) {
401
>
return null;
402
>
}
403
for (let k = 0; k < altChars.length; k++) {
404
if (!charactersMatch(altChars[k], target.charCodeAt(targetIndex + k))) {