From 235d7ab3625ffa27260da38267954e9b2a7a5faf Mon Sep 17 00:00:00 2001 From: KoloMl Date: Wed, 12 Feb 2025 21:03:34 +0400 Subject: [PATCH] Fixed local autocompleter returning aliases even when tag is matched by prefix Patch from @liamwhite --- assets/js/utils/local-autocompleter.ts | 15 ++++++++++++--- assets/js/utils/unique-heap.ts | 18 ++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/assets/js/utils/local-autocompleter.ts b/assets/js/utils/local-autocompleter.ts index fbd5d2b5..15c460ea 100644 --- a/assets/js/utils/local-autocompleter.ts +++ b/assets/js/utils/local-autocompleter.ts @@ -100,6 +100,13 @@ export class LocalAutocompleter { return tagPointer; } + /** + * Return whether the tag pointed to by the reference index is an alias. + */ + private tagReferenceIsAlias(i: TagReferenceIndex): boolean { + return this.view.getInt32(this.referenceStart + i * 8 + 4, true) < 0; + } + /** * Get the images count for the given reference index. */ @@ -173,6 +180,7 @@ export class LocalAutocompleter { getResult: (i: number) => TagReferenceIndex, compare: (result: TagReferenceIndex) => number, hasFilteredAssociation: (result: TagReferenceIndex) => boolean, + isAlias: (result: TagReferenceIndex) => boolean, results: UniqueHeap, ) { const filter = !store.get('unfilter_tag_suggestions'); @@ -207,7 +215,7 @@ export class LocalAutocompleter { } // Nothing was filtered, so add - results.append(referenceIndex); + results.append(referenceIndex, !isAlias(referenceIndex)); } } @@ -230,18 +238,19 @@ export class LocalAutocompleter { // Set up filter context const hiddenTags = new Set(window.booru.hiddenTagList); const hasFilteredAssociation = this.isFilteredByReference.bind(this, hiddenTags); + const isAlias = this.tagReferenceIsAlias.bind(this); // Find tags ordered by full name const prefixMatch = (i: TagReferenceIndex) => strcmp(this.referenceToName(i, false).slice(0, prefix.length), prefix); const referenceToNameIndex = (i: number) => i; - this.scanResults(referenceToNameIndex, prefixMatch, hasFilteredAssociation, results); + this.scanResults(referenceToNameIndex, prefixMatch, hasFilteredAssociation, isAlias, results); // Find tags ordered by name in namespace const namespaceMatch = (i: TagReferenceIndex) => strcmp(nameInNamespace(this.referenceToName(i, false)).slice(0, prefix.length), prefix); const referenceToAliasIndex = this.getSecondaryResultAt.bind(this); - this.scanResults(referenceToAliasIndex, namespaceMatch, hasFilteredAssociation, results); + this.scanResults(referenceToAliasIndex, namespaceMatch, hasFilteredAssociation, isAlias, results); // Convert top K from heap into result array return results.topK(k).map((i: TagReferenceIndex) => ({ diff --git a/assets/js/utils/unique-heap.ts b/assets/js/utils/unique-heap.ts index 184d355b..6c3d70f2 100644 --- a/assets/js/utils/unique-heap.ts +++ b/assets/js/utils/unique-heap.ts @@ -1,28 +1,34 @@ export type Compare = (a: T, b: T) => number; export type Unique = (a: T) => unknown; -export type Collection = { [index: number]: T; length: number }; +export type Collection = { + [index: number]: T; + length: number; +}; export class UniqueHeap { - private keys: Set; + private keys: Map; private values: Collection; private length: number; private compare: Compare; private unique: Unique; constructor(compare: Compare, unique: Unique, values: Collection) { - this.keys = new Set(); + this.keys = new Map(); this.values = values; this.length = 0; this.compare = compare; this.unique = unique; } - append(value: T) { + append(value: T, forceReplace: boolean = false) { const key = this.unique(value); + const prevIndex = this.keys.get(key); - if (!this.keys.has(key)) { - this.keys.add(key); + if (prevIndex === undefined) { + this.keys.set(key, this.length); this.values[this.length++] = value; + } else if (forceReplace) { + this.values[prevIndex] = value; } }