refactor serach index generator code (#15772)

* refactor: flatten search index

* chore: use Function() to simplify parsing attribute

* chore: remove comment handling

* chore: simplify processing SearchLabel and SearchKeyword element

* chore: use SearchLabel in mutedUsers

* chore: small improvements

* chore: remove a fallback path and simplify the entire code

* fix: result path is not correct

* chore: inline function
This commit is contained in:
anatawa12 2025-04-07 14:35:32 +09:00 committed by GitHub
parent 2349a5d20e
commit 85a7b10fcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 380 additions and 1160 deletions

File diff suppressed because it is too large Load diff

View file

@ -93,11 +93,11 @@ export type SuperMenuDef = {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { useTemplateRef, ref, watch, nextTick } from 'vue'; import { useTemplateRef, ref, watch, nextTick, computed } from 'vue';
import { getScrollContainer } from '@@/js/scroll.js';
import type { SearchIndexItem } from '@/utility/settings-search-index.js'; import type { SearchIndexItem } from '@/utility/settings-search-index.js';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { getScrollContainer } from '@@/js/scroll.js';
import { useRouter } from '@/router.js'; import { useRouter } from '@/router.js';
import { initIntlString, compareStringIncludes } from '@/utility/intl-string.js'; import { initIntlString, compareStringIncludes } from '@/utility/intl-string.js';
@ -124,6 +124,7 @@ const searchResult = ref<{
isRoot: boolean; isRoot: boolean;
parentLabels: string[]; parentLabels: string[];
}[]>([]); }[]>([]);
const searchIndexItemByIdComputed = computed(() => props.searchIndex && new Map<string, SearchIndexItem>(props.searchIndex.map(i => [i.id, i])));
watch(searchQuery, (value) => { watch(searchQuery, (value) => {
rawSearchQuery.value = value; rawSearchQuery.value = value;
@ -137,32 +138,41 @@ watch(rawSearchQuery, (value) => {
return; return;
} }
const dive = (items: SearchIndexItem[], parents: SearchIndexItem[] = []) => { const searchIndexItemById = searchIndexItemByIdComputed.value;
for (const item of items) { if (searchIndexItemById != null) {
const matched = ( const addSearchResult = (item: SearchIndexItem) => {
compareStringIncludes(item.label, value) || let path: string | undefined = item.path;
item.keywords.some((x) => compareStringIncludes(x, value)) let icon: string | undefined = item.icon;
); const parentLabels: string[] = [];
for (let current = searchIndexItemById.get(item.parentId ?? '');
current != null;
current = searchIndexItemById.get(current.parentId ?? '')) {
path ??= current.path;
icon ??= current.icon;
parentLabels.push(current.label);
}
if (_DEV_ && path == null) throw new Error('path is null for ' + item.id);
if (matched) {
searchResult.value.push({ searchResult.value.push({
id: item.id, id: item.id,
path: item.path ?? parents.find((x) => x.path != null)?.path ?? '/', // never gets `/` path: path ?? '/', // never gets `/`
label: item.label, label: item.label,
parentLabels: parents.map((x) => x.label).toReversed(), parentLabels: parentLabels.toReversed(),
icon: item.icon ?? parents.find((x) => x.icon != null)?.icon, icon,
isRoot: parents.length === 0, isRoot: item.parentId == null,
}); });
}
if (item.children) {
dive(item.children, [item, ...parents]);
}
}
}; };
if (props.searchIndex) { for (const item of searchIndexItemById.values()) {
dive(props.searchIndex); if (
compareStringIncludes(item.label, value) ||
item.keywords.some((x) => compareStringIncludes(x, value))
) {
addSearchResult(item);
}
}
} }
}); });

View file

@ -62,12 +62,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</SearchMarker> </SearchMarker>
<SearchMarker <SearchMarker
:label="`${i18n.ts.mutedUsers} (${ i18n.ts.renote })`"
:keywords="['renote', 'mute', 'hide', 'user']" :keywords="['renote', 'mute', 'hide', 'user']"
> >
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-repeat-off"></i></template> <template #icon><i class="ti ti-repeat-off"></i></template>
<template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template> <template #label><SearchLabel>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</SearchLabel></template>
<MkPagination :pagination="renoteMutingPagination"> <MkPagination :pagination="renoteMutingPagination">
<template #empty> <template #empty>

View file

@ -8,35 +8,27 @@ import type { GeneratedSearchIndexItem } from 'search-index:settings';
export type SearchIndexItem = { export type SearchIndexItem = {
id: string; id: string;
parentId?: string;
path?: string; path?: string;
label: string; label: string;
keywords: string[]; keywords: string[];
icon?: string; icon?: string;
children?: SearchIndexItem[];
}; };
const rootMods = new Map(generated.map(item => [item.id, item])); const rootMods = new Map(generated.map(item => [item.id, item]));
function walk(item: GeneratedSearchIndexItem) { // link inlining here
for (const item of generated) {
if (item.inlining) { if (item.inlining) {
for (const id of item.inlining) { for (const id of item.inlining) {
const inline = rootMods.get(id); const inline = rootMods.get(id);
if (inline) { if (inline) {
(item.children ??= []).push(inline); inline.parentId = item.id;
rootMods.delete(id);
} else { } else {
console.log('[Settings Search Index] Failed to inline', id); console.log('[Settings Search Index] Failed to inline', id);
} }
} }
} }
for (const child of item.children ?? []) {
walk(child);
}
}
for (const item of generated) {
walk(item);
} }
export const searchIndexes: SearchIndexItem[] = generated; export const searchIndexes: SearchIndexItem[] = generated;

View file

@ -6,12 +6,12 @@
declare module 'search-index:settings' { declare module 'search-index:settings' {
export type GeneratedSearchIndexItem = { export type GeneratedSearchIndexItem = {
id: string; id: string;
parentId?: string;
path?: string; path?: string;
label: string; label: string;
keywords: string[]; keywords: string[];
icon?: string; icon?: string;
inlining?: string[]; inlining?: string[];
children?: GeneratedSearchIndexItem[];
}; };
export const searchIndexes: GeneratedSearchIndexItem[]; export const searchIndexes: GeneratedSearchIndexItem[];