From 988ba6b0197844d0f02ba8fc603f24467dd2c439 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Wed, 12 Feb 2025 20:51:31 +0400 Subject: [PATCH 1/3] Revert "Don't display aliases when prefix is matching both alias and tag name" This reverts commit 15d09cf9ba38dc74d62f582becc93786b7761cb2. --- assets/js/autocomplete.ts | 8 ++-- assets/js/utils/__tests__/suggestions.spec.ts | 44 ++----------------- assets/js/utils/suggestions.ts | 18 ++++---- 3 files changed, 15 insertions(+), 55 deletions(-) diff --git a/assets/js/autocomplete.ts b/assets/js/autocomplete.ts index c5ae754c..d940794c 100644 --- a/assets/js/autocomplete.ts +++ b/assets/js/autocomplete.ts @@ -8,7 +8,7 @@ import store from './utils/store'; import { TermContext } from './query/lex'; import { $$ } from './utils/dom'; import { - createLocalAutocompleteResultFormatter, + formatLocalAutocompleteResult, fetchLocalAutocomplete, fetchSuggestions, SuggestionsPopup, @@ -196,11 +196,9 @@ function listenAutocomplete() { originalTerm = `${inputField.value}`.toLowerCase(); } - const matchedTerm = trimPrefixes(originalTerm); - const suggestions = localAc - .matchPrefix(matchedTerm, suggestionsCount) - .map(createLocalAutocompleteResultFormatter(matchedTerm)); + .matchPrefix(trimPrefixes(originalTerm), suggestionsCount) + .map(formatLocalAutocompleteResult); if (suggestions.length) { popup.renderSuggestions(suggestions).showForField(targetedInput); diff --git a/assets/js/utils/__tests__/suggestions.spec.ts b/assets/js/utils/__tests__/suggestions.spec.ts index 833915aa..29de9691 100644 --- a/assets/js/utils/__tests__/suggestions.spec.ts +++ b/assets/js/utils/__tests__/suggestions.spec.ts @@ -2,7 +2,7 @@ import { fetchMock } from '../../../test/fetch-mock.ts'; import { fetchLocalAutocomplete, fetchSuggestions, - createLocalAutocompleteResultFormatter, + formatLocalAutocompleteResult, purgeSuggestionsCache, SuggestionsPopup, TermSuggestion, @@ -334,13 +334,12 @@ describe('Suggestions', () => { }); }); - describe('createLocalAutocompleteResultFormatter', () => { + describe('formatLocalAutocompleteResult', () => { it('should format suggested tags as tag name and the count', () => { const tagName = 'safe'; const tagCount = getRandomIntBetween(5, 10); - const formatter = createLocalAutocompleteResultFormatter(); - const resultObject = formatter({ + const resultObject = formatLocalAutocompleteResult({ name: tagName, aliasName: tagName, imageCount: tagCount, @@ -355,8 +354,7 @@ describe('Suggestions', () => { const tagAlias = 'rating:safe'; const tagCount = getRandomIntBetween(5, 10); - const formatter = createLocalAutocompleteResultFormatter(); - const resultObject = formatter({ + const resultObject = formatLocalAutocompleteResult({ name: tagName, aliasName: tagAlias, imageCount: tagCount, @@ -365,39 +363,5 @@ describe('Suggestions', () => { expect(resultObject.label).toBe(`${tagAlias} ⇒ ${tagName} (${tagCount})`); expect(resultObject.value).toBe(tagName); }); - - it('should not display aliases when tag is starting with the same matched', () => { - const tagName = 'chest fluff'; - const tagAlias = 'chest floof'; - const tagCount = getRandomIntBetween(5, 10); - - const prefix = 'ch'; - - const formatter = createLocalAutocompleteResultFormatter(prefix); - const resultObject = formatter({ - name: tagName, - aliasName: tagAlias, - imageCount: tagCount, - }); - - expect(resultObject.label).toBe(`${tagName} (${tagCount})`); - }); - - it('should display aliases if matched prefix is different from the tag name', () => { - const tagName = 'queen chrysalis'; - const tagAlias = 'chrysalis'; - const tagCount = getRandomIntBetween(5, 10); - - const prefix = 'ch'; - - const formatter = createLocalAutocompleteResultFormatter(prefix); - const resultObject = formatter({ - name: tagName, - aliasName: tagAlias, - imageCount: tagCount, - }); - - expect(resultObject.label).toBe(`${tagAlias} ⇒ ${tagName} (${tagCount})`); - }); }); }); diff --git a/assets/js/utils/suggestions.ts b/assets/js/utils/suggestions.ts index 31d95a74..099c9860 100644 --- a/assets/js/utils/suggestions.ts +++ b/assets/js/utils/suggestions.ts @@ -176,17 +176,15 @@ export async function fetchLocalAutocomplete(): Promise { .then(buf => new LocalAutocompleter(buf)); } -export function createLocalAutocompleteResultFormatter(matchedPrefix?: string): (result: Result) => TermSuggestion { - return result => { - let tagName = result.name; +export function formatLocalAutocompleteResult(result: Result): TermSuggestion { + let tagName = result.name; - if (tagName !== result.aliasName && (!matchedPrefix || !tagName.startsWith(matchedPrefix))) { - tagName = `${result.aliasName} ⇒ ${tagName}`; - } + if (tagName !== result.aliasName) { + tagName = `${result.aliasName} ⇒ ${tagName}`; + } - return { - value: result.name, - label: `${tagName} (${result.imageCount})`, - }; + return { + value: result.name, + label: `${tagName} (${result.imageCount})`, }; } From 235d7ab3625ffa27260da38267954e9b2a7a5faf Mon Sep 17 00:00:00 2001 From: KoloMl Date: Wed, 12 Feb 2025 21:03:34 +0400 Subject: [PATCH 2/3] 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; } } From 5da1080ed926870a0e27a172a860177da9640dca Mon Sep 17 00:00:00 2001 From: KoloMl Date: Wed, 12 Feb 2025 21:30:06 +0400 Subject: [PATCH 3/3] Replaced `undefined` check with the type check --- assets/js/utils/unique-heap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/utils/unique-heap.ts b/assets/js/utils/unique-heap.ts index 6c3d70f2..92aeab91 100644 --- a/assets/js/utils/unique-heap.ts +++ b/assets/js/utils/unique-heap.ts @@ -24,7 +24,7 @@ export class UniqueHeap { const key = this.unique(value); const prevIndex = this.keys.get(key); - if (prevIndex === undefined) { + if (typeof prevIndex === 'undefined') { this.keys.set(key, this.length); this.values[this.length++] = value; } else if (forceReplace) {