📝 docs(vscode): 代码阅读规格加 Dockerfile 一节(嵌着的代码、并块、选项表),契约、命令规则、解析运行时、代码块树规则、颜色、代码位置、测试跟着补上
This commit is contained in:
1 parent
d4ec0e7de4
commit
8776502f27
1 file changed
+149
-18
@@ -14,7 +14,7 @@
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 流程 | 源码 →(tree-sitter)语法树 → 通用代码块树 → 译文 |
|
| 流程 | 源码 →(tree-sitter)语法树 → 通用代码块树 → 译文 |
|
||||||
| 第一版翻译 | 只做中文模板直译;AI 解读以后作为另一个翻译插件加 |
|
| 第一版翻译 | 只做中文模板直译;AI 解读以后作为另一个翻译插件加 |
|
||||||
| 第一版语言 | TypeScript / TSX(含 JavaScript / JSX)、Python、Shell 脚本(bash / sh / zsh) |
|
| 第一版语言 | TypeScript / TSX(含 JavaScript / JSX)、Python、Shell 脚本(bash / sh / zsh)、Dockerfile |
|
||||||
| 扩展方式 | 写代码注册:语言插件、翻译插件都实现约定接口,通过本插件 `activate()` 导出的 API 注册;内置实现也走同一个 API |
|
| 扩展方式 | 写代码注册:语言插件、翻译插件都实现约定接口,通过本插件 `activate()` 导出的 API 注册;内置实现也走同一个 API |
|
||||||
| 多种翻译怎么选 | 预览顶部筛选栏最右边的下拉框,整个文件一起换,按语言记住选择;只有一种时不显示 |
|
| 多种翻译怎么选 | 预览顶部筛选栏最右边的下拉框,整个文件一起换,按语言记住选择;只有一种时不显示 |
|
||||||
| 块筛选 | 预览顶部钉住一条筛选栏,按块的属性(种类、访问级别、异步……)筛;属性由语言插件按和语言无关的 id 报(通用的由宿主从 facts 推),名字由翻译插件起,webview 不认识任何语言;筛选整个工作区共用、记住 |
|
| 块筛选 | 预览顶部钉住一条筛选栏,按块的属性(种类、访问级别、异步……)筛;属性由语言插件按和语言无关的 id 报(通用的由宿主从 facts 推),名字由翻译插件起,webview 不认识任何语言;筛选整个工作区共用、记住 |
|
||||||
@@ -44,6 +44,9 @@
|
|||||||
| 调用与构造 | 实参列表跨了几行写的调用不挤成一长行:写成「[等待 ]新建 / 调用 被调用的名字 · N 个参数」,实参摊成一张表「参数 \| 值」(按名字传的写名字,按位置传的写「第 N 个」,`*x` / `...x` 写「展开」,`**x` 写「展开关键字」);一行写完的调用照旧一行译文。实参的值又是跨几行写的调用时,值那一格写它的一行、它的表套在格子里,一层层往下套;埋在式子里、后面还跟着东西的(`X(\n…\n).model_dump()`)括起来「(新建 X · 3 个参数).model_dump()」。新建还是调用:TypeScript 看有没有 `new`;Python 写法一样,查被调用的名字的定义,是类就是「新建」,是函数、查不到都是「调用」,先按调用画、查到了再刷新。表在变量 / 常量属性表的初始值 / 值那一行下面(能折叠),在返回、语句这些一行译文的块里排在那一行下面。语言插件报结构,翻译插件定用词,webview 只画表。见「调用与构造」 |
|
| 调用与构造 | 实参列表跨了几行写的调用不挤成一长行:写成「[等待 ]新建 / 调用 被调用的名字 · N 个参数」,实参摊成一张表「参数 \| 值」(按名字传的写名字,按位置传的写「第 N 个」,`*x` / `...x` 写「展开」,`**x` 写「展开关键字」);一行写完的调用照旧一行译文。实参的值又是跨几行写的调用时,值那一格写它的一行、它的表套在格子里,一层层往下套;埋在式子里、后面还跟着东西的(`X(\n…\n).model_dump()`)括起来「(新建 X · 3 个参数).model_dump()」。新建还是调用:TypeScript 看有没有 `new`;Python 写法一样,查被调用的名字的定义,是类就是「新建」,是函数、查不到都是「调用」,先按调用画、查到了再刷新。表在变量 / 常量属性表的初始值 / 值那一行下面(能折叠),在返回、语句这些一行译文的块里排在那一行下面。语言插件报结构,翻译插件定用词,webview 只画表。见「调用与构造」 |
|
||||||
| Shell 脚本 | `.sh`、`.bash`、`.zsh` 和首行写着 shell 解释器的无扩展名文件(VS Code 的 shellscript):读成解释器、选项(`set -euo pipefail` 每个开关一行)、变量 / 常量、分情况(短的一张「情况 \| 做什么」表,长的每个情况一块子块)、函数(参数按位置「位置 \| 函数里叫」、输出、返回)、命令、管道、条件、遍历;`$1` → 第 1 个参数,`${x:-d}` → x(没设就用 d),`$(cmd)` → 执行〔…〕的输出。语言插件在 tree-sitter-bash 上报块和事实,翻译插件定用词,webview 不认识 shell。同一文件里的函数、变量由语言插件认,⌘ / Ctrl + 点击、单击高亮照常。见「Shell 脚本」 |
|
| Shell 脚本 | `.sh`、`.bash`、`.zsh` 和首行写着 shell 解释器的无扩展名文件(VS Code 的 shellscript):读成解释器、选项(`set -euo pipefail` 每个开关一行)、变量 / 常量、分情况(短的一张「情况 \| 做什么」表,长的每个情况一块子块)、函数(参数按位置「位置 \| 函数里叫」、输出、返回)、命令、管道、条件、遍历;`$1` → 第 1 个参数,`${x:-d}` → x(没设就用 d),`$(cmd)` → 执行〔…〕的输出。语言插件在 tree-sitter-bash 上报块和事实,翻译插件定用词,webview 不认识 shell。同一文件里的函数、变量由语言插件认,⌘ / Ctrl + 点击、单击高亮照常。见「Shell 脚本」 |
|
||||||
| 命令规则 | 第五种插件:按命令名注册(`rm`、`curl`、`tar`……),把一条命令连同选项读成一句(「删除 dest(连同里面的东西、不提示)」);规则不认识的选项照抄在后面,没有规则的命令照抄命令名,重定向由翻译插件统一读。见「命令规则」 |
|
| 命令规则 | 第五种插件:按命令名注册(`rm`、`curl`、`tar`……),把一条命令连同选项读成一句(「删除 dest(连同里面的东西、不提示)」);规则不认识的选项照抄在后面,没有规则的命令照抄命令名,重定向由翻译插件统一读。见「命令规则」 |
|
||||||
|
| Dockerfile | `Dockerfile`、`Dockerfile.*`、`*.dockerfile`、`Containerfile`:读成解析指令、阶段(每个 FROM 一个,后面的指令是子块;阶段名能跳过去)、执行(RUN 按命令拆,只有一条命令时一行)、复制、变量(构建参数 / 环境变量)、设置、标签、健康检查、启动;连着写的 ENV / ARG / COPY(从同一个地方)/ LABEL 并成一块加一张表。语法包取自 npm 包 tree-sitter-wasm。见「Dockerfile」 |
|
||||||
|
| 嵌着的代码 | 一种语言里嵌着另一种语言的代码(Dockerfile 的 RUN 是 shell):语言插件报出是哪几段,宿主交给那种语言的语言插件、翻译插件、命令规则读,只解析这几段;外层的翻译插件只问读出来的样子,不再读一遍。见「Dockerfile」 |
|
||||||
|
| 选项多的命令 | 命令规则认出的选项有 4 个以上、或同一个选项写了几遍时,读成「… · N 个选项」加一张「选项 \| 意思」表,写了几遍的并成一行;少的照旧写在括号里 |
|
||||||
|
|
||||||
## 用户可见行为
|
## 用户可见行为
|
||||||
|
|
||||||
@@ -115,7 +118,7 @@
|
|||||||
|
|
||||||
### 预览(代码块树)
|
### 预览(代码块树)
|
||||||
|
|
||||||
- 每一句是一块:导入、函数、类、枚举、异常、类型、字段、常量、变量、条件、否则、循环、尝试、捕获、最终、返回、语句、注释;Shell 脚本另有解释器、选项、分情况、命令、管道(见「Shell 脚本」)。
|
- 每一句是一块:导入、函数、类、枚举、异常、类型、字段、常量、变量、条件、否则、循环、尝试、捕获、最终、返回、语句、注释;Shell 脚本另有解释器、选项、分情况、命令、管道(见「Shell 脚本」);Dockerfile 另有解析指令、阶段、执行、复制、设置、标签、健康检查、启动(见「Dockerfile」)。
|
||||||
- 嵌套的块放在父块下面、往右缩进;每一层左边一条竖线(颜色 `--vscode-editorIndentGuide-background`)。
|
- 嵌套的块放在父块下面、往右缩进;每一层左边一条竖线(颜色 `--vscode-editorIndentGuide-background`)。
|
||||||
- 块的组成(从左到右):折叠箭头(只有有子块的块才显示,其余占位对齐)、种类图标、内容、右上角一排图标按钮。
|
- 块的组成(从左到右):折叠箭头(只有有子块的块才显示,其余占位对齐)、种类图标、内容、右上角一排图标按钮。
|
||||||
- 种类图标(原型 `kindBadge`、画板「Prototype / 块种类图标」):块左边只画图标、不带字,和一行字等高(20px 的框,图形 16px,lucide 的线宽 2.2),颜色是种类色;种类的名字(翻译插件给 `kind` 值起的,见「筛选」;库规则的种类是规则给的 `badge.label`)写在悬停提示(见下「悬停提示」,同块右上角按钮)和 `aria-label` 里,`role="img"`。和折叠箭头、内容同一行顶对齐。每种内置种类由插件定图标、颜色、名字:图标从插件定义的一组图标里取(见下),内置种类 → 图标只在 webview 的一处(`kindIcons.tsx` 的 `KIND_ICONS`);颜色在 `codeReader.css` 的 `--cr-kind-*`(见「颜色」);名字由翻译插件给。同一类语义共用一个颜色,图标区分具体是哪种:
|
- 种类图标(原型 `kindBadge`、画板「Prototype / 块种类图标」):块左边只画图标、不带字,和一行字等高(20px 的框,图形 16px,lucide 的线宽 2.2),颜色是种类色;种类的名字(翻译插件给 `kind` 值起的,见「筛选」;库规则的种类是规则给的 `badge.label`)写在悬停提示(见下「悬停提示」,同块右上角按钮)和 `aria-label` 里,`role="img"`。和折叠箭头、内容同一行顶对齐。每种内置种类由插件定图标、颜色、名字:图标从插件定义的一组图标里取(见下),内置种类 → 图标只在 webview 的一处(`kindIcons.tsx` 的 `KIND_ICONS`);颜色在 `codeReader.css` 的 `--cr-kind-*`(见「颜色」);名字由翻译插件给。同一类语义共用一个颜色,图标区分具体是哪种:
|
||||||
@@ -130,15 +133,18 @@
|
|||||||
| exception 异常 | `triangle-alert` | loop 循环 | `repeat` | comment 注释 | `message-square-text` |
|
| exception 异常 | `triangle-alert` | loop 循环 | `repeat` | comment 注释 | `message-square-text` |
|
||||||
| interpreter 解释器 | `terminal` | options 选项 | `sliders-horizontal` | case 分情况 | `signpost` |
|
| interpreter 解释器 | `terminal` | options 选项 | `sliders-horizontal` | case 分情况 | `signpost` |
|
||||||
| command 命令 | `square-terminal` | pipeline 管道 | `square-terminal` | | |
|
| command 命令 | `square-terminal` | pipeline 管道 | `square-terminal` | | |
|
||||||
|
| directive 解析指令 | `terminal` | stage 阶段 | `layers` | run 执行 | `square-terminal` |
|
||||||
|
| copy 复制 | `copy` | setting 设置 | `sliders-horizontal` | label 标签 | `tag` |
|
||||||
|
| health 健康检查 | `activity` | start 启动 | `play` | | |
|
||||||
|
|
||||||
库规则的种类用规则给的图标(`KindBadge.icon`):SQLAlchemy 数据表 `database`、数据类 `package`、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`。
|
库规则的种类用规则给的图标(`KindBadge.icon`):SQLAlchemy 数据表 `database`、数据类 `package`、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`。
|
||||||
|
|
||||||
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 22 个——命令、管道共用一个——加库规则用的 6 个,lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,`KindBadge.icon` 只能写其中一个,写别的是类型错误。webview 给每个名字配一个逐个 import 的 lucide 图标组件(`kindIcons.tsx` 的 `KIND_ICON_COMPONENTS`,打包只带这些,预览脚本约 157 kB);webview 的 `KindIconName` 照契约手抄,`kindIconNames.test.ts` 核对两边一样。规则要新图标就把名字加进这组、在 webview 配上图标,没有按任意名字找图标的路。
|
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置共用一个,字段、标签共用一个——加库规则用的 6 个,lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,`KindBadge.icon` 只能写其中一个,写别的是类型错误。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 能走到,回车 / 空格切换。
|
- 右上角的图标按钮(原型组件 `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` 挂到元素上,读屏当描述念。
|
- 悬停提示(整个插件的网页只走这一条路,预览和看板共用同一个组件):带提示的元素只写 `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` 挂到元素上,读屏当描述念。
|
||||||
- 种类图标和底色按种类区分(颜色见「颜色」);注释块虚线框。
|
- 种类图标和底色按种类区分(颜色见「颜色」);注释块虚线框。
|
||||||
- 内容两种状态,每块单独切:
|
- 内容两种状态,每块单独切:
|
||||||
- 预览:函数、类、枚举、异常、导入、常量、变量、解释器、选项块是属性表(见下节),注释块是 Markdown 说明(见「注释的 Markdown 排版」),其余块是一行译文;按词着色,等宽字体(中文部分用系统中文字体回退)。一行译文里有跨几行写的调用时,摊开实参的表排在译文下面(见「调用与构造」);块带说明(Python 字段的属性 docstring)时,说明按 Markdown 再排在下面。
|
- 预览:函数、类、枚举、异常、导入、常量、变量、解释器、选项、解析指令、阶段、健康检查、启动块是属性表(见下节),注释块是 Markdown 说明(见「注释的 Markdown 排版」),其余块是一行译文;按词着色,等宽字体(中文部分用系统中文字体回退)。一行译文里有跨几行写的调用时,摊开实参的表排在译文下面(见「调用与构造」);块带说明(Python 字段的属性 docstring)时,说明按 Markdown 再排在下面。
|
||||||
- 编辑:这一块的源码(按语法树着色),可以直接改;蓝色边框。按钮换成眼睛「预览」;Esc 也回到预览。
|
- 编辑:这一块的源码(按语法树着色),可以直接改;蓝色边框。按钮换成眼睛「预览」;Esc 也回到预览。
|
||||||
- 复合语句(函数、条件、循环……)块里只放它自己的头部那几行(主体交给子块);收尾括号、空行不成块。函数、类的说明(JSDoc / docstring)也算在函数、类块的头部里;Python 常量、变量、字段的属性 docstring 算在它那一块里(中间隔着的空行也是)。
|
- 复合语句(函数、条件、循环……)块里只放它自己的头部那几行(主体交给子块);收尾括号、空行不成块。函数、类的说明(JSDoc / docstring)也算在函数、类块的头部里;Python 常量、变量、字段的属性 docstring 算在它那一块里(中间隔着的空行也是)。
|
||||||
- 库规则认领的块(数据表卡片等,见「库规则」):种类的图标、颜色和名字由规则给,内容是规则给的属性表;它的编辑范围是原块整条语句(类就是整个类),「编辑」时整段一起改。规则没并进卡片的子块(方法、别的成员)不管写在类主体哪儿(两个字段中间也算),都照源码顺序是卡片的子块,各自的「编辑」照旧只改自己那几行。
|
- 库规则认领的块(数据表卡片等,见「库规则」):种类的图标、颜色和名字由规则给,内容是规则给的属性表;它的编辑范围是原块整条语句(类就是整个类),「编辑」时整段一起改。规则没并进卡片的子块(方法、别的成员)不管写在类主体哪儿(两个字段中间也算),都照源码顺序是卡片的子块,各自的「编辑」照旧只改自己那几行。
|
||||||
@@ -304,7 +310,7 @@
|
|||||||
3. 变量、常量(variable / const,属性表,同别的语言的变量块):赋值和 `local` / `export` / `readonly` / `declare`;`local url="$1" sha="$2" out` 一句几个是一块,没给值的写「无」。常量是 `readonly` 的,和脚本顶层名字全大写、值里没有展开的。
|
3. 变量、常量(variable / const,属性表,同别的语言的变量块):赋值和 `local` / `export` / `readonly` / `declare`;`local url="$1" sha="$2" out` 一句几个是一块,没给值的写「无」。常量是 `readonly` 的,和脚本顶层名字全大写、值里没有展开的。
|
||||||
4. 分情况(case):每个情况都短(最多 3 句、每句一行写完、没有注释和嵌套的语句块)时整条一块,一行「按 x 分情况」,下面一张表「情况 \| 做什么」,做的几句在格子里一句一行;否则头部是「按 x 分情况」,每个情况一块子块「情况:以 -apple-darwin 结尾」,要做的各句是它的子块。
|
4. 分情况(case):每个情况都短(最多 3 句、每句一行写完、没有注释和嵌套的语句块)时整条一块,一行「按 x 分情况」,下面一张表「情况 \| 做什么」,做的几句在格子里一句一行;否则头部是「按 x 分情况」,每个情况一块子块「情况:以 -apple-darwin 结尾」,要做的各句是它的子块。
|
||||||
5. 函数(function,属性表):紧贴在上面的注释(中间没空行)是「说明」,不单独成块。shell 的函数没有参数表,参数按位置取:「输入参数」写「N 个,按位置」,表「位置 \| 函数里叫」,函数里用 `local x="$N"`(或 `x=$N`)起了名字的写名字,没起的写 `$N`。「输出」是函数自己打印到输出的那几句(`echo` / `printf`,重定向到别处的不算;进 if、循环、分情况,不进 `$(…)`、管道、嵌套的函数),同一文件里有 `$(函数名 …)` 时后面淡淡写「(调用处用 $(fetch …) 拿到)」。「返回」:写了 `return N` 的写那几个值,什么都不打印、也没写 return 的写「最后一句成不成功」。主体各句是子块,同别的语言的函数,默认收起。
|
5. 函数(function,属性表):紧贴在上面的注释(中间没空行)是「说明」,不单独成块。shell 的函数没有参数表,参数按位置取:「输入参数」写「N 个,按位置」,表「位置 \| 函数里叫」,函数里用 `local x="$N"`(或 `x=$N`)起了名字的写名字,没起的写 `$N`。「输出」是函数自己打印到输出的那几句(`echo` / `printf`,重定向到别处的不算;进 if、循环、分情况,不进 `$(…)`、管道、嵌套的函数),同一文件里有 `$(函数名 …)` 时后面淡淡写「(调用处用 $(fetch …) 拿到)」。「返回」:写了 `return N` 的写那几个值,什么都不打印、也没写 return 的写「最后一句成不成功」。主体各句是子块,同别的语言的函数,默认收起。
|
||||||
6. 命令(command):一条命令一行,由命令规则读(「删除 dest(连同里面的东西、不提示)」「下载 url 到 out.part(失败就报错、跟着跳转走、最多重试 3 次)」);没有规则的命令照抄命令名,后面的词照常翻(引号去掉,展开读成中文)。重定向统一读(不归命令规则):`>&2` →「(打印到错误输出)」,`2>/dev/null` →「(丢掉错误输出)」,`> f` →「(写到 f)」,`>> f` →「(追加到 f)」,`2>&1` →「(错误输出并进输出)」,`< f` →「(从 f 读)」。用 `&&` 连起来的几条命令读成「a 然后 b」,`a || b` 读成「a,失败就 b」。
|
6. 命令(command):一条命令一行,由命令规则读(「删除 dest(连同里面的东西、不提示)」「下载 url 到 out.part(失败就报错、跟着跳转走、最多重试 3 次)」);没有规则的命令照抄命令名,后面的词照常翻(引号去掉,展开读成中文)。重定向统一读(不归命令规则):`>&2` →「(打印到错误输出)」,`2>/dev/null` →「(丢掉错误输出)」,`> f` →「(写到 f)」,`>> f` →「(追加到 f)」,`2>&1` →「(错误输出并进输出)」,`< f` →「(从 f 读)」。用 `&&` 连起来的几条命令读成「a 然后 b」,`a || b` 读成「a,失败就 b」。tree-sitter-bash 把 `a && b > f` 读成整串带着重定向,其实重定向只属于 b:语言插件把它算在 b 上(「a 然后 b(写到 f)」)。
|
||||||
7. 管道(pipeline):`a | b | c` 读成「a → b → c」;最后一段是循环(`find … | while read -r link; do … done`)时读成「找出 dest 下面所有符号链接 → 逐行读进 link」,循环的主体是子块。
|
7. 管道(pipeline):`a | b | c` 读成「a → b → c」;最后一段是循环(`find … | while read -r link; do … done`)时读成「找出 dest 下面所有符号链接 → 逐行读进 link」,循环的主体是子块。
|
||||||
8. 条件、遍历(if / else / loop):`if` / `elif` / `else` 同 Python(`elif` / `else` 展平成兄弟块,读成「否则如果」「否则」)。条件:`[ -f x ]` → 文件 x 存在,`-d` 目录 x 存在,`-e` 存在,`-L` 是符号链接,`-z` / `-n` 为空 / 不为空,`-r` / `-w` / `-x` 可读 / 可写 / 可执行,`-eq` / `-ne` / `-lt` / `-gt` / `-le` / `-ge` 等于 / 不等于 / 小于 / 大于 / 不大于 / 不小于;`=` / `==` / `!=` 等于 / 不等于,`[[ … ]]` 里右边是通配时读成「以 .sh 结尾」「不以 .sh 结尾」;`!` → 不,`&&` / `||` → 并且 / 或者。一句 `[ … ]`(或一串全是条件)自己就是一句语句时是条件块,读成「成立:…」;条件后面跟一句的(`[ -f x ] && rm x`、`cond || continue`)读成「如果 文件 x 存在,删除 x」「如果不是〔cond〕,跳过这一个」。遍历:`for f in "$gc"/git-*` → 遍历 gc/git-* 匹配到的每个文件 f,`for x in a b` → 遍历 a、b 中的每个 x,`for a in "$@"` → 遍历 全部参数 的每个 a;`while cond` → 只要 cond 就一直重复,`until cond` → 直到 cond 之前一直重复,`while read -r x` → 逐行读进 x。
|
8. 条件、遍历(if / else / loop):`if` / `elif` / `else` 同 Python(`elif` / `else` 展平成兄弟块,读成「否则如果」「否则」)。条件:`[ -f x ]` → 文件 x 存在,`-d` 目录 x 存在,`-e` 存在,`-L` 是符号链接,`-z` / `-n` 为空 / 不为空,`-r` / `-w` / `-x` 可读 / 可写 / 可执行,`-eq` / `-ne` / `-lt` / `-gt` / `-le` / `-ge` 等于 / 不等于 / 小于 / 大于 / 不大于 / 不小于;`=` / `==` / `!=` 等于 / 不等于,`[[ … ]]` 里右边是通配时读成「以 .sh 结尾」「不以 .sh 结尾」;`!` → 不,`&&` / `||` → 并且 / 或者。一句 `[ … ]`(或一串全是条件)自己就是一句语句时是条件块,读成「成立:…」;条件后面跟一句的(`[ -f x ] && rm x`、`cond || continue`)读成「如果 文件 x 存在,删除 x」「如果不是〔cond〕,跳过这一个」。遍历:`for f in "$gc"/git-*` → 遍历 gc/git-* 匹配到的每个文件 f,`for x in a b` → 遍历 a、b 中的每个 x,`for a in "$@"` → 遍历 全部参数 的每个 a;`while cond` → 只要 cond 就一直重复,`until cond` → 直到 cond 之前一直重复,`while read -r x` → 逐行读进 x。
|
||||||
- 值和展开(一行译文、变量的值、表格里都一样):`$1` → 第 1 个参数,`$0` → 脚本路径,`$@` / `$*` → 全部参数,`$#` → 参数个数,`$?` → 上一条命令的退出码,`$$` → 当前进程号;`${x:-d}` → x(没设就用 d),第几个参数写「没给」:`${1:-}` → 第 1 个参数(没给就是空);`${x:=d}` → x(没设就设成 d),`${x:?m}` → x(没设就报错:m),`${x:+y}` → y(x 设了才有),`${#x}` → x 的长度,`${x#p}` / `${x##p}` → x(去掉开头的 / 开头最长的 p),`${x%s}` / `${x%%s}` → x(去掉结尾的 / 结尾最长的 s),`${x/a/b}` → x(其中第一个 a 换成 b),`${x//a/b}` → x(其中 a 全换成 b),`${x:1:2}` → x(从第 1 个字符起,取 2 个)。整个值是 `$(cmd)` 时读成「执行〔…〕的输出」,写在别的字中间的读成「〔…〕」;命令规则读出来是一个东西的直接写它:`$(dirname "$0")` → 脚本所在目录。双引号字符串去掉引号,写死的部分按字符串色,展开的变量按变量色;全大写的名字(`PY_VERSION`、`HOME`)按常量色。
|
- 值和展开(一行译文、变量的值、表格里都一样):`$1` → 第 1 个参数,`$0` → 脚本路径,`$@` / `$*` → 全部参数,`$#` → 参数个数,`$?` → 上一条命令的退出码,`$$` → 当前进程号;`${x:-d}` → x(没设就用 d),第几个参数写「没给」:`${1:-}` → 第 1 个参数(没给就是空);`${x:=d}` → x(没设就设成 d),`${x:?m}` → x(没设就报错:m),`${x:+y}` → y(x 设了才有),`${#x}` → x 的长度,`${x#p}` / `${x##p}` → x(去掉开头的 / 开头最长的 p),`${x%s}` / `${x%%s}` → x(去掉结尾的 / 结尾最长的 s),`${x/a/b}` → x(其中第一个 a 换成 b),`${x//a/b}` → x(其中 a 全换成 b),`${x:1:2}` → x(从第 1 个字符起,取 2 个)。整个值是 `$(cmd)` 时读成「执行〔…〕的输出」,写在别的字中间的读成「〔…〕」;命令规则读出来是一个东西的直接写它:`$(dirname "$0")` → 脚本所在目录。双引号字符串去掉引号,写死的部分按字符串色,展开的变量按变量色;全大写的名字(`PY_VERSION`、`HOME`)按常量色。
|
||||||
@@ -336,6 +342,60 @@
|
|||||||
|
|
||||||
- 和原型不一样的地方(插件生成的,比手写的更一致):函数里的变量也是属性表块(同别的语言,不写成一行「局部 …」);`$N` 一律写「第 N 个参数」;选项的意思之间一律用顿号(「连同里面的东西、不提示」);`[ "$(readlink "$f")" = git ]` 照结构读成「〔读出 f 指向的路径〕 等于 git」,不合写成「f 指向 git」。
|
- 和原型不一样的地方(插件生成的,比手写的更一致):函数里的变量也是属性表块(同别的语言,不写成一行「局部 …」);`$N` 一律写「第 N 个参数」;选项的意思之间一律用顿号(「连同里面的东西、不提示」);`[ "$(readlink "$f")" = git ]` 照结构读成「〔读出 f 指向的路径〕 等于 git」,不合写成「f 指向 git」。
|
||||||
|
|
||||||
|
### Dockerfile
|
||||||
|
|
||||||
|
原型画板「Dockerfile」(`code.js` 的「Dockerfile」一节:`DOCKER_KINDS`、`NEXT_DOCKERFILE`、`BACKEND_DOCKERFILE`;README「Dockerfile」)。样例是 luffy 的 `frontend/client-nextjs/Dockerfile` 和 `backend/docker/Dockerfile.backend`(复制成 `fixtures/clientNextjs.Dockerfile`、`fixtures/backend.Dockerfile`);原型里的中文是手写的目标,下面的译文都由插件生成,逐行核对见 `translators/dockerfileZh.test.ts` 的 `NEXT_READING`、`BACKEND_READING`,原型略去的几段也照样读。
|
||||||
|
|
||||||
|
- 哪些文件:VS Code 认成 `dockerfile` 的文件:`Dockerfile`、`Dockerfile.*`、`*.dockerfile`、`Containerfile`。语法包是 tree-sitter-dockerfile,`@vscode/tree-sitter-wasm` 里没有,取自 npm 包 `tree-sitter-wasm`(预编译、每个 wasm 都签过名、上游提交钉死),打包时和别的语法包一起复制进 `dist/tree-sitter/`(见「解析运行时」)。
|
||||||
|
- 分工同别的语言:语言插件(`languages/dockerfile.ts`)报块和 facts;翻译插件(`translators/dockerfileZh.ts`)定用词;RUN、shell 写法的 CMD / ENTRYPOINT、HEALTHCHECK 的检查命令是嵌着的 shell(`EmbeddedCode`),宿主交给 shell 的语言插件、翻译插件和命令规则读(见下「嵌着的代码」),Dockerfile 这边不再读一遍 shell;数组写法的命令(`["node", "server.js"]`)不经过 shell,由 Dockerfile 语言插件报成命令(`LanguageAdapter.command`),同样交给命令规则。webview 不认识 Dockerfile,只多认几种块的种类。
|
||||||
|
- 块(图标、颜色见「预览(代码块树)」「颜色」):
|
||||||
|
1. 解析指令(directive,属性表):文件最开头连着的 `# syntax=…`、`# escape=…`、`# check=…`,一条一行:「语法 docker/dockerfile:1.7(BuildKit 按这个版本读这份文件)」「转义字符 …」「检查 …」。别的注释、空行、指令之后的不算。
|
||||||
|
2. 阶段(stage,属性表):每个 FROM 开一个阶段,到下一个 FROM 之前的指令、注释都是它的子块;第一个 FROM 之前的(全局的 ARG)照常是顶层的块。卡片:名称(`AS` 后面的名字;没起名的写「没起名,按序号 N 引用」)/ 说明(紧贴在 FROM 上面、中间没空行的几行注释,算在阶段的头部里)/ 基础镜像(「node,标签 22-alpine」,写了摘要的加「,摘要 sha256:…」,接着前面某个阶段建的写「阶段 builder」,写成构建参数的读成「BASE(构建参数)」)/ 平台(写了 `--platform` 才有)。阶段名能点:`COPY --from=deps`、`RUN --mount=…,from=deps`、`FROM deps` 里的 deps ⌘ / Ctrl + 点击跳到那个阶段,单击高亮各处出现。阶段默认展开。
|
||||||
|
3. 执行(run):RUN 里的 shell 由 shell 的语言插件分步(`LanguageAdapter.steps`):只有一条命令时一行,就是那条命令读成的(「pnpm 装依赖(严格按 lock 文件、不跑安装脚本)」),一条循环、条件的子块归这个 RUN;只用 `&&` 连着的一串拆开,一条一步,头一行写「执行」,3 条以上加「(N 条命令,一条失败就停)」(原型 2 条时只写「执行」,10 条时带条数);分几句写的(heredoc 里一行一句)是「(N 条命令)」。续行的 `\` 去掉、各段拼起来读(`RUN apt-get install -y \⏎ curl git`);`RUN <<EOT` 的 heredoc 正文当一段脚本读;管道照 Shell 写「a → b」;`a && echo x > f && b` 这种整串带重定向的(tree-sitter-bash 这样读),重定向只算在 echo 那一步上。RUN 的选项写在括号里:`--mount=type=cache,target=X` → 缓存目录 X 跨构建保留,`type=secret,id=X` → 临时挂上密钥 X,`type=ssh` → 转发 SSH 密钥,`type=tmpfs,target=X` → X 用内存临时目录,`type=bind`(默认)→ 临时挂上 [阶段 F 的] 源 到 目标,`--network=none / host / default` → 不联网 / 用宿主机的网络 / 用默认网络,`--security=insecure / sandbox` → 不隔离权限 / 隔离权限,别的照抄。
|
||||||
|
4. 复制(copy):「[从阶段 X | 从镜像 Y] 复制 源 → 目标(属主 u:g)」;源是 `.` 写「整个构建目录」,目标是 `.` 写「./」;`--chmod` → 权限,`--parents` → 保留目录层级,`--exclude` → 跳过 X,`--link` → 单独成层。ADD:源全是网址写「下载」,全是本地的压缩包(.tar、.tar.gz、.tgz……,ADD 会自动解开)写「解压」,git 仓库写「拉取仓库」,`--checksum` → 校验;`COPY <<EOF /f` 写「写入 /f(内容写在下面)」。
|
||||||
|
5. 变量(variable,属性表):ARG 是「构建参数 X / 默认值 …」,没写默认值的写「没写,构建时传」;ENV 是「环境变量 X / 值 …」。值里的 `$X`、`${X}` 读成变量(带源码下标,全大写的按常量色),在它之前(同一阶段里,FROM 那一行看文件开头的)用 ARG 声明过的后面注「(构建参数)」,BuildKit 自动给的(TARGETPLATFORM、TARGETARCH……)注它的意思,`${X:-d}` 注「没设就用 d」,`${X:+d}` 注「设了就是 d」。
|
||||||
|
6. 并块:连着写的同种指令(紧挨着下一行,中间没空行、没注释、没别的指令)并成一块。ENV、ARG 并成一个变量块:一行「环境变量 4 条连着写」(一条 ENV 写了几个的是「3 个」),下面一张「名称 | 值」(构建参数是「名称 | 默认值」)表,每行讲的定义是那个变量;COPY 从同一个地方复制的(都从构建目录,或 `--from` 一样)并成一行「复制(N 条连着写,属主都是 u:g)」,下面一张「从 | 源 | 到」表(都从构建目录复制的没有「从」这一列,从阶段写「阶段 X」、从镜像写「镜像 名字:标签」),属主都一样才写在头一行,别的选项写在「到」那一格里;LABEL 并成一张「名称 | 值」表。ADD 不并(各条做的事不一样),带 heredoc 的 COPY 不并。
|
||||||
|
7. 设置(setting):WORKDIR「工作目录 /app」,USER「之后以 nextjs 身份运行」(`u:g` 照写),EXPOSE「开放端口 80(udp)、443」,VOLUME「声明数据卷 /data、/logs」,STOPSIGNAL「停止容器时发 SIGTERM 信号」,SHELL「之后的 RUN、CMD、ENTRYPOINT 用 /bin/bash -o pipefail -c 执行」,MAINTAINER「维护者 …」,各一行。ONBUILD 是一行「以它为基础镜像构建时再执行」,包着的那条指令是它的子块。
|
||||||
|
8. 标签(label):「标签」,下面一张「名称 | 值」表(一条 LABEL 写几个、连着写的几条都在表里)。
|
||||||
|
9. 健康检查(health,属性表):间隔 / 超时 / 启动后宽限 / 宽限期里的间隔(时长写成「1 分 30 秒」「500 毫秒」)/ 失败几次算不健康(「3 次」),写了才有;检查命令照 Shell 读(shell 写法)或交给命令规则(数组写法)。`HEALTHCHECK NONE` 只有一行「检查 不做健康检查(基础镜像里设的也关掉)」。
|
||||||
|
10. 启动(start,属性表):CMD 是「命令」,ENTRYPOINT 是「入口命令」,读法同检查命令;「写法」是「数组,不经过 shell」或「经过 shell」。
|
||||||
|
- 选项多的命令(`uv sync` 带一长串选项):命令规则认出的选项有 4 个以上(同一个选项写几遍算一个),或同一个选项写了几遍时,这一行后面写「· N 个选项」,意思排成一张「选项 | 意思」表,写了几遍的并成一行、选项后面写「× 11」(`--no-install-package × 11 | 不装这些包:agent-dsl、…`);3 个以内、都只写一遍的照旧写在括号里。表的样子同调用的实参表(见「调用与构造」)。
|
||||||
|
- 照旧能用的:按种类筛选、单击名字高亮各处出现、⌘ / Ctrl + 点击跳到定义(阶段名、ARG / ENV 的变量,同一文件里由语言插件认,同 Shell;RUN 里 shell 自己的变量由 shell 的语言插件认)、中文名(紧挨在 ARG、ENV(一个变量的)、`FROM … AS` 上面一行、只写了 `# @Comment 名字` 的注释,这行注释算在那条指令的块里、不单独成块)、文件注释、问 AI。
|
||||||
|
- 和原型不一样的地方(插件生成的,比手写的更一致):
|
||||||
|
- 并成一块的 ENV 是属性表的一行「环境变量 4 条连着写」加表,不写成一行字「环境变量(4 条连着写)」。它还是变量块,每行能跳到定义。
|
||||||
|
- `uv sync` 是一行「uv 按 lock 同步依赖 · 4 个选项」加表,不写成「命令 / 选项」两行(同调用的「调用 X · N 个参数」)。
|
||||||
|
- `curl` 没写 `-o` 时写「请求 url」(原型的 RUN 里写「下载」);`. /etc/os-release` 写「读入 … 里的变量和函数」;`echo "…" > f` 写「打印 "…"(写到 f)」(原型「写入 … 到 f(覆盖)」,重定向统一由翻译插件接上);echo 里的 `[signed-by=…]` 照源码写全。
|
||||||
|
- 检查命令 `curl -f … || exit 1` 照 Shell 读成「请求 …(失败就报错),失败就 退出,退出码 1」(原型「或者」)。
|
||||||
|
- 原型第 12、13 行(`COPY --from=deps …`、`COPY . .`)分开画,所以 COPY 只在从同一个地方复制时才并。
|
||||||
|
- 后端文件开头三行注释紧贴着 FROM,读成这个阶段的「说明」(原型没画这三行)。
|
||||||
|
- RUN 拆开的各步是 shell 的块,种类照 shell 的(命令、管道、变量……),不都叫「执行」。
|
||||||
|
- 嵌着的代码(通用的做法,见「通用契约」的 `EmbeddedCode`):语言插件报出这份源码里嵌着的别的语言的代码(`LanguageAdapter.embedded`:装着它的节点、语言、源码里属于它的几段,去掉了外层的记号)。宿主按语言找注册的语言插件、选中的翻译插件和命令规则,只解析这几段(`includedRanges`,节点下标照旧是整份源码的),按 `LanguageAdapter.steps` 分步:只有一步时这一步的子块归嵌着它的块,几步时各步是它的子块,都用那种语言的翻译插件翻、那种语言的词性着色(外层的源码着色上面叠一层);翻译插件翻嵌着它的块、属性表的行时问 `SourceDocument.embedded`,拿到各步读成的一行,连词、「N 条命令」由它自己定。那种语言没注册时照抄、没有子块。同一文件里的名字把嵌着的代码里认出的一并交给宿主。
|
||||||
|
- 样例(`fixtures/clientNextjs.Dockerfile`、`fixtures/backend.Dockerfile`):
|
||||||
|
|
||||||
|
| 源码 | 预览 |
|
||||||
|
| --- | --- |
|
||||||
|
| `# syntax=docker/dockerfile:1.7` | 语法 docker/dockerfile:1.7(BuildKit 按这个版本读这份文件) |
|
||||||
|
| `FROM node:22-alpine AS deps` | 名称 deps / 基础镜像 node,标签 22-alpine |
|
||||||
|
| `RUN corepack enable && corepack prepare pnpm@latest --activate` | 执行,子块「开启 corepack」「准备 pnpm latest(并设为当前用的版本)」 |
|
||||||
|
| `COPY package.json pnpm-lock.yaml ./` | 复制 package.json、pnpm-lock.yaml → ./ |
|
||||||
|
| `RUN pnpm install --frozen-lockfile --ignore-scripts` | pnpm 装依赖(严格按 lock 文件、不跑安装脚本) |
|
||||||
|
| `COPY --from=deps /app/node_modules ./node_modules` | 从阶段 deps 复制 /app/node_modules → ./node_modules |
|
||||||
|
| `COPY . .` | 复制 整个构建目录 → ./ |
|
||||||
|
| `ARG DEPLOYMENT_ID` ⏎ `ENV NEXT_DEPLOYMENT_ID=$DEPLOYMENT_ID` | 构建参数 DEPLOYMENT_ID / 默认值 没写,构建时传;环境变量 NEXT_DEPLOYMENT_ID / 值 DEPLOYMENT_ID(构建参数) |
|
||||||
|
| `RUN pnpm build` | pnpm 构建 |
|
||||||
|
| `ENV NODE_ENV=production` ⏎ …(4 条) | 环境变量 4 条连着写,表 `NODE_ENV \| production`、`NEXT_TELEMETRY_DISABLED \| 1`、`PORT \| 3000`、`HOSTNAME \| 0.0.0.0` |
|
||||||
|
| `RUN addgroup --system --gid 1001 nodejs && \` ⏎ `adduser --system --uid 1001 nextjs` | 执行,子块「新建系统用户组 nodejs(gid 1001)」「新建系统用户 nextjs(uid 1001)」 |
|
||||||
|
| `COPY --from=builder --chown=nextjs:nodejs …`(2 条) | 复制(2 条连着写,属主都是 nextjs:nodejs),表 `阶段 builder \| /app/.next/standalone \| ./` |
|
||||||
|
| `USER nextjs`、`EXPOSE 3000` | 之后以 nextjs 身份运行、开放端口 3000 |
|
||||||
|
| `CMD ["node", "server.js"]` | 命令 node server.js / 写法 数组,不经过 shell |
|
||||||
|
| `LABEL maintainer="Cruldra" \` ⏎ `description="…"` | 标签,表 `maintainer \| Cruldra`、`description \| luffy-agent-platform - …` |
|
||||||
|
| `RUN apt-get update && \` ⏎ …(10 条) | 执行(10 条命令,一条失败就停),子块「更新软件包列表」「安装 ca-certificates、curl、gnupg、postgresql-client、git(不装推荐的包、不用确认)」「建目录 /usr/share/keyrings」「请求 https://…/ACCC4CF8.asc(不打印进度、失败就报错、跟着跳转走) → 转成二进制密钥 写到 /usr/share/keyrings/postgresql.gpg」「读入 /etc/os-release 里的变量和函数」「打印 "deb …"(写到 /etc/apt/sources.list.d/pgdg.list)」…「打印 pg_repack 的版本」「删除 /var/lib/apt/lists/*(连同里面的东西、不提示)」 |
|
||||||
|
| `COPY --from=node:22-slim …`(2 条) | 复制(2 条连着写),表 `镜像 node:22-slim \| /usr/local/bin/node \| /usr/local/bin/node` |
|
||||||
|
| `COPY --chown=appuser:appuser …`(12 条) | 复制(12 条连着写,属主都是 appuser:appuser),表「源 \| 到」12 行 |
|
||||||
|
| `RUN uv sync --frozen --no-dev --no-install-project \` ⏎ `--no-install-package agent-dsl \` …(11 个) | uv 按 lock 同步依赖 · 4 个选项,表 `--frozen \| 严格按 lock 文件,不改它`、`--no-dev \| 不装开发依赖`、`--no-install-project \| 不装本项目`、`--no-install-package × 11 \| 不装这些包:agent-dsl、…` |
|
||||||
|
| `HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \` ⏎ `CMD curl -f http://localhost:8000/health \|\| exit 1` | 间隔 30 秒 / 超时 5 秒 / 启动后宽限 10 秒 / 失败几次算不健康 3 次 / 检查命令 请求 http://localhost:8000/health(失败就报错),失败就 退出,退出码 1 |
|
||||||
|
| `CMD ["uv", "run", "uvicorn", "luffy_agent.main:app", "--host", "0.0.0.0", "--port", "8000"]` | 命令 用 uv 运行 uvicorn luffy_agent.main:app,监听 0.0.0.0:8000 / 写法 数组,不经过 shell |
|
||||||
|
|
||||||
### 注释的 Markdown 排版
|
### 注释的 Markdown 排版
|
||||||
|
|
||||||
- 用在:注释块(`#`、`//`、`/* */`、不挂在函数、类上的 JSDoc、Python 模块开头的 docstring 和别的当注释用的单独字符串)的预览,函数、类、常量、变量属性表的「说明」,字段译文下面的说明。视图数据里它们是 `{ markdown }`(见「通用契约」的 `Markdown`),webview 按数据决定怎么渲染,不猜。
|
- 用在:注释块(`#`、`//`、`/* */`、不挂在函数、类上的 JSDoc、Python 模块开头的 docstring 和别的当注释用的单独字符串)的预览,函数、类、常量、变量属性表的「说明」,字段译文下面的说明。视图数据里它们是 `{ markdown }`(见「通用契约」的 `Markdown`),webview 按数据决定怎么渲染,不猜。
|
||||||
@@ -401,6 +461,7 @@
|
|||||||
| TypeScript / JavaScript | 定义那一行行尾的 `// @Comment 当前用户`(块注释不算) | 参数、变量、枚举成员、类字段、接口属性 |
|
| TypeScript / JavaScript | 定义那一行行尾的 `// @Comment 当前用户`(块注释不算) | 参数、变量、枚举成员、类字段、接口属性 |
|
||||||
| TypeScript / JavaScript | 兜底:函数的 JSDoc 里 `@param [{类型}] 名字 [-] 说明`(`[名字]`、`[名字=默认]` 也认,`opts.x` 这种属性不认),同上取第一小句、不超过 12 个字 | 参数(函数声明、方法、值是函数的变量和类字段) |
|
| TypeScript / JavaScript | 兜底:函数的 JSDoc 里 `@param [{类型}] 名字 [-] 说明`(`[名字]`、`[名字=默认]` 也认,`opts.x` 这种属性不认),同上取第一小句、不超过 12 个字 | 参数(函数声明、方法、值是函数的变量和类字段) |
|
||||||
| Shell | 赋值、`local` / `export` / `readonly` 声明那一行行尾的 `# @Comment 安装目录` | 变量 |
|
| Shell | 赋值、`local` / `export` / `readonly` 声明那一行行尾的 `# @Comment 安装目录` | 变量 |
|
||||||
|
| Dockerfile | 紧挨在指令上面一行、只写了 `# @Comment 部署 ID` 的注释(Dockerfile 的注释只能单独成行) | ARG、ENV(只有一个变量的)、`FROM … AS` 的阶段名 |
|
||||||
|
|
||||||
- 行尾记号给那一行第一个能带行尾记号的定义(上表第三行、第六行那些;函数、类用装饰器),单独成行的注释、那一行没有这种定义的不算。
|
- 行尾记号给那一行第一个能带行尾记号的定义(上表第三行、第六行那些;函数、类用装饰器),单独成行的注释、那一行没有这种定义的不算。
|
||||||
- 同一个定义几处都写了:写在代码里的记号(注解、装饰器)优先,其次行尾记号,最后才是说明文字。
|
- 同一个定义几处都写了:写在代码里的记号(注解、装饰器)优先,其次行尾记号,最后才是说明文字。
|
||||||
@@ -472,6 +533,7 @@ export type BlockKind =
|
|||||||
| 'import' | 'function' | 'class' | 'enum' | 'exception' | 'type' | 'field' | 'const' | 'variable'
|
| 'import' | 'function' | 'class' | 'enum' | 'exception' | 'type' | 'field' | 'const' | 'variable'
|
||||||
| 'if' | 'else' | 'loop' | 'try' | 'catch' | 'finally' | 'return' | 'statement' | 'comment'
|
| 'if' | 'else' | 'loop' | 'try' | 'catch' | 'finally' | 'return' | 'statement' | 'comment'
|
||||||
| 'interpreter' | 'options' | 'case' | 'command' | 'pipeline' // shell:解释器、选项(属性表),分情况、命令、管道(一行译文)
|
| 'interpreter' | 'options' | 'case' | 'command' | 'pipeline' // shell:解释器、选项(属性表),分情况、命令、管道(一行译文)
|
||||||
|
| 'directive' | 'stage' | 'run' | 'copy' | 'setting' | 'label' | 'health' | 'start' // Dockerfile:解析指令、阶段、健康检查、启动(属性表),执行、复制、设置、标签(一行译文)
|
||||||
|
|
||||||
/** 源码里一段的下标(源码字符串下标),end 为开区间。 */
|
/** 源码里一段的下标(源码字符串下标),end 为开区间。 */
|
||||||
export interface SourceRange { startIndex: number, endIndex: number }
|
export interface SourceRange { startIndex: number, endIndex: number }
|
||||||
@@ -514,6 +576,30 @@ export interface InterpreterFacts { program: string, lookup: 'env' | 'path', com
|
|||||||
export interface SwitchFacts { id: string, on: boolean, flag: string }
|
export interface SwitchFacts { id: string, on: boolean, flag: string }
|
||||||
export interface OptionsFacts { switches: SwitchFacts[] }
|
export interface OptionsFacts { switches: SwitchFacts[] }
|
||||||
|
|
||||||
|
/** 文件开头给构建工具看的解析指令(`# syntax=docker/dockerfile:1.7`),一行一条。 */
|
||||||
|
export interface ParserDirective { name: string, value: string }
|
||||||
|
export interface DirectiveFacts { directives: ParserDirective[] }
|
||||||
|
|
||||||
|
/** 指令后面的一个选项(`--from=deps`):值在源码里从哪开始(值里的阶段名、变量能跳到定义)。 */
|
||||||
|
export interface InstructionFlag { node: Node, name: string, value: string, valueStart: number }
|
||||||
|
|
||||||
|
/** 一个构建阶段(FROM):阶段名、第几个(没起名的按它引用)、紧贴在上面的注释、镜像名、是不是前面的阶段、标签 / 摘要(带 : / @)、选项。 */
|
||||||
|
export interface StageFacts { name?: Node, index: number, doc?: Node[], image: Node, stage: boolean, tag?: Node, digest?: Node, flags: InstructionFlag[] }
|
||||||
|
|
||||||
|
/** 要执行的命令的两种写法:数组(不经过 shell,node 是整个数组)、交给 shell 的一段嵌着的代码。 */
|
||||||
|
export type CommandForm = { form: 'exec', node: Node } | { form: 'shell', code: EmbeddedCode }
|
||||||
|
/** 健康检查:none 是 `HEALTHCHECK NONE`,否则选项和检查命令。 */
|
||||||
|
export interface HealthFacts { none: boolean, flags: InstructionFlag[], command?: CommandForm }
|
||||||
|
/** 容器启动时执行的命令:CMD(默认命令)或 ENTRYPOINT(入口)。 */
|
||||||
|
export interface StartFacts { instruction: 'cmd' | 'entrypoint', command: CommandForm }
|
||||||
|
|
||||||
|
/** 写在这门语言里的另一种语言的代码:外层语法树里装着它的节点、语言、源码里属于它的几段(去掉了外层的记号,拼起来是一份那种语言的代码)。 */
|
||||||
|
export interface EmbeddedCode { node: Node, languageId: string, ranges: SourceRange[] }
|
||||||
|
/** 嵌着的代码被当成一串步骤执行时的分块:一步一块,chained 表示前一步失败就不往下走(shell 的 &&)。 */
|
||||||
|
export interface StepBlocks { blocks: CodeBlock[], chained: boolean }
|
||||||
|
/** 宿主读嵌着的代码读出来的:各步读成的一行(注释不算一步)、是不是一步失败就停、整段是不是就一步(这一步的子块归嵌着它的块)。 */
|
||||||
|
export interface EmbeddedReading { steps: { text: Inline[], table?: PreviewTable }[], chained: boolean, whole: boolean }
|
||||||
|
|
||||||
/** 父类的一项:TS `extends Base<T>` 的 Base(表达式)和它后面的泛型实参 <T>;Python 类参数列表的一项(含 metaclass=…、*bases)。 */
|
/** 父类的一项:TS `extends Base<T>` 的 Base(表达式)和它后面的泛型实参 <T>;Python 类参数列表的一项(含 metaclass=…、*bases)。 */
|
||||||
export interface BaseFacts { value: Node, typeArguments?: Node }
|
export interface BaseFacts { value: Node, typeArguments?: Node }
|
||||||
|
|
||||||
@@ -626,7 +712,8 @@ export interface DeclaratorFacts {
|
|||||||
export interface VariableFacts {
|
export interface VariableFacts {
|
||||||
access?: Access
|
access?: Access
|
||||||
doc?: Node // 同 FunctionFacts.doc:Python 模块、类主体里紧跟在这条赋值后面的字符串节点(属性 docstring)
|
doc?: Node // 同 FunctionFacts.doc:Python 模块、类主体里紧跟在这条赋值后面的字符串节点(属性 docstring)
|
||||||
declarators: DeclaratorFacts[]
|
declarators: DeclaratorFacts[] // 几条连着写的声明并成一块时(Dockerfile 连着的 ENV)是它们的全部
|
||||||
|
scope?: 'build' | 'environment' // 语言分这个才填:build 是构建参数(ARG),environment 是环境变量(ENV)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -656,7 +743,9 @@ export interface EnumBlock extends BlockShape { kind: 'enum', facts: EnumFacts }
|
|||||||
export interface ExceptionBlock extends BlockShape { kind: 'exception', facts: ExceptionFacts } // 头部是整个类,留下的方法(含构造函数)等是子块
|
export interface ExceptionBlock extends BlockShape { kind: 'exception', facts: ExceptionFacts } // 头部是整个类,留下的方法(含构造函数)等是子块
|
||||||
export interface ImportBlock extends BlockShape { kind: 'import', facts: ImportFacts }
|
export interface ImportBlock extends BlockShape { kind: 'import', facts: ImportFacts }
|
||||||
export interface VariableBlock extends BlockShape { kind: 'const' | 'variable', facts: VariableFacts }
|
export interface VariableBlock extends BlockShape { kind: 'const' | 'variable', facts: VariableFacts }
|
||||||
export type FactBlock = FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | ImportBlock | VariableBlock
|
export type FactBlock
|
||||||
|
= | FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | ImportBlock | VariableBlock | InterpreterBlock | OptionsBlock
|
||||||
|
| DirectiveBlock | StageBlock | HealthBlock | StartBlock // 各带自己的 facts:InterpreterFacts、OptionsFacts、DirectiveFacts、StageFacts、HealthFacts、StartFacts
|
||||||
/** 带说明(facts.doc)的块。 */
|
/** 带说明(facts.doc)的块。 */
|
||||||
export type DocumentedBlock = FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | VariableBlock
|
export type DocumentedBlock = FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | VariableBlock
|
||||||
/** 属性表块的种类和 facts,不带块的形状:库规则把一块认成现成的一种属性表块时给它。 */
|
/** 属性表块的种类和 facts,不带块的形状:库规则把一块认成现成的一种属性表块时给它。 */
|
||||||
@@ -667,6 +756,8 @@ export type TextKind = Exclude<BlockKind, FactBlock['kind']>
|
|||||||
export interface TextBlock extends BlockShape {
|
export interface TextBlock extends BlockShape {
|
||||||
kind: TextKind
|
kind: TextKind
|
||||||
doc?: Node // 同 VariableFacts.doc:Python 字段后面紧跟的属性 docstring;预览在译文下面按 Markdown 显示(Translator.doc)
|
doc?: Node // 同 VariableFacts.doc:Python 字段后面紧跟的属性 docstring;预览在译文下面按 Markdown 显示(Translator.doc)
|
||||||
|
group?: Node[] // 几条连着写的同种语句并成的一块(Dockerfile 连着的 COPY、LABEL):各条,node 是第一条;翻译插件排成一行字加一张表
|
||||||
|
embedded?: EmbeddedCode // 这块执行的是嵌着的另一种语言的代码(RUN):宿主分步,步是它的子块(只有一步时是那一步的子块)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CodeBlock = TextBlock | FactBlock
|
export type CodeBlock = TextBlock | FactBlock
|
||||||
@@ -754,6 +845,10 @@ export interface LanguageAdapter {
|
|||||||
command?: (node: Node) => CommandFacts | undefined
|
command?: (node: Node) => CommandFacts | undefined
|
||||||
/** 一份源码里自己认得出的名字(LocalSymbol);VS Code 有这种语言的定义、高亮提供者时用不上,不填。 */
|
/** 一份源码里自己认得出的名字(LocalSymbol);VS Code 有这种语言的定义、高亮提供者时用不上,不填。 */
|
||||||
symbols?: (root: Node, source: string, languageId: string) => LocalSymbol[]
|
symbols?: (root: Node, source: string, languageId: string) => LocalSymbol[]
|
||||||
|
/** 这份源码里嵌着的别的语言的代码(块里的、属性表里的都算);没有的语言不填。 */
|
||||||
|
embedded?: (root: Node, source: string, languageId: string) => EmbeddedCode[]
|
||||||
|
/** 这门语言的代码被别的语言嵌着、当一串步骤执行时怎么分块;不填就按 toBlocks 分、不算一步失败就停。 */
|
||||||
|
steps?: (root: Node, source: string, languageId: string) => StepBlocks
|
||||||
/** 认代码里给定义起的中文名(见「中文名」);不填就没有中文名。 */
|
/** 认代码里给定义起的中文名(见「中文名」);不填就没有中文名。 */
|
||||||
displayNames?: DisplayNameReader
|
displayNames?: DisplayNameReader
|
||||||
}
|
}
|
||||||
@@ -784,6 +879,8 @@ export interface SourceDocument {
|
|||||||
calleeKind?: (call: CallFacts) => BlockKind | undefined
|
calleeKind?: (call: CallFacts) => BlockKind | undefined
|
||||||
/** 这个命令名的命令规则(这种语言注册的,先注册的算);没有时命令照抄。规则出错由宿主记下、当它没读。 */
|
/** 这个命令名的命令规则(这种语言注册的,先注册的算);没有时命令照抄。规则出错由宿主记下、当它没读。 */
|
||||||
commandRule?: (name: string) => CommandRule | undefined
|
commandRule?: (name: string) => CommandRule | undefined
|
||||||
|
/** 嵌着的代码(EmbeddedCode.node 是 node 的那段)读出来的样子:宿主用那种语言的插件读好了交出来;那种语言没注册时没有。 */
|
||||||
|
embedded?: (node: Node) => EmbeddedReading | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 一份源码里语言插件自己认得出的一个名字:定义处,和用到它的各处(含定义处)。没有定义、高亮提供者的语言(shell)靠它跳转、高亮。 */
|
/** 一份源码里语言插件自己认得出的一个名字:定义处,和用到它的各处(含定义处)。没有定义、高亮提供者的语言(shell)靠它跳转、高亮。 */
|
||||||
@@ -907,7 +1004,7 @@ export interface CommandWord { node: Node, literal?: string }
|
|||||||
export interface CommandFacts { name: CommandWord, arguments: CommandWord[], redirects: Redirect[] }
|
export interface CommandFacts { name: CommandWord, arguments: CommandWord[], redirects: Redirect[] }
|
||||||
|
|
||||||
/** 规则读出来的一行;value:读出来的是一个东西(`dirname "$0"` 是脚本所在目录),不是一个动作。 */
|
/** 规则读出来的一行;value:读出来的是一个东西(`dirname "$0"` 是脚本所在目录),不是一个动作。 */
|
||||||
export interface CommandReading { text: Inline[], value?: boolean }
|
export interface CommandReading { text: Inline[], value?: boolean, table?: PreviewTable } // table:选项写得多的命令的「选项 | 意思」表
|
||||||
|
|
||||||
export interface CommandContext {
|
export interface CommandContext {
|
||||||
displayLanguage: string // 当前翻译插件的译文语言,规则按它挑词表
|
displayLanguage: string // 当前翻译插件的译文语言,规则按它挑词表
|
||||||
@@ -919,17 +1016,18 @@ export interface CommandRule {
|
|||||||
id: string // 'shell.rm'
|
id: string // 'shell.rm'
|
||||||
label: string // 'rm'
|
label: string // 'rm'
|
||||||
languageIds: string[]
|
languageIds: string[]
|
||||||
commands: string[] // 管哪些命令名;几条规则管同一个名字时先注册的算
|
commands: string[] // 管哪些命令名;几条规则管同一个名字时先注册的算;'*' 管没有自己的规则的命令
|
||||||
read: (command: CommandFacts, context: CommandContext) => CommandReading | undefined // 读不了返回 undefined,命令照抄
|
read: (command: CommandFacts, context: CommandContext) => CommandReading | undefined // 读不了返回 undefined,命令照抄
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `activate()` 返回 `{ codeReader: CodeReaderApi }`;其他扩展声明 `extensionDependencies` 后用 `extensions.getExtension('clurdra.superpowers-vscode-clurdra')!.exports.codeReader` 注册。
|
- `activate()` 返回 `{ codeReader: CodeReaderApi }`;其他扩展声明 `extensionDependencies` 后用 `extensions.getExtension('clurdra.superpowers-vscode-clurdra')!.exports.codeReader` 注册。
|
||||||
- 内置的 TypeScript、Python、Shell 语言插件、三个中文直译翻译插件、SQLAlchemy、Celery、enum、dataclasses、FastAPI 库规则和 shell 命令规则在 `activate()` 里通过同一个 API 注册(规则按这个顺序),没有旁路。
|
- 内置的 TypeScript、Python、Shell、Dockerfile 语言插件、四个中文直译翻译插件、shell / 装系统 / 装软件包的命令规则和 SQLAlchemy、Celery、enum、dataclasses、FastAPI 库规则在 `activate()` 里通过同一个 API 注册(规则按这个顺序),没有旁路。
|
||||||
- 注册表变化时更新 `superpowers.codeReader.languageIds` 上下文,并让已打开的预览刷新。
|
- 注册表变化时更新 `superpowers.codeReader.languageIds` 上下文,并让已打开的预览刷新。
|
||||||
- 分工:语言插件只从语法树读出和语言无关的事实(facts,引用源码节点,不做正则匹配文字);翻译插件决定属性名、枚举词(公共 / 是 / 默认导出)和值怎么译,也给筛选属性和值起名(`attributeNames`)。筛选属性的通用部分由宿主从 facts 推出(`attributes.ts` 的 `blockAttributes`),语言插件只在 `BlockShape.attributes` 里补自己语言特有的。第三方语言插件给函数、类、枚举、异常、导入、常量、变量块填 facts 就能用上任何翻译插件的属性表;自己语言的字典 / 映射字面量填进 `DeclaratorFacts.entries` 就能展开成子行(中文模板直译按字面量的节点类型查叫法,见 `ZhTable.mapLiterals`)。写在表达式里的遍历(推导式这类)由语言插件的 `iteration` 给结构(节点 → `IterationFacts`),翻译插件排成一个 `SegmentGroup`(中文直译是样式 `iteration`、框头是结果类型的译名);webview 只认框的样式 id,不认语言。调用由语言插件的 `call` 给结构(节点 → `CallFacts`),跨几行写的由翻译插件排成一行字加一张表(`PreviewTable`,见「调用与构造」);webview 只画表。
|
- 分工:语言插件只从语法树读出和语言无关的事实(facts,引用源码节点,不做正则匹配文字);翻译插件决定属性名、枚举词(公共 / 是 / 默认导出)和值怎么译,也给筛选属性和值起名(`attributeNames`)。筛选属性的通用部分由宿主从 facts 推出(`attributes.ts` 的 `blockAttributes`),语言插件只在 `BlockShape.attributes` 里补自己语言特有的。第三方语言插件给函数、类、枚举、异常、导入、常量、变量块填 facts 就能用上任何翻译插件的属性表;自己语言的字典 / 映射字面量填进 `DeclaratorFacts.entries` 就能展开成子行(中文模板直译按字面量的节点类型查叫法,见 `ZhTable.mapLiterals`)。写在表达式里的遍历(推导式这类)由语言插件的 `iteration` 给结构(节点 → `IterationFacts`),翻译插件排成一个 `SegmentGroup`(中文直译是样式 `iteration`、框头是结果类型的译名);webview 只认框的样式 id,不认语言。调用由语言插件的 `call` 给结构(节点 → `CallFacts`),跨几行写的由翻译插件排成一行字加一张表(`PreviewTable`,见「调用与构造」);webview 只画表。
|
||||||
- 语言插件的信息只经契约到翻译插件:宿主翻每一块时把 `{ source, language }`(`SourceDocument`,language 是解析这份源码的语言插件)交给 `translate` / `describe`,翻译插件要词性、遍历结构都从 `language` 取,不 import 任何语言插件的模块(lint 规则挡住 `translators/` 下的非测试文件 import `languages/`)。所以第三方翻译插件(英文、AI 翻译)拿得到和内置翻译插件一样的信息,第三方语言插件也能配上任何翻译插件。
|
- 语言插件的信息只经契约到翻译插件:宿主翻每一块时把 `{ source, language }`(`SourceDocument`,language 是解析这份源码的语言插件)交给 `translate` / `describe`,翻译插件要词性、遍历结构都从 `language` 取,不 import 任何语言插件的模块(lint 规则挡住 `translators/` 下的非测试文件 import `languages/`)。所以第三方翻译插件(英文、AI 翻译)拿得到和内置翻译插件一样的信息,第三方语言插件也能配上任何翻译插件。
|
||||||
- 第三方语言插件要给的:`id`、`languageIds`、`grammarPath`、`toBlocks`(块的种类、头部、子块;函数、类、枚举、异常、导入、常量、变量块带 facts)、`tokenRole`。可以给的:facts 里语言有才填的项(async、abstract、typeOnly、access、doc、`DeclaratorFacts.entries`)、`BlockShape.attributes`(通用词表以外的筛选属性)、`iteration`(有写在表达式里的遍历的语言)、`call`(调用的结构)、`displayNames`(这门语言里给定义起中文名的写法)。没给的就少那一样:没有 entries 的值不展开,没有 `iteration` 的遍历照源码结构逐词翻,没有 `call` 的调用不摊成表,没有 `displayNames` 就没有中文名。
|
- 第三方语言插件要给的:`id`、`languageIds`、`grammarPath`、`toBlocks`(块的种类、头部、子块;函数、类、枚举、异常、导入、常量、变量块带 facts)、`tokenRole`。可以给的:facts 里语言有才填的项(async、abstract、typeOnly、access、doc、`DeclaratorFacts.entries`)、`BlockShape.attributes`(通用词表以外的筛选属性)、`iteration`(有写在表达式里的遍历的语言)、`call`(调用的结构)、`displayNames`(这门语言里给定义起中文名的写法)。没给的就少那一样:没有 entries 的值不展开,没有 `iteration` 的遍历照源码结构逐词翻,没有 `call` 的调用不摊成表,没有 `displayNames` 就没有中文名。
|
||||||
|
- 嵌着的代码的分工:外层的语言插件只报是哪几段(`LanguageAdapter.embedded`、`TextBlock.embedded`、`CommandForm`),宿主按语言找那种语言的语言插件、翻译插件、命令规则解析和分步(`LanguageAdapter.steps`),外层的翻译插件只问读出来的样子(`SourceDocument.embedded`),连词、条数这些叫法归它;两种语言的插件互不 import。
|
||||||
- 命令的分工:语言插件认出一条命令的结构(`LanguageAdapter.command`),命令规则按命令名读成译文(`SourceDocument.commandRule`,宿主按语言、注册顺序给),翻译插件读命令时问它、接上重定向;规则只从契约拿东西,不认识语言插件、翻译插件。
|
- 命令的分工:语言插件认出一条命令的结构(`LanguageAdapter.command`),命令规则按命令名读成译文(`SourceDocument.commandRule`,宿主按语言、注册顺序给),翻译插件读命令时问它、接上重定向;规则只从契约拿东西,不认识语言插件、翻译插件。
|
||||||
- 中文名的分工:语言插件只认写法(`DisplayNameReader.find`:定义处的名字、中文名、注释里的记号);宿主解析一次源码时顺带问它,记号放进 `SourceDocument.markers` 交给翻译插件(翻注释、说明时去掉,中文模板直译在 `ZhTable.commentText` 之前去掉),定义交给预览找用到它的各处(`nameResolution.ts`);翻译插件不认识 `@Comment`,webview 不认识任何语言。
|
- 中文名的分工:语言插件只认写法(`DisplayNameReader.find`:定义处的名字、中文名、注释里的记号);宿主解析一次源码时顺带问它,记号放进 `SourceDocument.markers` 交给翻译插件(翻注释、说明时去掉,中文模板直译在 `ZhTable.commentText` 之前去掉),定义交给预览找用到它的各处(`nameResolution.ts`);翻译插件不认识 `@Comment`,webview 不认识任何语言。
|
||||||
- 翻译插件拿到的:代码块(kind、node、header、children,属性表块还有 facts)和 `SourceDocument`。按语法节点类型写的叫法(关键字表、按语境换的译法、节点模板、注解位置这类只关乎措辞的判断)属于翻译插件自己,写在它的表里。
|
- 翻译插件拿到的:代码块(kind、node、header、children,属性表块还有 facts)和 `SourceDocument`。按语法节点类型写的叫法(关键字表、按语境换的译法、节点模板、注解位置这类只关乎措辞的判断)属于翻译插件自己,写在它的表里。
|
||||||
@@ -1318,13 +1416,16 @@ shell 脚本大半是一条条命令,一条命令读成什么取决于命令
|
|||||||
- 规则不认识的选项照抄在译文后面(`rm -rf --one-file-system x` → 删除 x(连同里面的东西、不提示) --one-file-system)。
|
- 规则不认识的选项照抄在译文后面(`rm -rf --one-file-system x` → 删除 x(连同里面的东西、不提示) --one-file-system)。
|
||||||
- 重定向不归规则:翻译插件读完命令再统一接上(见「Shell 脚本」),所以每条规则都不用管 `>&2`、`2>/dev/null`。
|
- 重定向不归规则:翻译插件读完命令再统一接上(见「Shell 脚本」),所以每条规则都不用管 `>&2`、`2>/dev/null`。
|
||||||
- 译文语言:内置规则只有中文词表,`CommandContext.displayLanguage` 不是 `zh` 时不读(照抄)。
|
- 译文语言:内置规则只有中文词表,`CommandContext.displayLanguage` 不是 `zh` 时不读(照抄)。
|
||||||
- 内置规则(`commands/shellCommands.ts`,认选项的共用办法在 `commands/reading.ts`:`-rf` 连写拆开,`-o 值` / `-o值` / `--retry 3` / `--retry=3` 都认,`--` 之后都不是选项):
|
- 管哪些语言:内置规则的 `languageIds` 是 shellscript 和 dockerfile(Dockerfile 数组写法的命令不经过 shell,执行的还是同一些程序)。
|
||||||
|
- 没有自己的规则的命令交给管 `'*'` 的规则:内置的只认只写了一个 `--version`(`-V`、`version`)的命令,读成「打印 pg_repack 的版本」,别的照抄。
|
||||||
|
- 选项多的:规则把认出的选项连同意思交给 `commands/reading.ts` 的 `explained`(选项在源码里的写法、写了几遍都由 `parseOptions` 记下),4 个以上(同一个写几遍算一个)或有写了几遍的,这一行后面写「· N 个选项」,意思排成「选项 | 意思」表(`CommandReading.table`,翻译插件排在这一行下面,同调用的实参表),写了几遍的并成一行「--no-install-package × 11 | 不装这些包:…」;别的写在括号里。
|
||||||
|
- 内置规则(文件、下载、打包、文本处理和 shell 自带的在 `commands/shellCommands.ts`,建用户、密钥、装文件、改属主和 `--version` 在 `commands/systemCommands.ts`,装软件包、装依赖、跑程序的在 `commands/packageCommands.ts`;认选项的共用办法在 `commands/reading.ts`:`-rf` 连写拆开,`-o 值` / `-o值` / `--retry 3` / `--retry=3` 都认,`--` 之后都不是选项):
|
||||||
|
|
||||||
| 命令 | 读成 |
|
| 命令 | 读成 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `rm -rf dest` | 删除 dest(连同里面的东西、不提示) |
|
| `rm -rf dest` | 删除 dest(连同里面的东西、不提示) |
|
||||||
| `mkdir -p d` | 建目录 d(连同上级目录) |
|
| `mkdir -p d` | 建目录 d(连同上级目录) |
|
||||||
| `curl -fL --retry 3 -o out url` | 下载 url 到 out(失败就报错、跟着跳转走、最多重试 3 次) |
|
| `curl -fL --retry 3 -o out url`、`curl -fsSL url` | 下载 url 到 out(失败就报错、跟着跳转走、最多重试 3 次)、请求 url(不打印进度、失败就报错、跟着跳转走;`-sS` 一起写只写「不打印进度」) |
|
||||||
| `tar -xzf f -C d`、`unzip -q f -d d` | 解压 tar.gz f 到 d、解压 zip f 到 d(不打印过程) |
|
| `tar -xzf f -C d`、`unzip -q f -d d` | 解压 tar.gz f 到 d、解压 zip f 到 d(不打印过程) |
|
||||||
| `find d -mindepth 1 -maxdepth 1 -type d ! -name m -exec rm -rf {} +` | 找出 d 下一层的目录,除了 m,逐个 删除(连同里面的东西、不提示);`find d -maxdepth 1 -name '*.dll' -delete` → 找出 d 这一层的 *.dll,逐个 删除;`find d -type l` → 找出 d 下面所有符号链接 |
|
| `find d -mindepth 1 -maxdepth 1 -type d ! -name m -exec rm -rf {} +` | 找出 d 下一层的目录,除了 m,逐个 删除(连同里面的东西、不提示);`find d -maxdepth 1 -name '*.dll' -delete` → 找出 d 这一层的 *.dll,逐个 删除;`find d -type l` → 找出 d 下面所有符号链接 |
|
||||||
| `cp -L a b`、`mv a b`、`mv a b d` | 复制 a 指向的实体文件 到 b、改名 a → b、移动 a、b 到 d |
|
| `cp -L a b`、`mv a b`、`mv a b d` | 复制 a 指向的实体文件 到 b、改名 a → b、移动 a、b 到 d |
|
||||||
@@ -1338,15 +1439,26 @@ shell 脚本大半是一条条命令,一条命令读成什么取决于命令
|
|||||||
| `du -sh a b` | 统计占用空间 a、b(按人读的单位、只看总数) |
|
| `du -sh a b` | 统计占用空间 a、b(按人读的单位、只看总数) |
|
||||||
| `cat f`、`grep -qi error log`、`sed -i 's/a/b/g' conf` | 打印 f 的内容、在 log 里找含 error 的行(只看找没找到、不分大小写)、把 conf 里的 a 全换成 b(直接改文件) |
|
| `cat f`、`grep -qi error log`、`sed -i 's/a/b/g' conf` | 打印 f 的内容、在 log 里找含 error 的行(只看找没找到、不分大小写)、把 conf 里的 a 全换成 b(直接改文件) |
|
||||||
| `chmod +x f`、`ln -sf t link`、`touch f` | 给 f 加上可执行权限、建符号链接 link → t(已有就替换)、更新修改时间 f(没有就建空文件) |
|
| `chmod +x f`、`ln -sf t link`、`touch f` | 给 f 加上可执行权限、建符号链接 link → t(已有就替换)、更新修改时间 f(没有就建空文件) |
|
||||||
| `source f`、`read -r x`、`sleep 1` | 载入并执行 f、读一行进 x(不认转义)、等 1 秒 |
|
| `source f` / `. f`、`read -r x`、`sleep 1` | 读入 f 里的变量和函数、读一行进 x(不认转义)、等 1 秒 |
|
||||||
| `continue`、`break`、`true` / `:`、`false` | 跳过这一个、跳出循环、什么也不做、失败 |
|
| `continue`、`break`、`true` / `:`、`false` | 跳过这一个、跳出循环、什么也不做、失败 |
|
||||||
|
| `apt-get update`、`apt-get install -y --no-install-recommends a b`、`apt-get purge -y x`、`apt-get clean` | 更新软件包列表、安装 a、b(不装推荐的包、不用确认)、卸载 x(连配置一起删、不用确认)、清掉下载的安装包 |
|
||||||
|
| `apk add --no-cache --virtual .deps gcc`、`apk del .deps` | 安装 gcc(不留索引缓存、记成一组 .deps)、卸载 .deps |
|
||||||
|
| `pip install --no-cache-dir -r requirements.txt`、`pip3 install -U pip` | pip 安装 requirements.txt 里列的包(不留缓存)、pip 安装 pip(升级到最新) |
|
||||||
|
| `npm ci --omit=dev`、`npm run build`、`npm run migrate` | npm 按 lock 文件装依赖(不装开发依赖)、npm 构建、npm 运行脚本 migrate(build / test / dev / start / lint 这些常见的脚本名写成要做的事) |
|
||||||
|
| `pnpm install --frozen-lockfile --ignore-scripts`、`pnpm build`、`yarn install --immutable` | pnpm 装依赖(严格按 lock 文件、不跑安装脚本)、pnpm 构建、yarn 装依赖(严格按 lock 文件) |
|
||||||
|
| `corepack enable`、`corepack prepare pnpm@latest --activate` | 开启 corepack、准备 pnpm latest(并设为当前用的版本) |
|
||||||
|
| `uv sync --frozen --no-dev`、`uv pip install --system -r req.txt`、`uv run --no-sync uvicorn app:app --host 0.0.0.0 --port 8000` | uv 按 lock 同步依赖(严格按 lock 文件,不改它、不装开发依赖)、uv 安装 req.txt 里列的包(装进系统的 Python)、用 uv 运行(不先同步依赖) uvicorn app:app,监听 0.0.0.0:8000(`uv run` 后面的命令照它自己的规则读) |
|
||||||
|
| `useradd -m -s /bin/false appuser`、`adduser --system --uid 1001 nextjs`、`addgroup --system --gid 1001 nodejs`、`adduser web www` | 新建用户 appuser(建主目录、不能登录)、新建系统用户 nextjs(uid 1001)、新建系统用户组 nodejs(gid 1001)、把 web 加进组 www(Debian 和 Alpine / BusyBox 的写法都认) |
|
||||||
|
| `gpg --dearmor -o k.gpg`、`install -d /d`、`install -m 644 a /etc/a`、`chown -R u:g /x` | 转成二进制密钥 写到 k.gpg、建目录 /d、安装 a 到 /etc/a(权限 644)、把 /x 的属主改成 u:g(连同里面的东西) |
|
||||||
|
| `pg_repack --version` | 打印 pg_repack 的版本(管 `'*'` 的规则) |
|
||||||
|
|
||||||
## 解析运行时
|
## 解析运行时
|
||||||
|
|
||||||
- 依赖 `@vscode/tree-sitter-wasm`(VS Code 团队维护,包含 web-tree-sitter 运行时和 TypeScript / TSX / JavaScript / Python 语法包,版本配套)。
|
- 依赖 `@vscode/tree-sitter-wasm`(VS Code 团队维护,包含 web-tree-sitter 运行时和 TypeScript / TSX / JavaScript / Python / Bash 语法包,版本配套)。它没有的语法包(Dockerfile)取自开发依赖 `tree-sitter-wasm`(Crysthamus/tree-sitter-wasm:各家语法包在 GitHub Actions 里编译,上游提交钉死,每个 wasm 都用 cosign 签名、发 npm 带来源证明;tree-sitter-dockerfile 是 ABI 15,和 `@vscode/tree-sitter-wasm` 的运行时配得上),`esbuild.mjs` 用它的 `getWasmPath` 取出来,和别的一起复制进 `dist/tree-sitter/`;单元测试直接用包里的(`testSupport.ts` 的 `DOCKERFILE_WASM_DIR`)。
|
||||||
- 打包:`esbuild.mjs` 构建时把 `tree-sitter.wasm` 和用到的语法包复制到 `dist/tree-sitter/`(`package.json` 的 `files` 已包含 `dist/**/*`)。
|
- 打包:`esbuild.mjs` 构建时把 `tree-sitter.wasm` 和用到的语法包复制到 `dist/tree-sitter/`(`package.json` 的 `files` 已包含 `dist/**/*`)。
|
||||||
- 运行时初始化一次 `Parser.init({ locateFile })` 指向 `dist/tree-sitter/tree-sitter.wasm`;语法包按路径懒加载并缓存。
|
- 运行时初始化一次 `Parser.init({ locateFile })` 指向 `dist/tree-sitter/tree-sitter.wasm`;语法包按路径懒加载并缓存。
|
||||||
- 文件扩展名到语法包:`.ts` → typescript;`.tsx` → tsx;`.js/.jsx/.mjs/.cjs` → javascript(tree-sitter-javascript 支持 JSX);`.py` → python;shellscript(`.sh` / `.bash` / `.zsh`、首行写着 shell 解释器的文件)→ bash。以 `languageId` 为准。
|
- 文件扩展名到语法包:`.ts` → typescript;`.tsx` → tsx;`.js/.jsx/.mjs/.cjs` → javascript(tree-sitter-javascript 支持 JSX);`.py` → python;shellscript(`.sh` / `.bash` / `.zsh`、首行写着 shell 解释器的文件)→ bash;dockerfile(`Dockerfile`、`Dockerfile.*`、`*.dockerfile`、`Containerfile`)→ dockerfile。
|
||||||
|
- 嵌着的代码只解析源码里的几段(`TreeSitterRuntime.parse` 的 `ranges`,交给 web-tree-sitter 的 `includedRanges`),节点下标、行列照旧是整份源码的。以 `languageId` 为准。
|
||||||
|
|
||||||
## 代码块树规则
|
## 代码块树规则
|
||||||
|
|
||||||
@@ -1399,6 +1511,14 @@ shell 脚本大半是一条条命令,一条命令读成什么取决于命令
|
|||||||
- `[ … ]`、`[[ … ]]`(前面可以带 `!`),和用 `&&` / `||` 连起来、全是条件或前面全是条件最后跟一句的一串 → if。
|
- `[ … ]`、`[[ … ]]`(前面可以带 `!`),和用 `&&` / `||` 连起来、全是条件或前面全是条件最后跟一句的一串 → if。
|
||||||
- `return` 命令 → return;别的命令(带不带重定向)、`&&` / `||` 连起来的一串命令 → command;其余语句 → statement。
|
- `return` 命令 → return;别的命令(带不带重定向)、`&&` / `||` 连起来的一串命令 → command;其余语句 → statement。
|
||||||
|
|
||||||
|
### Dockerfile
|
||||||
|
|
||||||
|
- 文件最开头连着的 `# syntax=` / `# escape=` / `# check=`(从第一行起、中间没空行)→ directive。
|
||||||
|
- 每个 `from_instruction` 开一个 stage,头部是 FROM 那一行,紧贴在上面(中间没空行)的几行注释是它的说明、头部往上延;到下一个阶段(连同它上面的说明)之前的指令、注释是子块;第一个 FROM 之前的照常是顶层的块。
|
||||||
|
- 相邻行的注释合成一块注释;只写了中文名记号、紧挨着下一条指令的注释算在那条指令的块里。
|
||||||
|
- `run_instruction` → run(整条指令,shell 写法的带 `embedded`);`copy_instruction`、`add_instruction` → copy;`label_instruction` → label;`env_instruction`、`arg_instruction` → variable(facts 的 scope 分构建参数、环境变量);WORKDIR / USER / EXPOSE / VOLUME / STOPSIGNAL / SHELL / MAINTAINER → setting;`healthcheck_instruction` → health;`cmd_instruction`、`entrypoint_instruction` → start;`onbuild_instruction` → setting,包着的指令照这些规则是它的子块;别的(语法错误)→ statement。
|
||||||
|
- 连着写的同种指令(紧挨着下一行)并成一块:ENV、ARG 并成一个变量块(declarators 是各条的全部),LABEL 和从同一个地方复制的 COPY 并成一块(`TextBlock.group`,头部盖住全部);ADD、带 heredoc 的 COPY 不并。
|
||||||
|
|
||||||
### 属性表信息(facts)从哪来
|
### 属性表信息(facts)从哪来
|
||||||
|
|
||||||
- TypeScript 函数:`function_declaration` / `generator_function_declaration` / `function_signature`、类里的 `method_definition` / 方法签名、值是带语句块主体函数的变量和类字段、`export default` 的函数值。名称取声明名 / 变量名 / 字段名;说明取紧贴在前面的 JSDoc 注释节点(见上面代码块树规则);参数来自 `formal_parameters`(`required_parameter` / `optional_parameter` 的 pattern、type、value,JavaScript 的 `assignment_pattern`),不带括号的单参数箭头函数取 `parameter`;返回类型取 `return_type` 里的类型;异步看有没有 `async`;类成员的装饰器是类主体里排在它前面的 `decorator` 兄弟节点。
|
- TypeScript 函数:`function_declaration` / `generator_function_declaration` / `function_signature`、类里的 `method_definition` / 方法签名、值是带语句块主体函数的变量和类字段、`export default` 的函数值。名称取声明名 / 变量名 / 字段名;说明取紧贴在前面的 JSDoc 注释节点(见上面代码块树规则);参数来自 `formal_parameters`(`required_parameter` / `optional_parameter` 的 pattern、type、value,JavaScript 的 `assignment_pattern`),不带括号的单参数箭头函数取 `parameter`;返回类型取 `return_type` 里的类型;异步看有没有 `async`;类成员的装饰器是类主体里排在它前面的 `decorator` 兄弟节点。
|
||||||
@@ -1411,6 +1531,7 @@ shell 脚本大半是一条条命令,一条命令读成什么取决于命令
|
|||||||
- TypeScript 枚举:`enum_declaration` 的 name、body;说明、访问级别同类(`export` 了公共、顶层没 `export` 私有、嵌套的没有);没有装饰器、父类。成员是 body 里的 `property_identifier`(没写值)和 `enum_assignment`(name、value),照源码顺序;没写值的接着上一个数往上加(第一个 0),上一个的值是数字字面量(可带正负号,`0x10` 这类也算)或算出来的数才算得出,否则没有 `computed`。说明:从上一个成员之后起单独成行的注释(`//` 和 JSDoc),和写在它这一行行尾的注释;同一行上一个成员后面的 JSDoc 讲的是下一个成员;最后一个成员后面的注释不算。
|
- TypeScript 枚举:`enum_declaration` 的 name、body;说明、访问级别同类(`export` 了公共、顶层没 `export` 私有、嵌套的没有);没有装饰器、父类。成员是 body 里的 `property_identifier`(没写值)和 `enum_assignment`(name、value),照源码顺序;没写值的接着上一个数往上加(第一个 0),上一个的值是数字字面量(可带正负号,`0x10` 这类也算)或算出来的数才算得出,否则没有 `computed`。说明:从上一个成员之后起单独成行的注释(`//` 和 JSDoc),和写在它这一行行尾的注释;同一行上一个成员后面的 JSDoc 讲的是下一个成员;最后一个成员后面的注释不算。
|
||||||
- Python 导入:`import_statement` 每个 name 一个来源、导入项是整个模块;`import_from_statement` 的 module_name 和各 name(`aliased_import` 拆出 alias),`wildcard_import` 是全部;`future_import_statement` 的来源是 `__future__`。
|
- Python 导入:`import_statement` 每个 name 一个来源、导入项是整个模块;`import_from_statement` 的 module_name 和各 name(`aliased_import` 拆出 alias),`wildcard_import` 是全部;`future_import_statement` 的来源是 `__future__`。
|
||||||
- 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` 命令。
|
- 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` 里的表达式。
|
- Python 变量:`assignment` 的 left / type / right,说明是紧跟在后面的属性 docstring 的字符串节点(见上面代码块树规则);right 是 `dictionary` 时 entries 是它的具名子节点(注释除外):`pair` 的 key / value(value 又是 `dictionary` 时连它的 entries)、`dictionary_splat` 里的表达式。
|
||||||
|
|
||||||
## 中文模板直译
|
## 中文模板直译
|
||||||
@@ -1436,7 +1557,7 @@ shell 脚本大半是一条条命令,一条命令读成什么取决于命令
|
|||||||
|
|
||||||
写在表达式里的遍历(推导式)不走节点模板:它的节数不定、要换成一个框,由语言插件的 `iteration`(翻译时从 `SourceDocument.language` 拿到)认出来,引擎按「推导式」一节的规则排进框里;和节点模板一样,没整个落在要翻的这一段里就不重排、照常翻。跨几行写的调用也不走节点模板:由语言插件的 `call` 认出来,引擎只在翻一行译文(`translate`)、变量 / 常量的值(`declaredValue`)和实参表里的值时按「调用与构造」一节排(每段一个 `TableSlot`,见 `expandedText`、`emitCall`、`argumentTable`),属性表别的值、库规则借的 `expression` 照常一行。
|
写在表达式里的遍历(推导式)不走节点模板:它的节数不定、要换成一个框,由语言插件的 `iteration`(翻译时从 `SourceDocument.language` 拿到)认出来,引擎按「推导式」一节的规则排进框里;和节点模板一样,没整个落在要翻的这一段里就不重排、照常翻。跨几行写的调用也不走节点模板:由语言插件的 `call` 认出来,引擎只在翻一行译文(`translate`)、变量 / 常量的值(`declaredValue`)和实参表里的值时按「调用与构造」一节排(每段一个 `TableSlot`,见 `expandedText`、`emitCall`、`argumentTable`),属性表别的值、库规则借的 `expression` 照常一行。
|
||||||
|
|
||||||
节点模板也排不了的写法(shell 的 `${x:-d}`、`[ -f x ]`、`case`、`for f in glob`)由翻译插件的节点读法排(`ZhTable.readers`:节点类型 → 读法,读法拿到 `ReaderContext`:照常翻子节点的 `say`、名字照抄带下标的 `name`、读一条命令的 `command`,能给一张排在这一段下面的表,分情况的「情况 \| 做什么」就是它;返回 undefined 照常翻)。一条命令由语言插件的 `command` 认出、按命令名交给 `SourceDocument.commandRule`(见「命令规则」),引擎接上重定向;没有规则时命令名照抄、各个词照常翻。解释器开关的叫法在 `ZhTable.switches`(errexit → 出错就停)。
|
节点模板也排不了的写法(shell 的 `${x:-d}`、`[ -f x ]`、`case`、`for f in glob`)由翻译插件的节点读法排(`ZhTable.readers`:节点类型 → 读法,读法拿到 `ReaderContext`:照常翻子节点的 `say`、名字照抄带下标的 `name`、读一条命令的 `command`,能给一张排在这一段下面的表,分情况的「情况 \| 做什么」就是它;返回 undefined 照常翻)。一条命令由语言插件的 `command` 认出、按命令名交给 `SourceDocument.commandRule`(见「命令规则」),引擎接上重定向,规则给了选项表就排在这一段下面;没有规则时命令名照抄(按函数着色)、各个词照常翻。解释器开关的叫法在 `ZhTable.switches`(errexit → 出错就停),解析指令的叫法在 `ZhTable.directives`(syntax → 语法),并成一块的几条(`TextBlock.group`)按第一条的节点类型查 `ZhTable.groups` 读成一行字加一张表。阶段、健康检查、启动、分构建参数 / 环境变量的变量这些属性表块的行由引擎按 facts 排;要执行的命令(`CommandForm`)数组写法照常翻(交给命令规则),shell 写法问 `SourceDocument.embedded`,几步用「然后」(一步失败就停的)或分号连起来。
|
||||||
|
|
||||||
TypeScript 关键字表(流程 = keyword,声明 = storage):
|
TypeScript 关键字表(流程 = keyword,声明 = storage):
|
||||||
|
|
||||||
@@ -1559,7 +1680,7 @@ type BlockView = BlockViewBase & ({ translation: PreviewText, table?: PreviewTab
|
|||||||
|
|
||||||
带自己颜色的段(`Segment.color`,库规则按库的配色给,例如 FastAPI 的 HTTP 方法、成功的状态码):暗色、亮色两个写在元素的 style 上(`--color-dark` / `--color-light`),`.cr-colored` 按主题挑一个,盖过词性的颜色;词性照旧报(`role`)。
|
带自己颜色的段(`Segment.color`,库规则按库的配色给,例如 FastAPI 的 HTTP 方法、成功的状态码):暗色、亮色两个写在元素的 style 上(`--color-dark` / `--color-light`),`.cr-colored` 按主题挑一个,盖过词性的颜色;词性照旧报(`role`)。
|
||||||
|
|
||||||
种类(种类图标的颜色 + 块底 7% + 边框 35%;亮色主题同色系;图标见「预览(代码块树)」):import 导入 #c586c0;function 函数 #dcdcaa;class 类、type 类型 #4ec9b0;enum 枚举 #b180d7(亮色 #652d90);exception 异常 #f14c4c(亮色 #cd3131,和尝试、捕获、最终同一色系,更红一些);field 字段、const 常量、variable 变量 #9cdcfe;if 条件、else 否则 #ce9178;loop 循环 #8f9cff;try 尝试、catch 捕获、finally 最终 #f48771;return 返回 #89d185;statement 语句 #9d9d9d;comment 注释 #6a9955(虚线框);Shell 的 interpreter 解释器同语句 #9d9d9d,options 选项 #e5c07b(亮色 #9a6a00),case 分情况同条件 #ce9178,command 命令、pipeline 管道 #d7ba7d(亮色 #7c5f12);库规则的种类用规则给的颜色(`badge.color` 的 dark / light,webview 写在块的 style 上按主题挑),内置 SQLAlchemy 数据表 #e8a860 / #a3590d,Celery 应用 #98c379 / #4d7f17,定时任务 #56b6c2 / #127f8c,数据类 #ef7fb0 / #b3306c,FastAPI 接口 #61afef / #1a6fb5,FastAPI 路由器 #7aa2f7 / #34548a。编辑状态:底色 `--vscode-editor-background`,边框 `--vscode-focusBorder`。
|
种类(种类图标的颜色 + 块底 7% + 边框 35%;亮色主题同色系;图标见「预览(代码块树)」):import 导入 #c586c0;function 函数 #dcdcaa;class 类、type 类型 #4ec9b0;enum 枚举 #b180d7(亮色 #652d90);exception 异常 #f14c4c(亮色 #cd3131,和尝试、捕获、最终同一色系,更红一些);field 字段、const 常量、variable 变量 #9cdcfe;if 条件、else 否则 #ce9178;loop 循环 #8f9cff;try 尝试、catch 捕获、finally 最终 #f48771;return 返回 #89d185;statement 语句 #9d9d9d;comment 注释 #6a9955(虚线框);Shell 的 interpreter 解释器同语句 #9d9d9d,options 选项 #e5c07b(亮色 #9a6a00),case 分情况同条件 #ce9178,command 命令、pipeline 管道 #d7ba7d(亮色 #7c5f12);Dockerfile 的 directive 解析指令同语句,stage 阶段 #56b6c2(亮色 #127f8c),run 执行同命令,copy 复制同导入,setting 设置同选项,label 标签同变量,health 健康检查同尝试 / 捕获,start 启动同返回(原型 DOCKER_KINDS 的颜色);库规则的种类用规则给的颜色(`badge.color` 的 dark / light,webview 写在块的 style 上按主题挑),内置 SQLAlchemy 数据表 #e8a860 / #a3590d,Celery 应用 #98c379 / #4d7f17,定时任务 #56b6c2 / #127f8c,数据类 #ef7fb0 / #b3306c,FastAPI 接口 #61afef / #1a6fb5,FastAPI 路由器 #7aa2f7 / #34548a。编辑状态:底色 `--vscode-editor-background`,边框 `--vscode-focusBorder`。
|
||||||
|
|
||||||
## 代码位置
|
## 代码位置
|
||||||
|
|
||||||
@@ -1568,7 +1689,7 @@ vscode/src/codeReader/
|
|||||||
contract.ts 通用契约
|
contract.ts 通用契约
|
||||||
registry.ts 注册表 + CodeReaderApi
|
registry.ts 注册表 + CodeReaderApi
|
||||||
runtime.ts web-tree-sitter 初始化、语法包加载、解析
|
runtime.ts web-tree-sitter 初始化、语法包加载、解析
|
||||||
blockView.ts CodeBlock → BlockView(源码着色、去缩进、调用翻译)
|
blockView.ts CodeBlock → BlockView(源码着色、去缩进、调用翻译);嵌着的代码:解析那几段、分步、换那种语言的翻译插件转子块、着色叠一层(EmbeddedPart)
|
||||||
attributes.ts 块的筛选属性(从 facts 推出通用属性 + 语言自己报的)、文件里出现过的属性和块数
|
attributes.ts 块的筛选属性(从 facts 推出通用属性 + 语言自己报的)、文件里出现过的属性和块数
|
||||||
libraryRules.ts 库规则的宿主部分:按导入启用、按注册顺序认领、出错退回、编辑范围和留下的子块、查规则标的引用
|
libraryRules.ts 库规则的宿主部分:按导入启用、按注册顺序认领、出错退回、编辑范围和留下的子块、查规则标的引用
|
||||||
workspaceIndex.ts 工作区索引(库规则的 WorkspaceIndex,和宿主自己的中文名索引)的宿主部分:建、缓存、跟着文件改动更新,查到的项带着认的时候给的东西(不碰 VS Code)
|
workspaceIndex.ts 工作区索引(库规则的 WorkspaceIndex,和宿主自己的中文名索引)的宿主部分:建、缓存、跟着文件改动更新,查到的项带着认的时候给的东西(不碰 VS Code)
|
||||||
@@ -1582,6 +1703,8 @@ vscode/src/codeReader/
|
|||||||
rules/fastapi.ts 内置 FastAPI 库规则(Python):路由器卡片(接口表)、接口卡片(请求表、响应表),参数照 FastAPI 的规则放,异常类的状态码、类型别名、枚举经索引查
|
rules/fastapi.ts 内置 FastAPI 库规则(Python):路由器卡片(接口表)、接口卡片(请求表、响应表),参数照 FastAPI 的规则放,异常类的状态码、类型别名、枚举经索引查
|
||||||
commandRules.ts 命令规则的宿主部分:命令名 → 规则(先注册的算),规则出错记下、当它没读
|
commandRules.ts 命令规则的宿主部分:命令名 → 规则(先注册的算),规则出错记下、当它没读
|
||||||
commands/reading.ts 命令规则共用的:认选项(连写拆开、带值的、长选项、--)、拼译文(动作、括号里的意思、照抄不认识的选项)
|
commands/reading.ts 命令规则共用的:认选项(连写拆开、带值的、长选项、--)、拼译文(动作、括号里的意思、照抄不认识的选项)
|
||||||
|
commands/systemCommands.ts 内置命令规则:新建用户 / 用户组(useradd、adduser、addgroup / groupadd)、gpg、install、chown,管 '*' 的只问版本的命令
|
||||||
|
commands/packageCommands.ts 内置命令规则:apt-get / apt、apk、pip / pip3、npm、pnpm、yarn、corepack、uv(sync、pip、run……)、uvicorn
|
||||||
commands/shellCommands.ts 内置 shell 命令规则(rm、mkdir、curl、tar、unzip、find、cp、mv、echo / printf、exit、cd、pwd、basename、dirname、readlink、shasum、cut、du、cat、grep、sed、chmod、ln、touch、source、read、sleep、continue / break……)
|
commands/shellCommands.ts 内置 shell 命令规则(rm、mkdir、curl、tar、unzip、find、cp、mv、echo / printf、exit、cd、pwd、basename、dirname、readlink、shasum、cut、du、cat、grep、sed、chmod、ln、touch、source、read、sleep、continue / break……)
|
||||||
localSymbols.ts 语言插件认的同一文件里的名字怎么用:盖住位置的名字、跳到哪处定义、各处出现(不碰 VS Code)
|
localSymbols.ts 语言插件认的同一文件里的名字怎么用:盖住位置的名字、跳到哪处定义、各处出现(不碰 VS Code)
|
||||||
ruleLookups.ts 库规则认领时查别的文件里的定义(RuleContext.lookup):索引 + 语言扩展查定义(不碰 VS Code)
|
ruleLookups.ts 库规则认领时查别的文件里的定义(RuleContext.lookup):索引 + 语言扩展查定义(不碰 VS Code)
|
||||||
@@ -1598,11 +1721,13 @@ vscode/src/codeReader/
|
|||||||
languages/pythonNames.ts Python 的中文名写法(Annotated、@Comment、行尾记号、docstring)
|
languages/pythonNames.ts Python 的中文名写法(Annotated、@Comment、行尾记号、docstring)
|
||||||
languages/bash.ts bash / sh / zsh(shellscript):代码块树、facts(解释器、选项、按位置的参数、输出、返回)、command(命令的结构、重定向)、symbols(同一文件里的函数、变量)
|
languages/bash.ts bash / sh / zsh(shellscript):代码块树、facts(解释器、选项、按位置的参数、输出、返回)、command(命令的结构、重定向)、symbols(同一文件里的函数、变量)
|
||||||
languages/bashNames.ts Shell 的中文名写法(行尾 # @Comment)
|
languages/bashNames.ts Shell 的中文名写法(行尾 # @Comment)
|
||||||
|
languages/dockerfile.ts Dockerfile:代码块树(阶段、并块)、facts(解析指令、阶段、健康检查、启动、分作用范围的变量)、embedded(RUN 这些 shell 在哪几段)、command(数组写法的命令)、symbols(阶段名、变量)、中文名
|
||||||
languages/typescriptNames.ts TypeScript / JavaScript 的中文名写法(@Comment 装饰器、行尾记号、JSDoc)
|
languages/typescriptNames.ts TypeScript / JavaScript 的中文名写法(@Comment 装饰器、行尾记号、JSDoc)
|
||||||
translators/zhTemplate.ts 模板直译引擎(按语言的表驱动,词性、遍历结构从 SourceDocument.language 取;推导式排进框见 iterationGroup)
|
translators/zhTemplate.ts 模板直译引擎(按语言的表驱动,词性、遍历结构从 SourceDocument.language 取;推导式排进框见 iterationGroup)
|
||||||
translators/typescriptZh.ts TS 表
|
translators/typescriptZh.ts TS 表
|
||||||
translators/pythonZh.ts Python 表(含注解位置的判断)
|
translators/pythonZh.ts Python 表(含注解位置的判断)
|
||||||
translators/bashZh.ts Shell 表:节点读法(展开、特殊变量、条件、通配、&& / ||、管道、遍历、分情况)、开关的叫法
|
translators/bashZh.ts Shell 表:节点读法(展开、特殊变量、条件、通配、&& / ||、管道、遍历、分情况)、开关的叫法
|
||||||
|
translators/dockerfileZh.ts Dockerfile 表:各条指令的读法(RUN 问嵌着的 shell 读出来的样子)、并块的读法(COPY、LABEL)、解析指令的叫法
|
||||||
explorer/fileTree.ts 侧栏文件树(FileTreeDecorator:给行添信息的接口;showChanges:审查工单时只摆 PR 改过的文件)
|
explorer/fileTree.ts 侧栏文件树(FileTreeDecorator:给行添信息的接口;showChanges:审查工单时只摆 PR 改过的文件)
|
||||||
explorer/copyText.ts 复制菜单的命令和复制出什么(纯函数)
|
explorer/copyText.ts 复制菜单的命令和复制出什么(纯函数)
|
||||||
explorer/copyCommands.ts 复制命令:写剪贴板、状态栏提示
|
explorer/copyCommands.ts 复制命令:写剪贴板、状态栏提示
|
||||||
@@ -1672,6 +1797,12 @@ 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`。
|
- 语言插件(`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")`、常量;字符串色 / 变量色 / 常量色;规则出错照抄命令并报告。
|
- 中文直译(`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`)。
|
- 宿主:命令规则按语言、注册顺序给(`registry.test.ts`),shellscript 的 document 消息带命令规则读的译文、shell 的种类名、同一文件里的名字(`editor/documentMessage.test.ts`),跳到之前最近的定义、定义处算写入(`localSymbols.test.ts`)。
|
||||||
|
- 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 着色。
|
||||||
|
- shell 的分步(`languages/bash.test.ts`):只用 && 连着的拆开、整串带着的重定向算在最后一条上,条件串、有 || 的、分几句写的不拆;`a && echo x > f` 在 shell 脚本里也把重定向写在 echo 上(`translators/bashZh.test.ts`)。
|
||||||
|
- 命令规则(`commands/buildCommands.test.ts`):装软件包、装依赖、跑程序、建用户、密钥、装文件、改属主各条的读法;选项到 4 个、写了几遍时排成表;`'*'` 的规则只认 `--version`;命令名 → 规则先注册的算、没有的交给 `'*'`、出错记下来当没读。
|
||||||
|
- 宿主(`editor/documentMessage.test.ts`):注册了 shell 和 Dockerfile 时 RUN 的各步是子块、按 shell 的种类,筛选栏有 Dockerfile 的种类名,名字连同 shell 里的一起交出。
|
||||||
- 注册表:注册 / 注销后 `languageIds` 与可选翻译正确;同一语言多个翻译时按 `globalState` 选中;库规则按语言过滤、保持注册顺序,注销后不再给。
|
- 注册表:注册 / 注销后 `languageIds` 与可选翻译正确;同一语言多个翻译时按 `globalState` 选中;库规则按语言过滤、保持注册顺序,注销后不再给。
|
||||||
- 库规则(宿主):导入了库(`from sqlalchemy.orm import …`、`import sqlalchemy as sa`、`import os, sqlalchemy`、函数里的导入)才启用,`sqlalchemy_utils`、相对导入 `.sqlalchemy` 不算;先注册的规则优先;规则抛错、替身不合约定时记下来、接着问下一个、都没认照常显示,document 消息不带 error;编辑范围是整个类、没并进来的子块(夹在中间的注释、两个字段中间的方法)照源码顺序留下,卡片「编辑」写回整个类,能编辑的范围只在块和它的子孙之间重叠;筛选的种类计数、名字取规则的标签;规则自己报的筛选属性名字取规则的 `attributeNames`(翻译插件给了的以它为准,`attributes.test.ts` 的合并、`documentMessage.test.ts` 的「请求方法」),规则起名时抛错记下来、照写 id;`rules/` 下的非测试文件只 import contract。
|
- 库规则(宿主):导入了库(`from sqlalchemy.orm import …`、`import sqlalchemy as sa`、`import os, sqlalchemy`、函数里的导入)才启用,`sqlalchemy_utils`、相对导入 `.sqlalchemy` 不算;先注册的规则优先;规则抛错、替身不合约定时记下来、接着问下一个、都没认照常显示,document 消息不带 error;编辑范围是整个类、没并进来的子块(夹在中间的注释、两个字段中间的方法)照源码顺序留下,卡片「编辑」写回整个类,能编辑的范围只在块和它的子孙之间重叠;筛选的种类计数、名字取规则的标签;规则自己报的筛选属性名字取规则的 `attributeNames`(翻译插件给了的以它为准,`attributes.test.ts` 的合并、`documentMessage.test.ts` 的「请求方法」),规则起名时抛错记下来、照写 id;`rules/` 下的非测试文件只 import contract。
|
||||||
- 同一行不在两处能改(webview `editing.test.ts`):卡片编辑时盖住它留下的子块(字段中间的方法连同主体),跳到被盖住的块改闪卡片;只编辑头部的块不盖住子块,一行写完的块盖住同一行的子块;点「编辑」时重叠的块退出编辑;任意点法下正在编辑、画出来的块两两不重叠。
|
- 同一行不在两处能改(webview `editing.test.ts`):卡片编辑时盖住它留下的子块(字段中间的方法连同主体),跳到被盖住的块改闪卡片;只编辑头部的块不盖住子块,一行写完的块盖住同一行的子块;点「编辑」时重叠的块退出编辑;任意点法下正在编辑、画出来的块两两不重叠。
|
||||||
|
|||||||
Reference in new issue
Block a user