diff --git a/javascript/_result.js b/javascript/_result.js
index c90b4e6..4c71405 100644
--- a/javascript/_result.js
+++ b/javascript/_result.js
@@ -30,7 +30,9 @@ class AutocompleteResult {
meta = null;
hash = null;
sortKey = null;
+ // uFuzzy specific
highlightedText = null;
+ matchSource = null;
// Constructor
constructor(text, type) {
diff --git a/javascript/_uFuzzy.js b/javascript/_uFuzzy.js
index bfd8a83..ed14a06 100644
--- a/javascript/_uFuzzy.js
+++ b/javascript/_uFuzzy.js
@@ -47,13 +47,28 @@ class TacFuzzy {
static #infoThresh = Infinity; // Make sure we are always getting info, performance isn't that bad for the default tag sets
static #usePrefixCache = false;
static #tacFuzzyOpts = {
- intraIns: Infinity,
+ intraIns: 10,
interIns: Infinity,
intraChars: "[\\w\\-']", // Alphanumeric, hyphen, underscore & apostrophe
+ interChars: "[^\\s,|<>\\[\\]:]", // Everything except tag separators
+ interLft: 1, // loose
+ sort: (info, haystack, needle) => { return info["idx"].map((v, i) => i); }
+ }
+ static #tacFuzzyOptsUnicode = {
+ intraIns: 10,
+ interIns: Infinity,
+ unicode: true,
+ interSplit: "[^\\p{L}\\d']+",
+ intraSplit: "\\p{Ll}\\p{Lu}",
+ intraBound: "\\p{L}\\d|\\d\\p{L}|\\p{Ll}\\p{Lu}",
+ intraChars: "[\\p{L}\\d']",
+ interChars: "[^\\s,|<>\\[\\]:]", // Everything except tag separators
+ intraContr: "'\\p{L}{1,2}\\b",
interLft: 1, // loose
sort: (info, haystack, needle) => { return info["idx"].map((v, i) => i); }
}
static #u = new this.#uFuzzy(this.#tacFuzzyOpts);
+ static #uUnicode = new this.#uFuzzy(this.#tacFuzzyOptsUnicode);
// Prefilter function to reduce search scope (from uFuzzy demo)
static #prefixCache = [];
static #prefilter = (haystack, needle) => {
@@ -99,16 +114,19 @@ class TacFuzzy {
* @param {String} needle - The search term (tagword)
* @returns A list of uFuzzy search results
*/
- static search = (haystack, needle) => {
+ static search = (haystack, needle, unicode = false) => {
let preFiltered = this.#usePrefixCache ? this.#prefilter(haystack, needle) : null;
- let [idxs, info, order] = this.#u.search(haystack, needle, this.#oooPermute, this.#infoThresh, preFiltered);
+ let [idxs, info, order] = unicode
+ ? this.#uUnicode.search(haystack, needle, this.#oooPermute, this.#infoThresh, preFiltered)
+ : this.#u.search(haystack, needle, this.#oooPermute, this.#infoThresh, preFiltered);
if (idxs != null) {
if (info != null) {
this.toStr = oi => {
let hi = info.idx[oi];
- return this.#uFuzzy.highlight(haystack[hi], info.ranges[oi]);
+ let mark = (part, matched) => matched ? '' + part + '' : part;
+ return this.#uFuzzy.highlight(haystack[hi], info.ranges[oi], mark);
};
return order.map(oi => [info.idx[oi], oi])
}
diff --git a/javascript/tagAutocomplete.js b/javascript/tagAutocomplete.js
index f72c76a..f3c0cda 100644
--- a/javascript/tagAutocomplete.js
+++ b/javascript/tagAutocomplete.js
@@ -13,6 +13,7 @@
"--live-translation-color-1": ["lightskyblue", "#2d89ef"],
"--live-translation-color-2": ["palegoldenrod", "#eb5700"],
"--live-translation-color-3": ["darkseagreen", "darkgreen"],
+ "--match-filter": ["brightness(1.2) drop-shadow(1px 1px 6px black)", "brightness(0.8)"]
}
const browserVars = {
"--results-overflow-y": {
@@ -90,6 +91,9 @@ const autocompleteCSS = `
content: "✨";
margin-right: 2px;
}
+ .acMatchHighlight {
+ filter: var(--match-filter);
+ }
.acWikiLink {
padding: 0.5rem;
margin: -0.5rem 0 -0.5rem -0.5rem;
@@ -713,9 +717,27 @@ function addResultsToList(textArea, results, tagword, resetList) {
displayText += `[${translations.get(result.text)}]`;
// Print search term bolded in result
- //itemText.innerHTML = displayText.replace(tagword, `${tagword}`);
- itemText.innerHTML = result.highlightedText || displayText.replace(new RegExp(escapeRegExp(tagword), "ig"), "$&");
- //itemText.innerHTML = displayText.replace(/<mark>(.*)<\/mark>/g, "$1")
+ if (result.highlightedText) {
+ switch (result.matchSource) {
+ case "base":
+ itemText.innerHTML = result.highlightedText;
+ break;
+ case "alias":
+ let aliases = result.highlightedText.split(",");
+ let matchingAlias = aliases.find(a => a.includes(""));
+ itemText.innerHTML = matchingAlias + " ➝ " + result.text;
+ break;
+ case "translation":
+ console.error("Translation highlighting not implemented yet for aliases")
+ itemText.innerHTML = `${result.text}[${result.highlightedText}]`
+ break;
+ default:
+ itemText.innerHTML = displayText;
+ break;
+ }
+ } else {
+ itemText.innerHTML = displayText;
+ }
const splitTypes = [ResultType.wildcardFile, ResultType.yamlWildcard]
if (splitTypes.includes(result.type) && itemText.innerHTML.includes("/")) {
@@ -1151,6 +1173,8 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
if (!fuzMatchSet.has(idx)) {
const result = new AutocompleteResult(allTags[idx][0], ResultType.tag);
result.highlightedText = TacFuzzy.toStr(orderIdx);
+ result.matchSource = "base"
+
result.category = allTags[idx][1];
result.count = allTags[idx][2];
result.aliases = allTags[idx][3];
@@ -1165,6 +1189,8 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
if (!fuzMatchSet.has(idx)) {
const result = new AutocompleteResult(allTags[idx][0], ResultType.tag)
result.highlightedText = TacFuzzy.toStr(orderIdx);
+ result.matchSource = "alias"
+
result.category = allTags[idx][1];
result.count = allTags[idx][2];
result.aliases = allTags[idx][3];
@@ -1179,6 +1205,8 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
if (!extraFuzMatchSet.has(idx)) {
const result = new AutocompleteResult(extras[idx][0], ResultType.extra)
result.highlightedText = TacFuzzy.toStr(orderIdx);
+ result.matchSource = "base"
+
result.category = extras[idx][1] || 0; // If no category is given, use 0 as the default
result.meta = extras[idx][2] || "Custom tag";
result.aliases = extras[idx][3] || "";
@@ -1193,6 +1221,8 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
if (!extraFuzMatchSet.has(idx)) {
const result = new AutocompleteResult(extras[idx][0], ResultType.extra)
result.highlightedText = TacFuzzy.toStr(orderIdx);
+ result.matchSource = "alias"
+
result.category = extras[idx][1] || 0; // If no category is given, use 0 as the default
result.meta = extras[idx][2] || "Custom tag";
result.aliases = extras[idx][3] || "";
@@ -1200,7 +1230,36 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
extraFuzMatchSet.add(idx);
}
});
- const transFuzResult = TacFuzzy.search([...translations.keys()].filter(x => !!x), tagword)
+ const transFuzResult = TacFuzzy.search([...translations.keys()].filter(x => !!x), tagword, true) // Unicode search here, slower but needed for non-latin translations
+ transFuzResult.forEach(pair => {
+ const idx = pair[0];
+ const orderIdx = pair[1];
+ if (!transFuzMatchSet.has(idx)) {
+ const translationKey = [...translations.keys()][idx];
+ const tagForTranslation = allTags.find(t => t[0] === translationKey || t[3]?.split(",").some(a => a === translationKey));
+ const extraForTranslation = extras.find(e => e[0] === translationKey || e[3]?.split(",").some(a => a === translationKey));
+
+ const result = new AutocompleteResult(tagForTranslation[0] || extraForTranslation[0], ResultType.tag)
+ result.highlightedText = TacFuzzy.toStr(orderIdx);
+ // TODO: translations can't differentiate between tag and alias here yet
+ result.matchSource = "translation";
+
+ result.category = tagForTranslation[1] || extraForTranslation[1] || 0;
+
+ if (tagForTranslation)
+ result.count = tagForTranslation[2] || 0;
+ else if (extraForTranslation)
+ result.meta = extraForTranslation[2] || "Custom tag";
+
+ result.aliases = tagForTranslation[3] || extraForTranslation[3] || "";
+
+ if (tagForTranslation) {
+ tagOut.push(result);
+ } else if (extraForTranslation) {
+ extraOut.push(result);
+ }
+ }
+ });
// Append results for each set
results = results.concat([...extraOut]).concat([...tagOut]);