mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 21:17:47 +00:00
Add ability to receive string array, treat as one string for matching, but return same string array decorated
This commit is contained in:
parent
fdf1eae882
commit
7ddafdd45f
@ -510,13 +510,10 @@ export interface FuzzyScorer {
|
||||
): FuzzyScore | undefined;
|
||||
}
|
||||
|
||||
export function createMatches(score: undefined | FuzzyScore): Match[] {
|
||||
if (typeof score === "undefined") {
|
||||
return [];
|
||||
}
|
||||
function _createMatches(score: FuzzyScore, wordPos: number) {
|
||||
const res: Match[] = [];
|
||||
const wordPos = score[1];
|
||||
for (let i = score.length - 1; i > 1; i--) {
|
||||
|
||||
for (let i = score.length - 1; i >= 0; i--) {
|
||||
const pos = score[i] + wordPos;
|
||||
const last = res[res.length - 1];
|
||||
if (last && last.end === pos) {
|
||||
@ -525,9 +522,64 @@ export function createMatches(score: undefined | FuzzyScore): Match[] {
|
||||
res.push({ start: pos, end: pos + 1 });
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export function createMatches(score: undefined | FuzzyScore): Match[] {
|
||||
if (typeof score === "undefined") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const wordPos = score[1];
|
||||
const _score = score.splice(2);
|
||||
|
||||
return _createMatches(_score, wordPos);
|
||||
}
|
||||
|
||||
// The first and second elements in score represent total score, and the offset at which
|
||||
// matching started. For this method, we only care about match positions, not the score
|
||||
// or offset.
|
||||
const findFirstOutOfRangeElement = (number, score: FuzzyScore) =>
|
||||
score.findIndex((num) => num < number);
|
||||
|
||||
export function createMatchesFragmented(
|
||||
score: undefined | FuzzyScore,
|
||||
strings: string[]
|
||||
): Match[][] {
|
||||
if (typeof score === "undefined") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const matches: Match[][] = [];
|
||||
const wordPos = score[1];
|
||||
let lengthCounter = 0;
|
||||
const _score = score.splice(2);
|
||||
const fragmentedScores: FuzzyScore[] = [];
|
||||
|
||||
for (const string of strings) {
|
||||
const prevLengthCounter = lengthCounter;
|
||||
lengthCounter += string.length;
|
||||
const lastIndex = findFirstOutOfRangeElement(lengthCounter, _score);
|
||||
|
||||
if (lastIndex < 0) {
|
||||
fragmentedScores.push([]);
|
||||
continue;
|
||||
}
|
||||
|
||||
fragmentedScores.push(
|
||||
_score.splice(lastIndex).map((pos) => pos - prevLengthCounter)
|
||||
);
|
||||
}
|
||||
|
||||
for (const fragmentedScore of fragmentedScores) {
|
||||
const res = _createMatches(fragmentedScore, wordPos);
|
||||
matches.push(res);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* A fast function (therefore imprecise) to check if code points are emojis.
|
||||
* Generated using https://github.com/alexdima/unicode-utils/blob/master/generate-emoji-test.js
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { createMatches, FuzzyScore, fuzzyScore } from "./filter";
|
||||
import {
|
||||
createMatches,
|
||||
createMatchesFragmented,
|
||||
FuzzyScore,
|
||||
fuzzyScore,
|
||||
} from "./filter";
|
||||
|
||||
/**
|
||||
* Determine whether a sequence of letters exists in another string,
|
||||
@ -23,8 +28,11 @@ export const fuzzySequentialMatch: FuzzySequentialMatcher = (
|
||||
) => {
|
||||
let topScore = Number.NEGATIVE_INFINITY;
|
||||
const decoratedStrings: string[][] = [];
|
||||
const strings = item.treatArrayAsSingleString
|
||||
? [item.strings.join("")]
|
||||
: item.strings;
|
||||
|
||||
for (const word of item.strings) {
|
||||
for (const word of strings) {
|
||||
const scores = fuzzyScore(
|
||||
filter,
|
||||
filter.toLowerCase(),
|
||||
@ -36,7 +44,7 @@ export const fuzzySequentialMatch: FuzzySequentialMatcher = (
|
||||
);
|
||||
|
||||
if (decorate) {
|
||||
decoratedStrings.push(decorate(word, scores));
|
||||
decoratedStrings.push(decorate(word, item, scores));
|
||||
}
|
||||
|
||||
if (!scores) {
|
||||
@ -81,6 +89,7 @@ export interface ScorableTextItem {
|
||||
score?: number;
|
||||
strings: string[];
|
||||
decoratedStrings?: string[][];
|
||||
treatArrayAsSingleString?: boolean;
|
||||
}
|
||||
|
||||
type FuzzyFilterSort = <T extends ScorableTextItem>(
|
||||
@ -109,43 +118,49 @@ export const fuzzyFilterSort: FuzzyFilterSort = (
|
||||
);
|
||||
};
|
||||
|
||||
type MatchDecorator = (word: string, scores?: FuzzyScore) => string[];
|
||||
type MatchDecorator = (
|
||||
word: string,
|
||||
item: ScorableTextItem,
|
||||
scores?: FuzzyScore
|
||||
) => string[];
|
||||
export const createMatchDecorator: (
|
||||
left: string,
|
||||
right: string
|
||||
) => MatchDecorator = (left, right) => (word, scores) =>
|
||||
_decorateMatch(word, [left, right], scores);
|
||||
) => MatchDecorator = (left, right) => (word, item, scores) =>
|
||||
_decorateMatch(word, [left, right], item, scores);
|
||||
|
||||
const _decorateMatch: (
|
||||
word: string,
|
||||
surroundWith: [string, string],
|
||||
item: ScorableTextItem,
|
||||
scores?: FuzzyScore
|
||||
) => string[] = (word, surroundWith, scores) => {
|
||||
) => string[] = (word, surroundWith, item, scores) => {
|
||||
if (!scores) {
|
||||
return [word];
|
||||
}
|
||||
|
||||
const decoratedText: string[] = [];
|
||||
const matches = createMatches(scores);
|
||||
const matches = item.treatArrayAsSingleString
|
||||
? createMatchesFragmented(scores, item.strings)
|
||||
: [createMatches(scores)];
|
||||
const [left, right] = surroundWith;
|
||||
let pos = 0;
|
||||
|
||||
let actualWord = "";
|
||||
for (const match of matches) {
|
||||
actualWord +=
|
||||
word.substring(pos, match.start) +
|
||||
left +
|
||||
word.substring(match.start, match.end) +
|
||||
right;
|
||||
pos = match.end;
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
const match = matches[i];
|
||||
const _word = item.treatArrayAsSingleString ? item.strings[i] : word;
|
||||
let pos = 0;
|
||||
let actualWord = "";
|
||||
for (const fragmentedMatch of match) {
|
||||
actualWord +=
|
||||
_word.substring(pos, fragmentedMatch.start) +
|
||||
left +
|
||||
_word.substring(fragmentedMatch.start, fragmentedMatch.end) +
|
||||
right;
|
||||
pos = fragmentedMatch.end;
|
||||
}
|
||||
actualWord += _word.substring(pos);
|
||||
|
||||
decoratedText.push(actualWord);
|
||||
}
|
||||
actualWord += word.substring(pos);
|
||||
|
||||
const fragments = actualWord.split("::");
|
||||
|
||||
for (const fragment of fragments) {
|
||||
decoratedText.push(fragment);
|
||||
}
|
||||
|
||||
return decoratedText;
|
||||
};
|
||||
|
@ -476,7 +476,8 @@ export class QuickBar extends LitElement {
|
||||
return {
|
||||
...commandItem,
|
||||
categoryKey: "reload",
|
||||
strings: [`${commandItem.categoryText}::${commandItem.primaryText}`],
|
||||
strings: [`${commandItem.categoryText} `, commandItem.primaryText],
|
||||
treatArrayAsSingleString: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -510,7 +511,8 @@ export class QuickBar extends LitElement {
|
||||
return this._generateConfirmationCommand(
|
||||
{
|
||||
...item,
|
||||
strings: [`${item.categoryText}::${item.primaryText}`],
|
||||
strings: [`${item.categoryText} `, item.primaryText],
|
||||
treatArrayAsSingleString: true,
|
||||
},
|
||||
this.hass.localize("ui.dialogs.generic.ok")
|
||||
);
|
||||
@ -611,7 +613,8 @@ export class QuickBar extends LitElement {
|
||||
|
||||
return {
|
||||
...navItem,
|
||||
strings: [`${navItem.categoryText}::${navItem.primaryText}`],
|
||||
strings: [`${navItem.categoryText} `, navItem.primaryText],
|
||||
treatArrayAsSingleString: true,
|
||||
categoryKey,
|
||||
};
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user