@unocss/autocomplete 0.53.3 → 0.53.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,6 +4,43 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const core = require('@unocss/core');
6
6
  const lruCache = require('lru-cache');
7
+ const fzf = require('fzf');
8
+
9
+ function searchUsageBoundary(line, index) {
10
+ let start = index;
11
+ let end = index;
12
+ const regex = /[^\s>"'`;]/;
13
+ while (start && regex.test(line.charAt(start - 1)))
14
+ --start;
15
+ while (end < line.length && regex.test(line.charAt(end)))
16
+ ++end;
17
+ return {
18
+ content: line.slice(start, end),
19
+ start,
20
+ end
21
+ };
22
+ }
23
+ function searchAttrKey(content, cursor) {
24
+ const text = content.substring(0, cursor);
25
+ if (text.match(/(<\w+\s*)[^>]*$/) !== null)
26
+ return text.match(/\S+(?=\s*=\s*["']?[^"']*$)/)?.[0];
27
+ }
28
+ function cartesian(arr) {
29
+ if (arr.length < 2)
30
+ return arr;
31
+ return arr.reduce(
32
+ (a, b) => {
33
+ const ret = [];
34
+ a.forEach((a2) => {
35
+ b.forEach((b2) => {
36
+ ret.push(a2.concat([b2]));
37
+ });
38
+ });
39
+ return ret;
40
+ },
41
+ [[]]
42
+ );
43
+ }
7
44
 
8
45
  const shorthands = {
9
46
  num: `(${[0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 24, 36].join("|")})`,
@@ -31,6 +68,7 @@ function parseAutocomplete(template, theme = {}) {
31
68
  return shorthands[key];
32
69
  });
33
70
  handleGroups(template);
71
+ const fzf$1 = new fzf.Fzf(getAllCombination(parts));
34
72
  return {
35
73
  parts,
36
74
  suggest
@@ -74,7 +112,9 @@ function parseAutocomplete(template, theme = {}) {
74
112
  }
75
113
  );
76
114
  }
77
- function suggest(input) {
115
+ function suggest(input, matchType = "prefix") {
116
+ if (input.length && matchType === "fuzzy")
117
+ return fzf$1.find(input).map((i) => i.item);
78
118
  let rest = input;
79
119
  let matched = "";
80
120
  let combinations = [];
@@ -130,32 +170,43 @@ function parseAutocomplete(template, theme = {}) {
130
170
  return combinations.map((i) => matched + i).filter((i) => i.length >= input.length);
131
171
  }
132
172
  }
133
-
134
- function searchUsageBoundary(line, index) {
135
- let start = index;
136
- let end = index;
137
- const regex = /[^\s>"'`;]/;
138
- while (start && regex.test(line.charAt(start - 1)))
139
- --start;
140
- while (end < line.length && regex.test(line.charAt(end)))
141
- ++end;
142
- return {
143
- content: line.slice(start, end),
144
- start,
145
- end
146
- };
173
+ function getValuesFromPartTemplate(part) {
174
+ if (part.type === "static")
175
+ return [part.value];
176
+ if (part.type === "theme") {
177
+ return part.objects.flatMap((i) => {
178
+ const keys = Object.keys(i).filter((i2) => i2 && i2[0] !== "_");
179
+ for (const key in i) {
180
+ const value = i[key];
181
+ if (value === null || value === void 0)
182
+ continue;
183
+ if (typeof value === "object" && !Array.isArray(value)) {
184
+ const subKeys = getValuesFromPartTemplate({
185
+ type: "theme",
186
+ objects: [value]
187
+ }).map((i2) => `${key}-${i2}`);
188
+ keys.push(...subKeys);
189
+ }
190
+ }
191
+ return keys;
192
+ });
193
+ }
194
+ if (part.type === "group")
195
+ return [...part.values];
196
+ return [];
147
197
  }
148
- function searchAttrKey(content, cursor) {
149
- const text = content.substring(0, cursor);
150
- if (text.match(/(<\w+\s*)[^>]*$/) !== null)
151
- return text.match(/\S+(?=\s*=\s*["']?[^"']*$)/)?.[0];
198
+ function getAllCombination(parts) {
199
+ const values = parts.map((i) => getValuesFromPartTemplate(i));
200
+ const list = core.uniq(cartesian(values).flatMap((i) => i.join("").replace("-DEFAULT", "")));
201
+ return list;
152
202
  }
153
203
 
154
- function createAutocomplete(uno) {
204
+ function createAutocomplete(uno, options = {}) {
155
205
  const templateCache = /* @__PURE__ */ new Map();
156
206
  const cache = new lruCache.LRUCache({ max: 5e3 });
157
207
  let staticUtils = [];
158
208
  const templates = [];
209
+ const matchType = options.matchType ?? "prefix";
159
210
  reset();
160
211
  return {
161
212
  suggest,
@@ -197,7 +248,7 @@ function createAutocomplete(uno) {
197
248
  idx = 0;
198
249
  const variantPrefix = input.slice(0, idx);
199
250
  const variantSuffix = input.slice(idx + input.length);
200
- const result = processSuggestions(
251
+ let result = processSuggestions(
201
252
  await Promise.all([
202
253
  suggestSelf(processed),
203
254
  suggestStatic(processed),
@@ -208,6 +259,12 @@ function createAutocomplete(uno) {
208
259
  variantPrefix,
209
260
  variantSuffix
210
261
  );
262
+ if (matchType === "fuzzy") {
263
+ const fzf$1 = new fzf.Fzf(result, {
264
+ tiebreakers: [fzf.byStartAsc, fzf.byLengthAsc]
265
+ });
266
+ result = fzf$1.find(input).map((i) => i.item);
267
+ }
211
268
  cache.set(input, result);
212
269
  return result;
213
270
  }
@@ -248,6 +305,8 @@ function createAutocomplete(uno) {
248
305
  return i ? [input] : [];
249
306
  }
250
307
  async function suggestStatic(input) {
308
+ if (matchType === "fuzzy")
309
+ return staticUtils;
251
310
  return staticUtils.filter((i) => i.startsWith(input));
252
311
  }
253
312
  async function suggestUnoCache(input) {
@@ -256,12 +315,12 @@ function createAutocomplete(uno) {
256
315
  }
257
316
  function suggestFromPreset(input) {
258
317
  return templates.map(
259
- (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input)
318
+ (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input, matchType)
260
319
  ) || [];
261
320
  }
262
321
  function suggestVariant(input, used) {
263
322
  return uno.config.variants.filter((v) => v.autocomplete && (v.multiPass || !used.has(v))).flatMap((v) => core.toArray(v.autocomplete || [])).map(
264
- (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input)
323
+ (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input, matchType)
265
324
  );
266
325
  }
267
326
  function reset() {
@@ -280,8 +339,8 @@ function createAutocomplete(uno) {
280
339
  }
281
340
  function processSuggestions(suggestions, prefix = "", suffix = "") {
282
341
  return core.uniq(suggestions.flat()).filter((i) => !!(i && !i.match(/-$/) && !uno.isBlocked(i))).sort((a, b) => {
283
- const numA = +(a.match(/\d+$/)?.[0] || NaN);
284
- const numB = +(b.match(/\d+$/)?.[0] || NaN);
342
+ const numA = +(a.match(/\d+$/)?.[0] || Number.NaN);
343
+ const numB = +(b.match(/\d+$/)?.[0] || Number.NaN);
285
344
  if (!Number.isNaN(numA) && !Number.isNaN(numB))
286
345
  return numA - numB;
287
346
  return a.localeCompare(b);
@@ -289,6 +348,7 @@ function createAutocomplete(uno) {
289
348
  }
290
349
  }
291
350
 
351
+ exports.cartesian = cartesian;
292
352
  exports.createAutocomplete = createAutocomplete;
293
353
  exports.ignoredThemeKeys = ignoredThemeKeys;
294
354
  exports.parseAutocomplete = parseAutocomplete;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import { SuggestResult, AutoCompleteFunction, UnoGenerator } from '@unocss/core';
2
2
  import { LRUCache } from 'lru-cache';
3
3
 
4
+ type AutoCompleteMatchType = 'prefix' | 'fuzzy';
5
+ interface AutocompleteOptions {
6
+ matchType?: AutoCompleteMatchType;
7
+ }
4
8
  type AutocompleteTemplatePart = AutocompleteTemplateStatic | AutocompleteTemplateGroup | AutocompleteTemplateTheme;
5
9
  interface AutocompleteTemplateStatic {
6
10
  type: 'static';
@@ -16,10 +20,10 @@ interface AutocompleteTemplateTheme {
16
20
  }
17
21
  interface ParsedAutocompleteTemplate {
18
22
  parts: AutocompleteTemplatePart[];
19
- suggest(input: string): string[] | undefined;
23
+ suggest(input: string, matchType?: AutoCompleteMatchType): string[] | undefined;
20
24
  }
21
25
  interface UnocssAutocomplete {
22
- suggest: (input: string) => Promise<string[]>;
26
+ suggest: (input: string, allowsEmptyInput?: boolean) => Promise<string[]>;
23
27
  suggestInFile: (content: string, cursor: number) => Promise<SuggestResult>;
24
28
  templates: (string | AutoCompleteFunction)[];
25
29
  cache: LRUCache<string, string[]>;
@@ -27,7 +31,7 @@ interface UnocssAutocomplete {
27
31
  enumerate: () => Promise<Set<string>>;
28
32
  }
29
33
 
30
- declare function createAutocomplete(uno: UnoGenerator): UnocssAutocomplete;
34
+ declare function createAutocomplete(uno: UnoGenerator, options?: AutocompleteOptions): UnocssAutocomplete;
31
35
 
32
36
  declare const shorthands: Record<string, string>;
33
37
  declare const ignoredThemeKeys: string[];
@@ -39,5 +43,6 @@ declare function searchUsageBoundary(line: string, index: number): {
39
43
  end: number;
40
44
  };
41
45
  declare function searchAttrKey(content: string, cursor: number): string | undefined;
46
+ declare function cartesian<T>(arr: T[][]): T[][];
42
47
 
43
- export { AutocompleteTemplateGroup, AutocompleteTemplatePart, AutocompleteTemplateStatic, AutocompleteTemplateTheme, ParsedAutocompleteTemplate, UnocssAutocomplete, createAutocomplete, ignoredThemeKeys, parseAutocomplete, searchAttrKey, searchUsageBoundary, shorthands };
48
+ export { AutoCompleteMatchType, AutocompleteOptions, AutocompleteTemplateGroup, AutocompleteTemplatePart, AutocompleteTemplateStatic, AutocompleteTemplateTheme, ParsedAutocompleteTemplate, UnocssAutocomplete, cartesian, createAutocomplete, ignoredThemeKeys, parseAutocomplete, searchAttrKey, searchUsageBoundary, shorthands };
package/dist/index.mjs CHANGED
@@ -1,5 +1,42 @@
1
- import { escapeRegExp, toArray, uniq } from '@unocss/core';
1
+ import { uniq, escapeRegExp, toArray } from '@unocss/core';
2
2
  import { LRUCache } from 'lru-cache';
3
+ import { Fzf, byStartAsc, byLengthAsc } from 'fzf';
4
+
5
+ function searchUsageBoundary(line, index) {
6
+ let start = index;
7
+ let end = index;
8
+ const regex = /[^\s>"'`;]/;
9
+ while (start && regex.test(line.charAt(start - 1)))
10
+ --start;
11
+ while (end < line.length && regex.test(line.charAt(end)))
12
+ ++end;
13
+ return {
14
+ content: line.slice(start, end),
15
+ start,
16
+ end
17
+ };
18
+ }
19
+ function searchAttrKey(content, cursor) {
20
+ const text = content.substring(0, cursor);
21
+ if (text.match(/(<\w+\s*)[^>]*$/) !== null)
22
+ return text.match(/\S+(?=\s*=\s*["']?[^"']*$)/)?.[0];
23
+ }
24
+ function cartesian(arr) {
25
+ if (arr.length < 2)
26
+ return arr;
27
+ return arr.reduce(
28
+ (a, b) => {
29
+ const ret = [];
30
+ a.forEach((a2) => {
31
+ b.forEach((b2) => {
32
+ ret.push(a2.concat([b2]));
33
+ });
34
+ });
35
+ return ret;
36
+ },
37
+ [[]]
38
+ );
39
+ }
3
40
 
4
41
  const shorthands = {
5
42
  num: `(${[0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 24, 36].join("|")})`,
@@ -27,6 +64,7 @@ function parseAutocomplete(template, theme = {}) {
27
64
  return shorthands[key];
28
65
  });
29
66
  handleGroups(template);
67
+ const fzf = new Fzf(getAllCombination(parts));
30
68
  return {
31
69
  parts,
32
70
  suggest
@@ -70,7 +108,9 @@ function parseAutocomplete(template, theme = {}) {
70
108
  }
71
109
  );
72
110
  }
73
- function suggest(input) {
111
+ function suggest(input, matchType = "prefix") {
112
+ if (input.length && matchType === "fuzzy")
113
+ return fzf.find(input).map((i) => i.item);
74
114
  let rest = input;
75
115
  let matched = "";
76
116
  let combinations = [];
@@ -126,32 +166,43 @@ function parseAutocomplete(template, theme = {}) {
126
166
  return combinations.map((i) => matched + i).filter((i) => i.length >= input.length);
127
167
  }
128
168
  }
129
-
130
- function searchUsageBoundary(line, index) {
131
- let start = index;
132
- let end = index;
133
- const regex = /[^\s>"'`;]/;
134
- while (start && regex.test(line.charAt(start - 1)))
135
- --start;
136
- while (end < line.length && regex.test(line.charAt(end)))
137
- ++end;
138
- return {
139
- content: line.slice(start, end),
140
- start,
141
- end
142
- };
169
+ function getValuesFromPartTemplate(part) {
170
+ if (part.type === "static")
171
+ return [part.value];
172
+ if (part.type === "theme") {
173
+ return part.objects.flatMap((i) => {
174
+ const keys = Object.keys(i).filter((i2) => i2 && i2[0] !== "_");
175
+ for (const key in i) {
176
+ const value = i[key];
177
+ if (value === null || value === void 0)
178
+ continue;
179
+ if (typeof value === "object" && !Array.isArray(value)) {
180
+ const subKeys = getValuesFromPartTemplate({
181
+ type: "theme",
182
+ objects: [value]
183
+ }).map((i2) => `${key}-${i2}`);
184
+ keys.push(...subKeys);
185
+ }
186
+ }
187
+ return keys;
188
+ });
189
+ }
190
+ if (part.type === "group")
191
+ return [...part.values];
192
+ return [];
143
193
  }
144
- function searchAttrKey(content, cursor) {
145
- const text = content.substring(0, cursor);
146
- if (text.match(/(<\w+\s*)[^>]*$/) !== null)
147
- return text.match(/\S+(?=\s*=\s*["']?[^"']*$)/)?.[0];
194
+ function getAllCombination(parts) {
195
+ const values = parts.map((i) => getValuesFromPartTemplate(i));
196
+ const list = uniq(cartesian(values).flatMap((i) => i.join("").replace("-DEFAULT", "")));
197
+ return list;
148
198
  }
149
199
 
150
- function createAutocomplete(uno) {
200
+ function createAutocomplete(uno, options = {}) {
151
201
  const templateCache = /* @__PURE__ */ new Map();
152
202
  const cache = new LRUCache({ max: 5e3 });
153
203
  let staticUtils = [];
154
204
  const templates = [];
205
+ const matchType = options.matchType ?? "prefix";
155
206
  reset();
156
207
  return {
157
208
  suggest,
@@ -193,7 +244,7 @@ function createAutocomplete(uno) {
193
244
  idx = 0;
194
245
  const variantPrefix = input.slice(0, idx);
195
246
  const variantSuffix = input.slice(idx + input.length);
196
- const result = processSuggestions(
247
+ let result = processSuggestions(
197
248
  await Promise.all([
198
249
  suggestSelf(processed),
199
250
  suggestStatic(processed),
@@ -204,6 +255,12 @@ function createAutocomplete(uno) {
204
255
  variantPrefix,
205
256
  variantSuffix
206
257
  );
258
+ if (matchType === "fuzzy") {
259
+ const fzf = new Fzf(result, {
260
+ tiebreakers: [byStartAsc, byLengthAsc]
261
+ });
262
+ result = fzf.find(input).map((i) => i.item);
263
+ }
207
264
  cache.set(input, result);
208
265
  return result;
209
266
  }
@@ -244,6 +301,8 @@ function createAutocomplete(uno) {
244
301
  return i ? [input] : [];
245
302
  }
246
303
  async function suggestStatic(input) {
304
+ if (matchType === "fuzzy")
305
+ return staticUtils;
247
306
  return staticUtils.filter((i) => i.startsWith(input));
248
307
  }
249
308
  async function suggestUnoCache(input) {
@@ -252,12 +311,12 @@ function createAutocomplete(uno) {
252
311
  }
253
312
  function suggestFromPreset(input) {
254
313
  return templates.map(
255
- (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input)
314
+ (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input, matchType)
256
315
  ) || [];
257
316
  }
258
317
  function suggestVariant(input, used) {
259
318
  return uno.config.variants.filter((v) => v.autocomplete && (v.multiPass || !used.has(v))).flatMap((v) => toArray(v.autocomplete || [])).map(
260
- (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input)
319
+ (fn) => typeof fn === "function" ? fn(input) : getParsed(fn)(input, matchType)
261
320
  );
262
321
  }
263
322
  function reset() {
@@ -276,8 +335,8 @@ function createAutocomplete(uno) {
276
335
  }
277
336
  function processSuggestions(suggestions, prefix = "", suffix = "") {
278
337
  return uniq(suggestions.flat()).filter((i) => !!(i && !i.match(/-$/) && !uno.isBlocked(i))).sort((a, b) => {
279
- const numA = +(a.match(/\d+$/)?.[0] || NaN);
280
- const numB = +(b.match(/\d+$/)?.[0] || NaN);
338
+ const numA = +(a.match(/\d+$/)?.[0] || Number.NaN);
339
+ const numB = +(b.match(/\d+$/)?.[0] || Number.NaN);
281
340
  if (!Number.isNaN(numA) && !Number.isNaN(numB))
282
341
  return numA - numB;
283
342
  return a.localeCompare(b);
@@ -285,4 +344,4 @@ function createAutocomplete(uno) {
285
344
  }
286
345
  }
287
346
 
288
- export { createAutocomplete, ignoredThemeKeys, parseAutocomplete, searchAttrKey, searchUsageBoundary, shorthands };
347
+ export { cartesian, createAutocomplete, ignoredThemeKeys, parseAutocomplete, searchAttrKey, searchUsageBoundary, shorthands };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unocss/autocomplete",
3
- "version": "0.53.3",
3
+ "version": "0.53.5",
4
4
  "description": "Autocomplete utils for UnoCSS",
5
5
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
6
6
  "license": "MIT",
@@ -33,10 +33,11 @@
33
33
  "dist"
34
34
  ],
35
35
  "dependencies": {
36
+ "fzf": "^0.5.2",
36
37
  "lru-cache": "^10.0.0"
37
38
  },
38
39
  "devDependencies": {
39
- "@unocss/core": "0.53.3"
40
+ "@unocss/core": "0.53.5"
40
41
  },
41
42
  "scripts": {
42
43
  "build": "unbuild",