✨ feat(vscode): Rust 的 #![…] 内部属性做成「编译属性」卡片
This commit is contained in:
1 parent
2f610e9cb8
commit
04a11eb0b2
14 files changed
+386
-20
No files matched your search
@@ -166,11 +166,11 @@
|
||||
| directive 解析指令 | `terminal` | stage 阶段 | `layers` | run 执行 | `square-terminal` |
|
||||
| copy 复制 | `copy` | setting 设置 | `sliders-horizontal` | label 标签 | `tag` |
|
||||
| health 健康检查 | `activity` | start 启动 | `play` | trait 特质 | `network` |
|
||||
| impl 实现 | `square-function` | module 模块 | `layers` | | |
|
||||
| impl 实现 | `square-function` | module 模块 | `layers` | attribute 编译属性 | `sliders-horizontal` |
|
||||
|
||||
库规则的种类用规则给的图标(`KindBadge.icon`),跟在语言的种类图标后面:SQLAlchemy 数据表 `database`、数据类 `package`、Pydantic 数据模型 `braces`(和类型块同一个图标,颜色不同)、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`、React 组件 `atom`、状态 `toggle-right`、引用 `anchor`、数据源 `download`、派生值 `sigma`、副作用 `zap`、事件处理函数 `mouse-pointer-click`、渲染输出 `layout-template`、serde 可序列化 `braces`、thiserror 错误 `triangle-alert`、axum 接口 `globe`、sqlx 查询 `database`、tauri 桌面命令 `square-function`。数据类写了 `frozen=True` 再加一把锁(`lock`,悬停「不可修改」)。实现块再由规则 `rust.impl` 换成卡片,左边因此有两个「实现」图标。
|
||||
|
||||
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置共用一个,字段、标签共用一个——加库规则用的 14 个(React 的组件、状态、引用、数据源、派生值、副作用、事件处理函数、渲染输出的 8 个也在里面),lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,概念的 `icon` 只能写其中一个,写别的是类型错误。内置种类 → 图标名在 `contract.ts` 的 `KIND_ICONS`(宿主写进概念列表)。webview 给每个名字配一个逐个 import 的 lucide 图标组件(`kindIcons.tsx` 的 `KIND_ICON_COMPONENTS`,打包只带这些,预览脚本约 157 kB);webview 的 `KindIconName` 照契约手抄,`kindIconNames.test.ts` 核对两边一样。规则要新图标就把名字加进这组、在 webview 配上图标,没有按任意名字找图标的路。
|
||||
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置、编译属性共用一个,字段、标签共用一个——加库规则用的 14 个(React 的组件、状态、引用、数据源、派生值、副作用、事件处理函数、渲染输出的 8 个也在里面),lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,概念的 `icon` 只能写其中一个,写别的是类型错误。内置种类 → 图标名在 `contract.ts` 的 `KIND_ICONS`(宿主写进概念列表)。webview 给每个名字配一个逐个 import 的 lucide 图标组件(`kindIcons.tsx` 的 `KIND_ICON_COMPONENTS`,打包只带这些,预览脚本约 157 kB);webview 的 `KindIconName` 照契约手抄,`kindIconNames.test.ts` 核对两边一样。规则要新图标就把名字加进这组、在 webview 配上图标,没有按任意名字找图标的路。
|
||||
- 右上角的图标按钮(原型组件 `Reader / BlockTools`,普通块、属性表块、库规则的卡片共用):只有图标、不带字,名字写在悬停提示和 `aria-label` 里;整排是 `role="group"`(`aria-label="块操作"`)。先是「问 AI」(sparkles 图标,点开 / 收起块下面的问答区,展开时带按下的底色、`aria-expanded`;文件不在工作区文件夹里时没有,见「问 AI」),再是「编辑 / 预览」切换:预览时铅笔「编辑」,编辑时眼睛「预览」、带按下的底色(`--vscode-toolbar-activeBackground`)。按钮宽 24px、高 20px(和种类图标同高),图标 14px、`descriptionForeground` 色;悬停 `--vscode-toolbar-hoverBackground`,键盘聚焦描 1px `--vscode-focusBorder`;Tab 能走到,回车 / 空格切换。
|
||||
- 悬停提示(整个插件的网页只走这一条路,预览和看板共用同一个组件):带提示的元素只写 `data-tooltip="提示"`(名字照旧写在 `aria-label` 里,只有图标的按钮必须有 `aria-label`),页面根上挂一个 `TooltipHost`(`webview-ui/src/components/ui/tooltip.tsx`)统一弹,不用原生 `title`;提示里的换行照样换行(看板摘修复弹框里「迁移」列出的几个文件一行一个);注释、回答里 Markdown 链接的标题(`[文字](网址 "标题")`)也写成 `data-tooltip`。网页的 TSX 里写原生 `title` 属性是 lint 错误(`vscode/eslint.config.mjs`),只有 `iframe` 这种必须带标题的元素例外,组件自己叫 `title` 的属性不算。原因:macOS 上 VS Code 带的 Electron(1.139 是 Electron 43)只在鼠标压着带 `title` 的元素自身边缘时才弹原生提示,停在它里面的图标、文字上不弹(electron/electron#49843,Electron 38 起),种类图标 20px 的框几乎被 16px 的图形占满,基本弹不出来;样子也是系统的。样子照 VS Code 的悬停框:`--vscode-editorHoverWidget-background` 底、`--vscode-editorHoverWidget-border` 边框、`--vscode-editorHoverWidget-foreground` 字、`0 2px 8px --vscode-widget-shadow` 阴影,11px 字,圆角 4px,最宽 22rem;优先在元素上方,上方放不下翻到下方,水平对着元素中间,贴边留 8px,不接鼠标。鼠标停 500ms 才弹,在同一个元素里移动不重新计时,已经弹着时移到另一个带提示的元素上立刻换;移开、移出页面、按下鼠标、按任意键(含 Esc)、滚动就收,收了之后鼠标不离开这个元素不再弹。键盘把焦点移到带提示的元素上(`:focus-visible`)立刻弹,焦点离开就收;提示不是元素自己的名字(`aria-label` 或它的字)时用 `aria-describedby` 挂到元素上,读屏当描述念。
|
||||
- 种类图标和底色按种类区分(颜色见「颜色」);注释块虚线框。
|
||||
@@ -592,7 +592,7 @@
|
||||
```ts
|
||||
export type BlockKind =
|
||||
| 'import' | 'function' | 'class' | 'enum' | 'exception' | 'type' | 'field' | 'const' | 'variable'
|
||||
| 'trait' | 'impl' | 'module' // Rust:特质、实现、模块(属性表)
|
||||
| 'trait' | 'impl' | 'module' | 'attribute' // Rust:特质、实现、模块、编译属性(属性表)
|
||||
| 'if' | 'else' | 'loop' | 'try' | 'catch' | 'finally' | 'return' | 'statement' | 'comment'
|
||||
| 'interpreter' | 'options' | 'case' | 'command' | 'pipeline' // shell:解释器、选项(属性表),分情况、命令、管道(一行译文)
|
||||
| 'directive' | 'stage' | 'run' | 'copy' | 'setting' | 'label' | 'health' | 'start' // Dockerfile:解析指令、阶段、健康检查、启动(属性表),执行、复制、设置、标签(一行译文)
|
||||
@@ -805,8 +805,10 @@ export interface EnumBlock extends BlockShape { kind: 'enum', facts: EnumFacts }
|
||||
export interface ExceptionBlock extends BlockShape { kind: 'exception', facts: ExceptionFacts } // 头部是整个类,留下的方法(含构造函数)等是子块
|
||||
export interface ImportBlock extends BlockShape { kind: 'import', facts: ImportFacts }
|
||||
export interface VariableBlock extends BlockShape { kind: 'const' | 'variable', facts: VariableFacts }
|
||||
/** Rust 的内部属性 `#![…]`:生效的各个属性(cfg_attr 拆开)、cfg_attr 的生效条件(CfgPredicate:not / all / any / 配置项 / 照原文)、写在哪一层(文件顶层、mod、impl)。 */
|
||||
export interface AttributeBlock extends BlockShape { kind: 'attribute', facts: AttributeFacts }
|
||||
export type FactBlock
|
||||
= | FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | ImportBlock | VariableBlock | InterpreterBlock | OptionsBlock
|
||||
= | FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | ImportBlock | VariableBlock | AttributeBlock | InterpreterBlock | OptionsBlock
|
||||
| DirectiveBlock | StageBlock | HealthBlock | StartBlock // 各带自己的 facts:InterpreterFacts、OptionsFacts、DirectiveFacts、StageFacts、HealthFacts、StartFacts
|
||||
/** 带说明(facts.doc)的块。 */
|
||||
export type DocumentedBlock = FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | VariableBlock
|
||||
@@ -943,6 +945,8 @@ export interface DisplayNameReader {
|
||||
export interface SourceDocument {
|
||||
source: string
|
||||
language: LanguageAdapter
|
||||
/** 文件路径(TextDocument.fileName);宿主没给(嵌着的代码、只读一段源码)时没有,按文件名定的说法写成判断不了的那种(Rust 编译属性的作用范围)。 */
|
||||
fileName?: string
|
||||
markers: SourceRange[]
|
||||
/**
|
||||
* 调用的名字(CallFacts.name)的定义是哪种块:宿主问语言扩展定义在哪,用那个文件的语言插件分块认出来。
|
||||
@@ -1816,6 +1820,12 @@ shell 脚本大半是一条条命令,一条命令读成什么取决于命令
|
||||
- `mod 名字 { … }`(`mod_item` 有 `body`)→ module(中文「模块」,图标 `layers`,颜色同阶段)。卡片行:名称、说明(有文档注释才有)、可见性(没写也是本模块,跟结构体一样总有这一行)。身体用和文件顶层同一套规则拆成子块,排在这张卡片下面,不另做一张表把子块塞进某一行。模块自己的文档注释留在模块卡片上,身体里每项自己的文档注释留在那一项上。`mod 名字;` 没有身体,不拆,仍是一条 statement。
|
||||
- `const`(`const_item`)→ const,`static`(`static_item`)→ variable。属性表不另写一套译文,走中文模板里常量 / 变量那一套:名称、说明、访问级别(公开才有,模板里叫「公共」)、类型、值(`static` 的值叫「初始值」)。文件顶层、模块身体、impl 身体同一条路。
|
||||
- `extern "…" { … }`(`foreign_mod_item`)不单独成一块源码。里面的函数签名(`function_signature_item`,没有函数体)是函数卡片,跟普通函数一样有名称、说明、可见性、参数、返回,作为所在这一层(模块、文件顶层或 impl)的子块。
|
||||
- 内部属性 `#![…]`(`inner_attribute_item`)→ attribute(中文「编译属性」,图标 `sliders-horizontal`,颜色同常量词性的蓝 `--cr-constant`:暗 #4fc1ff / 亮 #0070c1,和特质的声明关键字蓝、选项的黄分开)。它写给所在的 crate 或模块,例如 Tauri `main.rs` 开头的 `#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]`。语言插件把 `cfg_attr(条件, a, b)` 拆成生效条件和 a、b 两个属性(`AttributeFacts`),别的属性就是它自己一个;`cfg_attr` 只写了条件、没写属性的照一个属性算。卡片行(有才显示):
|
||||
- 属性:属性名连参数照原文着色(`windows_subsystem = "windows"`、`allow(dead_code, unused_imports)`)。`cfg_attr` 里写了几个属性时值是「N 个」,下面一张「属性 | 说明」表,每个属性一行(认不得的说明写「—」),不再另起「说明」一行。
|
||||
- 生效条件:只有 `cfg_attr` 有。`debug_assertions` 是「调试构建」,`test` 是「测试构建」,`target_os = "windows"` 是「目标系统 windows」,`feature = "x"` 是「启用特性 x」,`not(x)` 是「非 x」,`all(…)` 用「并且」、`any(…)` 用「或者」连起来,套在别的条件里、有两项以上的加括号(「unix 并且 非 (测试构建 或者 启用特性 mock)」);别的配置项、认不得的写法照原文。「非」「并且」「或者」按 operator 着色,特性名、系统名按 string。
|
||||
- 作用范围(按 keyword 着色):写在文件顶层时,crate 根文件是「整个 crate」,别的文件是「本模块」,宿主没给文件名(`SourceDocument.fileName`)时是「所在模块」;写在 `mod` 身体里是「本模块」,`impl` 身体里是「本 impl 块」。crate 根文件按 Cargo 默认的认法(`rules/rustTree.ts` 的 `isCrateRoot`):`main.rs`、`lib.rs`,`src/bin/` 下的 `.rs`,包根下 `examples/`、`tests/`、`benches/` 里直接放的 `.rs`(`src/` 下同名目录里的是模块)。
|
||||
- 说明(按 comment 着色,列出的名字按 variable):认得的属性才有。`windows_subsystem = "windows"`「在 Windows 上编译为图形界面程序(windows 子系统),启动时不弹出控制台窗口」,`= "console"`「在 Windows 上编译为控制台程序(console 子系统),启动时打开控制台窗口」;`allow` / `warn` / `deny` / `forbid`「编译检查(lint)级别设为放行 / 警告 / 报错 / 强制报错(内层不能再放宽):」后面列括号里的各项;`feature`「启用实验特性(需 nightly 编译器):」后面列名字;`no_std`「不链接标准库 std,只使用核心库 core」;`no_main`「不使用标准的 main 入口函数,程序入口另行提供」;`recursion_limit = "N"`「宏展开等编译期递归的深度上限设为 N(默认 128)」。
|
||||
- 挂在函数、结构体上的外部属性 `#[…]` 不变:算进那一项的头部,`derive`、库规则照旧。
|
||||
- 其余(`let`、宏调用)→ statement。不拆函数体。模块身体里宏调用后面多出来的那个空 `;`(`empty_statement`)不成块。
|
||||
|
||||
### 属性表信息(facts)从哪来
|
||||
@@ -1832,7 +1842,7 @@ shell 脚本大半是一条条命令,一条命令读成什么取决于命令
|
||||
- Shell:解释器取 `#!` 后面的整句,第一个词是 `env` 的是按 PATH 找、程序名是它后面那个词,否则是写死的路径、程序名是路径最后一节。选项:每个开关一项,单字母换成 bash 的长名字(e → errexit、u → nounset、x → xtrace……),`-o 名字` 照写,`-` 开、`+` 关。变量:`variable_assignment` 的 name / value,声明里没给值的只有 name。函数:name 是函数名,doc 是紧贴在上面的注释;`positional` 是主体里(不进嵌套的函数)用到的 `$N` / `${N}` / `${N:-…}`,照位置排,`local x="$N"`、`x=$N` 给它起名字;`prints` 是没重定向到别处的 `echo` / `printf`(进 if、循环、分情况,不进 `$(…)`、管道、嵌套的函数);`captured` 看同一文件里有没有 `$(函数名 …)`;`returns` 是主体里的 `return` 命令。
|
||||
- Dockerfile:解析指令是每行注释 `# 名字=值` 的名字(小写)和值;阶段取 `image_spec` 的 name / tag / digest 字段、`as` 字段(image_alias),stage 看镜像名是不是前面某个阶段的名字(不分大小写),选项是 `param`、`mount_param` 节点的原文 `--名字=值`(值在语法树里没有单独的节点,按原文拆,记下值从哪开始);变量是 `env_pair` 的 name / value、`arg_instruction` 的 name / default;健康检查的 none 看有没有 NONE,检查命令是里面的 `cmd_instruction`;数组写法取 `json_string_array`,shell 写法取 `shell_command` 里各个 `shell_fragment`(`line_continuation` 不算),只有一个 `<<EOT` 的取它下面 `heredoc_block` 从第一行到最后一行。
|
||||
- Python 变量:`assignment` 的 left / type / right,说明是紧跟在后面的属性 docstring 的字符串节点(见上面代码块树规则);right 是 `dictionary` 时 entries 是它的具名子节点(注释除外):`pair` 的 key / value(value 又是 `dictionary` 时连它的 entries)、`dictionary_splat` 里的表达式。
|
||||
- Rust:函数(含 `extern` 块里没有函数体的 `function_signature_item`)的 name / parameters / return_type,`async` 只在写了时填;`self` 的类型节点是整个接收者(`&mut self`)。结构体、枚举、特质、实现、模块的名称、说明、可见性见「Rust」(模块用 ClassFacts:name、doc、access)。`const`、`static` 的名称、类型、值是 `const_item` / `static_item` 的 name / type / value(VariableFacts 的一项 declarator),可见性同别的项:`pub` 写在 `facts.access`,其余写在块的 `access` 属性上。字段和变体携带的数据由中文直译读语法树,不另做 facts。
|
||||
- Rust:函数(含 `extern` 块里没有函数体的 `function_signature_item`)的 name / parameters / return_type,`async` 只在写了时填;`self` 的类型节点是整个接收者(`&mut self`)。结构体、枚举、特质、实现、模块的名称、说明、可见性见「Rust」(模块用 ClassFacts:name、doc、access)。`const`、`static` 的名称、类型、值是 `const_item` / `static_item` 的 name / type / value(VariableFacts 的一项 declarator),可见性同别的项:`pub` 写在 `facts.access`,其余写在块的 `access` 属性上。字段和变体携带的数据由中文直译读语法树,不另做 facts。编译属性(AttributeFacts):`inner_attribute_item` 里的 `attribute` 是名字(路径照写)后面跟 `= 值`(字符串取 `string_content`)或 `token_tree`;`token_tree` 按顶层逗号切成各段(每段是几个词),`cfg_attr` 的第一段读成 CfgPredicate(`all` / `any` / `not` 带括号的、只有一个名字或 `名字 = 值` 的配置项,别的照原文),后面各段同样读成名字、值、括号里各项的原文;placement 看 `#![…]` 的上一层(`source_file`)或上两层(`mod_item` / `impl_item` 的 `declaration_list`)。
|
||||
|
||||
## 中文模板直译
|
||||
|
||||
@@ -2124,7 +2134,7 @@ vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vit
|
||||
- 语言插件(`languages/bash.test.ts`,真实解析):`fixtures/fetchRuntime.sh` 的代码块树(种类、行号、层级,同原型 `SHELL_BLOCKS`,原型略去的行也在);elif / else 展平、while / for 的主体是子块、条件串和命令串;解释器(env、写死的路径)、选项(连写拆开、`-o`、`+`、`set --` 不是)、变量(local 一句几个、没值的、readonly 和顶层全大写写死的是常量)、函数(按位置的参数和名字、打印的几句、被 `$(…)` 拿去用、写了的 return);命令的词(写死的文字、有展开的没有)和重定向;同一文件里的名字(函数、赋值、local、for、read、展开,第几个参数不算);行尾 `# @Comment`。
|
||||
- 中文直译(`translators/bashZh.test.ts`,走 `readBlockViews`):`fetchRuntime.sh` 整份逐行同 `FETCH_READING`(即上面「Shell 脚本」的样例);展开(长度、去头去尾、截取、没设时、替换、特殊变量)、条件、重定向、命令规则(不认识的选项照抄、没有规则的照抄)、`&&` / `||`、遍历、`$(dirname "$0")`、常量;字符串色 / 变量色 / 常量色;规则出错照抄命令并报告。
|
||||
- 宿主:命令规则按语言、注册顺序给(`registry.test.ts`),shellscript 的 document 消息带命令规则读的译文、shell 的种类名、同一文件里的名字(`editor/documentMessage.test.ts`),跳到之前最近的定义、定义处算写入(`localSymbols.test.ts`)。
|
||||
- Rust(`languages/rust.test.ts`、`rules/rustLibs.test.ts`,真实解析):`fixtures/rustLang.rs` 的导入、结构体、枚举、实现(方法挂在方法表的子行上)、函数(异步、分情况)、特质逐行同预览;给结构体加一个字段,字段表那一行标成新增。`fixtures/rustComments.rs` 开头连着写的 `//` 合成一块注释,预览是去掉 `//` 的三段 Markdown;隔着空行、语句的注释各是一块,行尾 `//` 不单独成块,`//!` 连着几行合成一块,`///` 并进下一个函数当说明,`mod`、`impl` 身体里同一套。`fixtures/rustLibs.rs` 的 serde(改名、不输出、字段名改驼峰)、thiserror(展示、透传来源)、axum(方法、路径、路径参数)、sqlx、tauri 逐行同预览;没导入 serde 时 derive 仍是结构体;改 axum 的路径,路径那一格标成改过。`axum::routing` 这种来源算导入了 axum。
|
||||
- Rust(`languages/rust.test.ts`、`rules/rustLibs.test.ts`,真实解析):`fixtures/rustLang.rs` 的导入、结构体、枚举、实现(方法挂在方法表的子行上)、函数(异步、分情况)、特质逐行同预览;给结构体加一个字段,字段表那一行标成新增。`fixtures/rustComments.rs` 开头连着写的 `//` 合成一块注释,预览是去掉 `//` 的三段 Markdown;隔着空行、语句的注释各是一块,行尾 `//` 不单独成块,`//!` 连着几行合成一块,`///` 并进下一个函数当说明,`mod`、`impl` 身体里同一套。编译属性:Tauri `main.rs` 的 `cfg_attr` 卡片逐行(属性的着色、「非 调试构建」、「整个 crate」、windows 子系统的说明),别的文件顶层的 `allow` 是本模块、列出 lint 名,认不得的属性没有说明、不知道文件名时是所在模块,`cfg_attr` 里几个属性是一张表、套着的条件加括号,`isCrateRoot` 的几种路径。`fixtures/rustLibs.rs` 的 serde(改名、不输出、字段名改驼峰)、thiserror(展示、透传来源)、axum(方法、路径、路径参数)、sqlx、tauri 逐行同预览;没导入 serde 时 derive 仍是结构体;改 axum 的路径,路径那一格标成改过。`axum::routing` 这种来源算导入了 axum。
|
||||
- Dockerfile:
|
||||
- 语言插件(`languages/dockerfile.test.ts`,真实解析):两份 luffy 的 Dockerfile 的代码块树(同原型,后端的三行注释是阶段说明);并块(紧挨着才并,空行、注释、别的指令隔开,COPY 要从同一个地方,ADD、heredoc 的 COPY 不并,第一个 FROM 之前的是顶层);ONBUILD 和各种设置;facts(解析指令、阶段的名称 / 序号 / 说明 / 标签 / 摘要 / 平台 / 接着前面的阶段、构建参数和环境变量并块、健康检查两种写法和 NONE、启动);嵌着的 shell 的几段(续行去掉、heredoc 正文);数组写法的命令(VOLUME、SHELL 的数组不算);同一文件里的名字(阶段名在 --from、--mount 的 from、FROM 里,变量);中文名;着色。
|
||||
- 中文直译(`translators/dockerfileZh.test.ts`,走 `readBlockViews`,RUN 交给 bash 的插件):两份 luffy 的 Dockerfile 整份逐行同 `NEXT_READING`、`BACKEND_READING`;阶段的各种写法、变量的展开和注解、RUN 的选项 / 数组写法 / heredoc / 一条循环 / 带重定向的一串、没有 shell 插件时照抄、复制和 ADD、设置、健康检查、启动;阶段名和表里变量名的源码下标、RUN 的源码按 shell 着色。
|
||||
|
||||
Reference in new issue
Block a user