From d2b5142d7d8834e35a41a297c0b5834c153302f9 Mon Sep 17 00:00:00 2001 From: ctwrs <> Date: Sat, 24 Dec 2022 01:28:12 +0100 Subject: [PATCH 1/2] Umi filtering - initial version --- javascript/tagAutocomplete.js | 68 +++++++++++++++++++++++++++--- scripts/tag_autocomplete_helper.py | 8 ++-- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/javascript/tagAutocomplete.js b/javascript/tagAutocomplete.js index 29328dd..f013e62 100644 --- a/javascript/tagAutocomplete.js +++ b/javascript/tagAutocomplete.js @@ -669,9 +669,54 @@ async function autocomplete(textArea, prompt, fixedTag = null) { let umiSubPrompts = [...prompt.matchAll(UMI_PROMPT_REGEX)]; let umiTags = []; + let umiTagsWithOperators = [] umiSubPrompts.forEach(umiSubPrompt => { umiTags = umiTags.concat([...umiSubPrompt[0].matchAll(UMI_TAG_REGEX)].map(x => x[1].toLowerCase())); + umiTagsWithOperators = umiTagsWithOperators.concat([...umiSubPrompt[0].matchAll(UMI_TAG_REGEX)]); }); + + // UMI tag matching for narrowed down tag counts + const partition = (arr, predicate) => arr.reduce((acc, val, i, arr) => { + acc[predicate(val, i, arr) ? 0 : 1].push(val); + return acc; + }, [[], []]); + + const [positiveAndOptional, negative] = partition(umiTagsWithOperators, x => !x[0].startsWith("--")); + + const [positive, optional] = partition( + positiveAndOptional, + (x, i, arr) => + x[0].startsWith("[") + && x.input.endsWith("|"), // bad condition, find a better one + ); + + const matches = { + neg: negative.map(x => x[1].toLowerCase()), + pos: positive.map(x => x[1].toLowerCase()), + opt: optional.map(x => x[1].toLowerCase()) + }; + + const filteredWildcards = (tagword) => { + // TODO; fix optional tagword matching, rn the top level is too greedy and makes finding out if this is an optional tagword impossible + const tagwordIsOptional = matches.opt.includes(tagword); + console.log('tagwordIsOptional', tagwordIsOptional) + const wildcards = yamlWildcards.filter(x => { + let tags = x[1]; + const matchesNeg = matches.neg.length === 0 || matches.neg.every(x => x === tagword || !tags[x]); + if (!matchesNeg) return false; + const matchesPos = matches.pos.length === 0 || matches.pos.every(x => x === tagword || tags[x]); + if (!matchesPos) return false; + const matchesOpt = matches.opt.length === 0 || tagwordIsOptional || matches.opt.some(x => x !== tagword && tags[x]); + if (!matchesOpt) return false; + return true; + }).reduce((acc, val) => { + Object.keys(val[1]).forEach(tag => acc[tag] = acc[tag] + 1 || 1); + return acc; + }, {}); + console.log(wildcards); + + return Object.entries(wildcards).sort((a, b) => b[1] - a[1]).filter(x => x[0] === tagword || !umiTags.includes(x[0])); + } if (umiTags.length > 0) { // Get difference for subprompt @@ -682,7 +727,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) { // Show all condition let showAll = tagword.endsWith("[") || tagword.endsWith("[--") || tagword.endsWith("|"); - console.log(tagword, umiTags, diff, tagCountChange) + console.log(tagword, umiTags, diff, tagCountChange, umiTagsWithOperators) // Exit early if the user closed the bracket manually if ((!diff || diff.length === 0 || (diff.length === 1 && tagCountChange < 0)) && !showAll) { @@ -696,19 +741,21 @@ async function autocomplete(textArea, prompt, fixedTag = null) { umiTagword = umiTagword.toLowerCase().replace(/[\n\r]/g, ""); originalTagword = tagword; tagword = umiTagword; - + let filteredWildcardsSorted = filteredWildcards(umiTagword); let searchRegex = new RegExp(`(^|[^a-zA-Z])${escapeRegExp(umiTagword)}`, 'i') let baseFilter = x => x[0].toLowerCase().search(searchRegex) > -1; let spaceIncludeFilter = x => x[0].toLowerCase().replaceAll(" ", "_").search(searchRegex) > -1; - tempResults = yamlWildcards.filter(x => baseFilter(x) || spaceIncludeFilter(x)) // Filter by tagword + tempResults = filteredWildcardsSorted.filter(x => baseFilter(x) || spaceIncludeFilter(x)) // Filter by tagword results = tempResults.map(x => [x[0].trim(), "yamlWildcard", x[1]]); // Mark as yaml wildcard } else if (showAll) { - results = yamlWildcards.map(x => [x[0].trim(), "yamlWildcard", x[1]]); // Mark as yaml wildcard + let filteredWildcardsSorted = filteredWildcards(""); + results = filteredWildcardsSorted.map(x => [x[0].trim(), "yamlWildcard", x[1]]); // Mark as yaml wildcard originalTagword = tagword; tagword = ""; } } else { - results = yamlWildcards.map(x => [x[0].trim(), "yamlWildcard", x[1]]); // Mark as yaml wildcard + let filteredWildcardsSorted = filteredWildcards(""); + results = filteredWildcardsSorted.map(x => [x[0].trim(), "yamlWildcard", x[1]]); // Mark as yaml wildcard originalTagword = tagword; tagword = ""; } @@ -907,7 +954,16 @@ async function setup() { try { let yamlTags = (await readFile(`${tagBasePath}/temp/wcet.txt?${new Date().getTime()}`)).split("\n"); // Split into tag, count pairs - yamlWildcards = yamlTags.map(x => x.trim().split(",")); + yamlWildcards = yamlTags.map(x => x + .trim() + .split(",")) + .map(([i, ...rest]) => [ + i, + rest.reduce((a, b) => { + a[b.toLowerCase()] = true; + return a; + }, {}), + ]); } catch (e) { console.error("Error loading yaml wildcards: " + e); } diff --git a/scripts/tag_autocomplete_helper.py b/scripts/tag_autocomplete_helper.py index cb9c71b..296a816 100644 --- a/scripts/tag_autocomplete_helper.py +++ b/scripts/tag_autocomplete_helper.py @@ -61,16 +61,14 @@ def get_ext_wildcard_tags(): for path in WILDCARD_EXT_PATHS: yaml_files.extend(p for p in path.rglob("*.yml")) yaml_files.extend(p for p in path.rglob("*.yaml")) + count = 0 for path in yaml_files: try: with open(path, encoding="utf8") as file: data = yaml.safe_load(file) for item in data: - for _, tag in enumerate(data[item]['Tags']): - if tag not in wildcard_tags: - wildcard_tags[tag] = 1 - else: - wildcard_tags[tag] += 1 + wildcard_tags[count] = ','.join(data[item]['Tags']) + count += 1 except yaml.YAMLError as exc: print(exc) # Sort by count From 0c3397aee64c3b61ca2ff05b8f173e8ca6254ed7 Mon Sep 17 00:00:00 2001 From: ctwrs <> Date: Mon, 26 Dec 2022 01:57:15 +0100 Subject: [PATCH 2/2] Fix umi tag autocomplete filtering logic --- javascript/tagAutocomplete.js | 112 ++++++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 31 deletions(-) diff --git a/javascript/tagAutocomplete.js b/javascript/tagAutocomplete.js index f013e62..3bac509 100644 --- a/javascript/tagAutocomplete.js +++ b/javascript/tagAutocomplete.js @@ -132,7 +132,7 @@ var translations = new Map(); async function loadTags(c) { // Load main tags and aliases - if (allTags.length === 0) { + if (allTags.length === 0 && c.tagFile && c.tagFile !== "None") { try { allTags = await loadCSV(`${tagBasePath}/${c.tagFile}?${new Date().getTime()}`); } catch (e) { @@ -670,52 +670,103 @@ async function autocomplete(textArea, prompt, fixedTag = null) { let umiTags = []; let umiTagsWithOperators = [] + + const insertAt = (str,char,pos) => str.slice(0,pos) + char + str.slice(pos); + umiSubPrompts.forEach(umiSubPrompt => { umiTags = umiTags.concat([...umiSubPrompt[0].matchAll(UMI_TAG_REGEX)].map(x => x[1].toLowerCase())); - umiTagsWithOperators = umiTagsWithOperators.concat([...umiSubPrompt[0].matchAll(UMI_TAG_REGEX)]); + + const start = umiSubPrompt.index; + const end = umiSubPrompt.index + umiSubPrompt[0].length; + if (textArea.selectionStart >= start && textArea.selectionStart <= end) { + umiTagsWithOperators = insertAt(umiSubPrompt[0], '###', textArea.selectionStart - start); + } }); - // UMI tag matching for narrowed down tag counts - const partition = (arr, predicate) => arr.reduce((acc, val, i, arr) => { - acc[predicate(val, i, arr) ? 0 : 1].push(val); + const promptSplitToTags = umiTagsWithOperators.replace(']###[', '][').split("]["); + + const clean = (str) => str + .replaceAll('>', '') + .replaceAll('<', '') + .replaceAll('[', '') + .replaceAll(']', '') + .trim(); + + const matches = promptSplitToTags.reduce((acc, curr) => { + isOptional = curr.includes("|"); + isNegative = curr.startsWith("--"); + let out; + if (isOptional) { + out = { + hasCursor: curr.includes("###"), + tags: clean(curr).split('|').map(x => ({ + hasCursor: x.includes("###"), + isNegative: x.startsWith("--"), + tag: clean(x).replaceAll("###", '').replaceAll("--", '') + })) + }; + acc.optional.push(out); + acc.all.push(...out.tags.map(x => x.tag)); + } else if (isNegative) { + out = { + hasCursor: curr.includes("###"), + tags: clean(curr).replaceAll("###", '').split('|'), + }; + out.tags = out.tags.map(x => x.startsWith("--") ? x.substring(2) : x); + acc.negative.push(out); + acc.all.push(...out.tags); + } else { + out = { + hasCursor: curr.includes("###"), + tags: clean(curr).replaceAll("###", '').split('|'), + }; + acc.positive.push(out); + acc.all.push(...out.tags); + } return acc; - }, [[], []]); + }, { positive: [], negative: [], optional: [], all: [] }); - const [positiveAndOptional, negative] = partition(umiTagsWithOperators, x => !x[0].startsWith("--")); - - const [positive, optional] = partition( - positiveAndOptional, - (x, i, arr) => - x[0].startsWith("[") - && x.input.endsWith("|"), // bad condition, find a better one - ); - - const matches = { - neg: negative.map(x => x[1].toLowerCase()), - pos: positive.map(x => x[1].toLowerCase()), - opt: optional.map(x => x[1].toLowerCase()) - }; + console.log({ matches }) const filteredWildcards = (tagword) => { - // TODO; fix optional tagword matching, rn the top level is too greedy and makes finding out if this is an optional tagword impossible - const tagwordIsOptional = matches.opt.includes(tagword); - console.log('tagwordIsOptional', tagwordIsOptional) const wildcards = yamlWildcards.filter(x => { let tags = x[1]; - const matchesNeg = matches.neg.length === 0 || matches.neg.every(x => x === tagword || !tags[x]); + const matchesNeg = + matches.negative.length === 0 + || matches.negative.every(x => + x.hasCursor + || x.tags.every(t => !tags[t]) + ); if (!matchesNeg) return false; - const matchesPos = matches.pos.length === 0 || matches.pos.every(x => x === tagword || tags[x]); + const matchesPos = + matches.positive.length === 0 + || matches.positive.every(x => + x.hasCursor + || x.tags.every(t => tags[t]) + ); if (!matchesPos) return false; - const matchesOpt = matches.opt.length === 0 || tagwordIsOptional || matches.opt.some(x => x !== tagword && tags[x]); + const matchesOpt = + matches.optional.length === 0 + || matches.optional.some(x => + x.tags.some(t => + t.hasCursor + || t.isNegative + ? !tags[t.tag] + : tags[t.tag] + )); if (!matchesOpt) return false; return true; }).reduce((acc, val) => { Object.keys(val[1]).forEach(tag => acc[tag] = acc[tag] + 1 || 1); return acc; }, {}); - console.log(wildcards); - return Object.entries(wildcards).sort((a, b) => b[1] - a[1]).filter(x => x[0] === tagword || !umiTags.includes(x[0])); + return Object.entries(wildcards) + .sort((a, b) => b[1] - a[1]) + .filter(x => + x[0] === tagword + || !matches.all.includes(x[0]) + ); } if (umiTags.length > 0) { @@ -727,15 +778,13 @@ async function autocomplete(textArea, prompt, fixedTag = null) { // Show all condition let showAll = tagword.endsWith("[") || tagword.endsWith("[--") || tagword.endsWith("|"); - console.log(tagword, umiTags, diff, tagCountChange, umiTagsWithOperators) - // Exit early if the user closed the bracket manually if ((!diff || diff.length === 0 || (diff.length === 1 && tagCountChange < 0)) && !showAll) { if (!hideBlocked) hideResults(textArea); return; } - let umiTagword = diff[0]; + let umiTagword = diff[0] || ''; let tempResults = []; if (umiTagword && umiTagword.length > 0) { umiTagword = umiTagword.toLowerCase().replace(/[\n\r]/g, ""); @@ -817,6 +866,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) { // Guard for empty results if (!results.length) { + console.log('No results found for "' + tagword + '"'); hideResults(textArea); return; }