UnoCSS Autocomplete 完全指南:为原子化 CSS 打造智能补全
【免费下载链接】unocssThe instant on-demand atomic CSS engine.项目地址: https://gitcode.com/GitHub_Trending/un/unocss
UnoCSS 的 Autocomplete 是一套面向智能提示的可定制机制,它内置于 Playground 与 VS Code 扩展,让开发者输入诸如bg-、m-这类前缀时就能即时获得精准的补全建议。本文以 UnoCSS 仓库中 autocomplete 配置文档 与 autocomplete 工具文档 为主线,结合 @unocss/autocomplete 的源码实现与测试用例,系统讲解其配置结构、DSL 语法、shorthands、extractors,以及如何在自定义规则中声明补全模板,帮助你为项目配置出贴合主题体系与业务习惯的智能提示。
Autocomplete 是什么,运行在哪里
Autocomplete 是 UnoCSS 的"智能建议"能力:当你在 Playground 或 VS Code 扩展 中输入候选类名时,它负责解析你的输入并返回可用的补全列表。这套能力由独立包@unocss/autocomplete提供(源码见 packages-engine/autocomplete/src),核心入口是createAutocomplete(uno, options),返回一个包含suggest、suggestInFile、templates、enumerate等能力的对象(见 create.ts)。
从集成侧看,语言服务器(Language Server)在收到补全请求时,正是通过createAutocomplete(ctx.uno, { matchType, throwErrors: false })构建补全器,然后调用suggest/suggestInFile返回结果(见 completion.ts)。这意味着你配置的 autocomplete 模板会直接作用于编辑器中的补全弹窗。
配置总览:三个核心字段
在uno.config.ts的根配置中加入autocomplete字段即可开启定制:
autocomplete: { templates: [ // 主题推断(theme inferring) 'bg-$color/<opacity>', // 简写(short hands) 'text-<font-size>', // 逻辑 OR 组 '(b|border)-(solid|dashed|dotted|double|hidden|none)', // 常量 'w-half', ], shorthands: { // 等价于 `opacity: "(0|10|20|30|40|50|60|70|90|100)"` 'opacity': Array.from({ length: 11 }, (_, i) => i * 10), 'font-size': '(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)', // 覆盖内置简写 'num': '(0|1|2|3|4|5|6|7|8|9)', }, extractors: [ // ...extractors ], }三个字段的分工如下:
templates:使用一套简单 DSL 指定补全建议(详见下一节)。它可以接收字符串模板,也可以接收返回建议列表的函数(AutoCompleteFunction)。shorthands:简写名到模板的映射表。当值为数组时,会被自动拼成一个逻辑 OR 组(即用|连接并包上())。extractors:负责从源码上下文中"拾取"可能正在输入的类,并把类名风格的建议转换为当前场景下正确的格式(例如将 attributify 属性值还原为可替换的形式)。
对应类型定义见 core 的 UserConfig,其中明确了:templates可为Arrayable<AutoCompleteFunction | AutoCompleteTemplate>,shorthands的 value 可为string | string[]。
在配置合并层面,config.ts 会把所有 preset 与用户配置中的templates去重合并、按order排序extractors,并通过mergeAutocompleteShorthands合并shorthands。因此,各 preset 自带的内置补全与你的自定义模板是共存而非互斥的。
templates DSL 详解
模板 DSL 的核心语法由parseAutocomplete实现(见 parse.ts)。解析器会把模板拆解为三种节点类型(见 types.ts):
- static(静态文本):如
m-、w-,按字面匹配; - group(逻辑 OR 组):以
|分隔、被()包裹的候选值; - theme(主题推断):以
$开头,指向 theme 对象的某个属性。
(...|...):逻辑 OR 组
用|分隔一组候选项,当输入命中其中某些项时,这些项会被作为建议返回。例如模板(border|b)-(solid|dashed|dotted|double|hidden|none):
- 输入
b-do→ 建议b-dotted、b-double
这一行为与 autocomplete-parse.test.ts 的断言完全一致:解析器会把(border|b)拆成values: ['border', 'b']的 group 节点,且支持可空组(如(-suffix|))来生成带后缀与不带后缀的完整候选。
<...>:内置 shorthands
尖括号内的名称是内置简写,当前支持:
<num>:(0|1|2|3|4|5|6|8|10|12|24|36)<percent>:(0|10|20|30|40|50|60|70|80|90|100)<percentage>:(10%|20%|30%|40%|50%|60%|70%|80%|90%|100%)<directions>:(x|y|t|b|l|r|s|e)
这些内置定义见 parse.ts。解析时,/<\w+>/g会先被替换为对应的正则片段;若引用了未定义的简写名,会抛出AutocompleteParseError(提示Unknown template shorthand: <key>)。
例如模板m-<num>:
- 输入
m-→ 建议m-1、m-2、m-3…
而(m|p)<directions>-<num>则会组合出pt-0、pt-1、px-2、mb-4这类完整候选(见 autocomplete-parse.test.ts)。
$...:主题推断(theme inferring)
以$开头引用主题对象,例如$colors会枚举 theme 中colors对象的所有属性名。主题可以多级嵌套,例如$animation.keyframes会深入 theme 的animation.keyframes结构。
- 模板
text-$colors:输入text-r→ 建议text-red、text-rose…
解析实现(parse.ts)会把$后的路径按.切分逐层深入 theme 对象,并过滤掉DEFAULT键(ignoredThemeKeys = ['DEFAULT'])以及_开头的内部键。同时支持$路径用|并列(如$colors|$spacing)以合并多个主题分支的候选。
多模板组合
templates数组可同时传入多个模板,补全时取并集。例如:
- 模板:
['(border|b)-<num>', '(border|b)-<directions>-<num>'] - 输入
b-→ 建议b-x、b-y、b-1、b-2… - 输入
b-x-→ 建议b-x-1、b-x-2…
在内部,所有模板会被预解析并缓存(create.ts),然后与静态规则、动态规则、shortcuts、variants 中声明的模板一起参与建议生成(见下文"在规则中声明 autocomplete")。
shorthands:自定义与覆盖简写
shorthands让你用语义化名字包装一组候选值,并在模板中通过<name>引用:
shorthands: { // 数组会被自动转换为逻辑 OR 组 'opacity': Array.from({ length: 11 }, (_, i) => i * 10), 'font-size': '(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)', // 覆盖内置简写 'num': '(0|1|2|3|4|5|6|7|8|9)', }要点:
- value 为字符串时直接作为模板片段使用;为数组时等价于
(<a>|<b>|…)的 OR 组。例如上面的opacity数组等价于字符串(0|10|20|30|40|50|60|70|90|100)。 - 自定义简写会覆盖同名内置简写:例如把
num重定义为(0|1|2|3|4|5|6|7|8|9),那么所有模板中出现的<num>都会使用新的候选集。源码层面,parse.ts 在解析时通过{ ...shorthands, ...extraShorthands }合并,后者的优先级更高。
extractors:从代码上下文拾取候选类
extractors是补全能力的进阶开关:它们读取光标所在位置的文件内容,判断用户正在输入什么(例如处于某个标签属性、某个 class 属性或某段自由文本内),返回"已提取的输入片段"以及如何把补全建议转换/回填的规则。
AutoCompleteExtractor的核心接口包含(见 core 类型 附近):
extract({ content, cursor }):返回{ extracted, transformSuggestions?, resolveReplacement? }或null;resolveReplacement(suggestion):将建议转换为{ start, end, replacement }用于精确替换光标附近文本;transformSuggestions(suggestions):把类名风格的建议改写为当前输入场景下的正确格式。
在suggestInFile中,补全器会先尝试按 extractor 提取输入(create.ts);若没有 extractor 命中,则退回到通用边界识别searchUsageBoundary,它会扫描光标前后,识别class=""、className=""、@apply等上下文并排除引号、空白与;等分隔符(utils.ts)。
实例:attributify 自动补全提取器
官方在 preset-attributify/src/autocomplete.ts 中实现了一个典型的 extractor,它处理的是 attributify 风格(如<div bg="blue-500">)的补全:
- 先用正则定位光标所在的 HTML 元素及属性区间;
- 跳过
class/className/:class这类常规属性; - 若光标在属性名上,则把属性名当作待补全输入(例如输入
bg); - 若光标在属性值上,则将
属性名-拼到值前作为补全输入,并利用transformSuggestions把bg-blue-500这类类名风格建议还原为blue-500属性值风格,同时resolveReplacement保证替换范围精确命中。
这就是为什么在 Playground 的 attributify 模式下输入<div b时,可以补出bg、border等属性名;输入<div bg="b时,可以补出blue-500、black等值。这也是原文档建议"参考 attributify extractor 实现自定义提取器"的最佳范本。
在规则与 shortcuts 中声明 autocomplete(meta 方式)
除全局autocomplete.templates外,静态规则天然可补全:只要规则是静态字符串(如['flex', { display: 'flex' }]),createAutocomplete会将其键名收集进staticUtils,无需任何配置即可补出flex(create.ts)。
动态规则则需要通过第三个元素meta声明autocomplete:
rules: [ [ /^m-(\d)$/, ([, d]) => ({ margin: `${d / 4}rem` }), { autocomplete: 'm-<num>' }, // <-- 这里 ], ]在reset()中,补全器会从以下来源收集全部模板(create.ts):
uno.config.autocomplete.templates(全局模板);- 所有动态规则的
meta.autocomplete; - 所有 shortcuts 的
meta.autocomplete; - 所有 variants 的
autocomplete。
因此快捷方式也可以携带补全声明。core的类型定义(types.ts)确认了 rules、shortcuts、variants 均支持autocomplete?: Arrayable<AutoCompleteTemplate>。
以 preset-wind3 的动画规则为例,仓库源码中大量使用这一机制(animation.ts):
{ autocomplete: ['animate-keyframes-$animation.keyframes', 'keyframes-$animation.keyframes'] } { autocomplete: 'animate-$animation.keyframes' } { autocomplete: ['animate-duration', 'animate-duration-$duration'] } { autocomplete: [ 'animate-(fill|mode|fill-mode)', 'animate-(fill|mode|fill-mode)-(none|forwards|backwards|both|inherit|initial|revert|revert-layer|unset)', 'animate-(none|forwards|backwards|both|inherit|initial|revert|revert-layer|unset)', ], }可以看到,把meta.autocomplete与全局templates结合,是给现有预设规则"加餐"补全的标准姿势。
补全行为与内部机制
建议来源与排序
一次suggest(input)会并行收集四路建议(create.ts):
suggestSelf:直接尝试把输入作为合法 token 解析(uno.parseToken),命中则原样返回;suggestStatic:从静态规则 / 字符串 shortcuts 中按前缀过滤;suggestUnoCache:从生成器的已缓存 token 中匹配;suggestFromTemplates:所有已解析模板的suggest结果,加上函数型模板。
随后会做去重、过滤以-结尾的残缺建议、过滤blocklist中被屏蔽的类(uno.isBlocked),并按"含数字的排后面 +Intl.Collator数值感知排序"输出(create.ts)。测试 autocomplete.test.ts 专门验证了被 blocklist 屏蔽的规则不会出现在建议中。
matchType:prefix 与 fuzzy
createAutocomplete支持两个选项(types.ts):
matchType: 'prefix' | 'fuzzy'(默认'prefix'):前缀匹配,或基于 fzf 的模糊匹配;throwErrors: boolean(默认true):模板解析出错时是否直接抛出。
fuzzy 模式下会使用fzf库对结果做打分排序(create.ts),并有独立的 autocomplete-fuzzy.test.ts 覆盖。语言服务器在调用时传入了throwErrors: false,因此某个模板写错不会让整个编辑器补全崩溃。
suggestInFile 与 enumerate
suggestInFile(content, cursor):面向编辑器光标场景,先尝试 extractor,再退回到边界识别,返回带resolveReplacement的结果(create.ts);enumerate():枚举从aa到zz等组合的完整建议集合,通常用于生成完整的候选清单或文档统计(create.ts)。
此外建议结果带有 LRU 缓存(max: 5000)与模板解析缓存,保证编辑过程中的高频补全请求足够流畅。
实践建议与注意事项
- 优先复用内置简写:
<num>、<percent>、<percentage>、<directions>已覆盖大部分数值/方向类补全需求,先组合再自定义。 - 主题推断优先于硬编码:
$colors、$duration、$animation.keyframes这类引用会随 theme 扩展自动生效,比写死候选列表更易维护。 - 数组即 OR 组:在
shorthands中使用数组(如Array.from({ length: 11 }, (_, i) => i * 10))可避免手写一长串|。 - 自定义 extractor 参考 attributify 实现:如果你为某种模板语法(如 Vue 指令、JSX 属性)做补全,直接参考 preset-attributify/src/autocomplete.ts 的
extract/transformSuggestions/resolveReplacement三段式写法。 - 模板写错会报错:全局配置下默认
throwErrors: true,解析失败的模板会在启动时抛出带模板原文的AutocompleteParseError,方便你定位;编辑器侧则关闭了抛错以免影响体验。 - blocklist 优先级最高:被 blocklist 的类即使命中模板也不会出现在建议里。
延伸阅读
- 配置总览:docs/config/index.md
- VS Code 扩展接入:docs/integrations/vscode.md
- 源码实现:packages-engine/autocomplete/src、autocomplete 测试
- Attributify 提取器参考:packages-presets/preset-attributify/src/autocomplete.ts
- 内置规则 meta 示例:packages-presets/preset-wind3/src/rules/animation.ts
【免费下载链接】unocssThe instant on-demand atomic CSS engine.项目地址: https://gitcode.com/GitHub_Trending/un/unocss
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考