ext_umi.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. const UMI_PROMPT_REGEX = /<[^\s]*?\[[^,<>]*[\]|]?>?/gi;
  2. const UMI_TAG_REGEX = /(?:\[|\||--)([^<>\[\]\-|]+)/gi;
  3. const UMI_TRIGGER = () => CFG.useWildcards && [...tagword.matchAll(UMI_PROMPT_REGEX)].length > 0;
  4. class UmiParser extends BaseTagParser {
  5. parse(textArea, prompt) {
  6. // We are in a UMI yaml tag definition, parse further
  7. let umiSubPrompts = [...prompt.matchAll(UMI_PROMPT_REGEX)];
  8. let umiTags = [];
  9. let umiTagsWithOperators = []
  10. const insertAt = (str,char,pos) => str.slice(0,pos) + char + str.slice(pos);
  11. umiSubPrompts.forEach(umiSubPrompt => {
  12. umiTags = umiTags.concat([...umiSubPrompt[0].matchAll(UMI_TAG_REGEX)].map(x => x[1].toLowerCase()));
  13. const start = umiSubPrompt.index;
  14. const end = umiSubPrompt.index + umiSubPrompt[0].length;
  15. if (textArea.selectionStart >= start && textArea.selectionStart <= end) {
  16. umiTagsWithOperators = insertAt(umiSubPrompt[0], '###', textArea.selectionStart - start);
  17. }
  18. });
  19. // Safety check since UMI parsing sometimes seems to trigger outside of an UMI subprompt and thus fails
  20. if (umiTagsWithOperators.length === 0) {
  21. return null;
  22. }
  23. const promptSplitToTags = umiTagsWithOperators.replace(']###[', '][').split("][");
  24. const clean = (str) => str
  25. .replaceAll('>', '')
  26. .replaceAll('<', '')
  27. .replaceAll('[', '')
  28. .replaceAll(']', '')
  29. .trim();
  30. const matches = promptSplitToTags.reduce((acc, curr) => {
  31. let isOptional = curr.includes("|");
  32. let isNegative = curr.startsWith("--");
  33. let out;
  34. if (isOptional) {
  35. out = {
  36. hasCursor: curr.includes("###"),
  37. tags: clean(curr).split('|').map(x => ({
  38. hasCursor: x.includes("###"),
  39. isNegative: x.startsWith("--"),
  40. tag: clean(x).replaceAll("###", '').replaceAll("--", '')
  41. }))
  42. };
  43. acc.optional.push(out);
  44. acc.all.push(...out.tags.map(x => x.tag));
  45. } else if (isNegative) {
  46. out = {
  47. hasCursor: curr.includes("###"),
  48. tags: clean(curr).replaceAll("###", '').split('|'),
  49. };
  50. out.tags = out.tags.map(x => x.startsWith("--") ? x.substring(2) : x);
  51. acc.negative.push(out);
  52. acc.all.push(...out.tags);
  53. } else {
  54. out = {
  55. hasCursor: curr.includes("###"),
  56. tags: clean(curr).replaceAll("###", '').split('|'),
  57. };
  58. acc.positive.push(out);
  59. acc.all.push(...out.tags);
  60. }
  61. return acc;
  62. }, { positive: [], negative: [], optional: [], all: [] });
  63. //console.log({ matches })
  64. const filteredWildcards = (tagword) => {
  65. const wildcards = yamlWildcards.filter(x => {
  66. let tags = x[1];
  67. const matchesNeg =
  68. matches.negative.length === 0
  69. || matches.negative.every(x =>
  70. x.hasCursor
  71. || x.tags.every(t => !tags[t])
  72. );
  73. if (!matchesNeg) return false;
  74. const matchesPos =
  75. matches.positive.length === 0
  76. || matches.positive.every(x =>
  77. x.hasCursor
  78. || x.tags.every(t => tags[t])
  79. );
  80. if (!matchesPos) return false;
  81. const matchesOpt =
  82. matches.optional.length === 0
  83. || matches.optional.some(x =>
  84. x.tags.some(t =>
  85. t.hasCursor
  86. || t.isNegative
  87. ? !tags[t.tag]
  88. : tags[t.tag]
  89. ));
  90. if (!matchesOpt) return false;
  91. return true;
  92. }).reduce((acc, val) => {
  93. Object.keys(val[1]).forEach(tag => acc[tag] = acc[tag] + 1 || 1);
  94. return acc;
  95. }, {});
  96. return Object.entries(wildcards)
  97. .sort((a, b) => b[1] - a[1])
  98. .filter(x =>
  99. x[0] === tagword
  100. || !matches.all.includes(x[0])
  101. );
  102. }
  103. if (umiTags.length > 0) {
  104. // Get difference for subprompt
  105. let tagCountChange = umiTags.length - umiPreviousTags.length;
  106. let diff = difference(umiTags, umiPreviousTags);
  107. umiPreviousTags = umiTags;
  108. // Show all condition
  109. let showAll = tagword.endsWith("[") || tagword.endsWith("[--") || tagword.endsWith("|");
  110. // Exit early if the user closed the bracket manually
  111. if ((!diff || diff.length === 0 || (diff.length === 1 && tagCountChange < 0)) && !showAll) {
  112. if (!hideBlocked) hideResults(textArea);
  113. return;
  114. }
  115. let umiTagword = diff[0] || '';
  116. let tempResults = [];
  117. if (umiTagword && umiTagword.length > 0) {
  118. umiTagword = umiTagword.toLowerCase().replace(/[\n\r]/g, "");
  119. originalTagword = tagword;
  120. tagword = umiTagword;
  121. let filteredWildcardsSorted = filteredWildcards(umiTagword);
  122. let searchRegex = new RegExp(`(^|[^a-zA-Z])${escapeRegExp(umiTagword)}`, 'i')
  123. let baseFilter = x => x[0].toLowerCase().search(searchRegex) > -1;
  124. let spaceIncludeFilter = x => x[0].toLowerCase().replaceAll(" ", "_").search(searchRegex) > -1;
  125. tempResults = filteredWildcardsSorted.filter(x => baseFilter(x) || spaceIncludeFilter(x)) // Filter by tagword
  126. // Add final results
  127. let finalResults = [];
  128. tempResults.forEach(t => {
  129. let result = new AutocompleteResult(t[0].trim(), ResultType.yamlWildcard)
  130. result.count = t[1];
  131. finalResults.push(result);
  132. });
  133. return finalResults;
  134. } else if (showAll) {
  135. let filteredWildcardsSorted = filteredWildcards("");
  136. // Add final results
  137. let finalResults = [];
  138. filteredWildcardsSorted.forEach(t => {
  139. let result = new AutocompleteResult(t[0].trim(), ResultType.yamlWildcard)
  140. result.count = t[1];
  141. finalResults.push(result);
  142. });
  143. originalTagword = tagword;
  144. tagword = "";
  145. return finalResults;
  146. }
  147. } else {
  148. let filteredWildcardsSorted = filteredWildcards("");
  149. // Add final results
  150. let finalResults = [];
  151. filteredWildcardsSorted.forEach(t => {
  152. let result = new AutocompleteResult(t[0].trim(), ResultType.yamlWildcard)
  153. result.count = t[1];
  154. finalResults.push(result);
  155. });
  156. originalTagword = tagword;
  157. tagword = "";
  158. return finalResults;
  159. }
  160. }
  161. }
  162. function updateUmiTags( tagType, sanitizedText, newPrompt, textArea) {
  163. // If it was a yaml wildcard, also update the umiPreviousTags
  164. if (tagType === ResultType.yamlWildcard && originalTagword.length > 0) {
  165. let umiSubPrompts = [...newPrompt.matchAll(UMI_PROMPT_REGEX)];
  166. let umiTags = [];
  167. umiSubPrompts.forEach(umiSubPrompt => {
  168. umiTags = umiTags.concat([...umiSubPrompt[0].matchAll(UMI_TAG_REGEX)].map(x => x[1].toLowerCase()));
  169. });
  170. umiPreviousTags = umiTags;
  171. hideResults(textArea);
  172. return true;
  173. }
  174. return false;
  175. }
  176. async function load() {
  177. if (yamlWildcards.length === 0) {
  178. try {
  179. let yamlTags = (await readFile(`${tagBasePath}/temp/wcet.txt`)).split("\n");
  180. // Split into tag, count pairs
  181. yamlWildcards = yamlTags.map(x => x
  182. .trim()
  183. .split(","))
  184. .map(([i, ...rest]) => [
  185. i,
  186. rest.reduce((a, b) => {
  187. a[b.toLowerCase()] = true;
  188. return a;
  189. }, {}),
  190. ]);
  191. } catch (e) {
  192. console.error("Error loading yaml wildcards: " + e);
  193. }
  194. }
  195. }
  196. function sanitize(tagType, text) {
  197. // Replace underscores only if the yaml tag is not using them
  198. if (tagType === ResultType.yamlWildcard && !yamlWildcards.includes(text)) {
  199. return text.replaceAll("_", " ");
  200. }
  201. return null;
  202. }
  203. // Add UMI parser
  204. PARSERS.push(new UmiParser(UMI_TRIGGER));
  205. // Add our utility functions to their respective queues
  206. QUEUE_FILE_LOAD.push(load);
  207. QUEUE_SANITIZE.push(sanitize);
  208. QUEUE_AFTER_INSERT.push(updateUmiTags);