news 2026/9/19 23:57:30

Slang 编译器 AST 中 Modifier 与 Attribute 参考文档的自动化审查与修复实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Slang 编译器 AST 中 Modifier 与 Attribute 参考文档的自动化审查与修复实践
  • 编译器
  • 图形学
  • 编程语言

【免费下载链接】slang

Making it easier to work with shaders

项目地址:https://gitcode.com/GitHub_Trending/sl/slang
点击查看免费下载

本文以 Slang 仓库中 ast-reference/modifiers.md 修复报告 为骨架,系统讲解 Slang 编译器前端 AST 中Modifier/Attribute两大语法族系的参考文档结构、其背后的类继承体系与源码落点,以及该文档所经历的"机器审查(review)— 机器修复(remediation)"质量保障流水线。读完本文,读者既能快速定位某个[attr]或关键字在 AST 中对应哪个类、字段如何声明、语义检查在哪里进行,也能理解生成式技术文档如何通过 finding 驱动的闭环迭代保证与源码一致。

一、这份参考文档解决什么问题

Modifiers and Attributes Reference(modifiers.md)是写给在 Slang 编译器的parser、checker 或 backend emit中工作的贡献者使用的:当他们在源码中看到一个[attr]或关键字修饰符时,需要知道它最终变成 AST 中的哪一个类、携带哪些字段、语义检查在哪里完成。该页面向导式地覆盖了slang-ast-modifier.h中每一个具体(concrete)Modifier子类,Modifier基类本身则在 base.md 中单独记录。

1.1 Modifier 与 Attribute 的分界

Slang 有意将两族语法区分开:

  • Modifier(修饰符):不带参数列表的关键字标记,如inoutinoutconststaticuniformgloballycoherentnoperspective等,每个关键字对应一个独立的Modifier子类。
  • Attribute(属性):采用[name(args)]语法,派生自AttributeBase(而AttributeBase本身又派生自Modifier)。具体的属性类(UnrollAttributeNumThreadsAttribute等)持有解析后的参数及检查阶段(checking)产生的元数据。

两者最终都以链接形式挂在ModifiableSyntaxNode::modifiers上,并通过findModifier<T>()/hasModifier<T>()遍历查找,因此大多数消费代码无需区分二者。

1.2 文档的五个组成部分

页面主体由五部分构成,这一骨架也是本文后续展开的脉络:

章节内容
## Source类声明位置(头文件)、解析入口(parser)、表面拼写(spelling)的来源文件
## Family hierarchy一张 mermaid 继承关系图,覆盖全部具体类及其抽象中间层
## Nodes264 个具体类的编目:按 26 个功能分组表格列出,每行含 Class / Parent / Key fields / Grammar / Summary 五列
## Notable nodes对易混淆、易误解的重点节点组的深度讲解
## See also指向 base/declarations/expressions/types/values 及语法、能力系统等关联页面

二、文档的源码与拼写落点(Source)

参考文档明确指出,modifier 类声明于 slang-ast-modifier.h,建立在 slang-ast-base.h 中的Modifier/ModifiableSyntaxNode之上;解析发生在 slang-parser.cpp:

  • parseAttributeNameParseSquareBracketAttributes处理[name(args)]形式;
  • parseUncheckedGLSLLayoutAttribute处理 GLSLlayout(...)分组中的带值条目;
  • 属性通过AttributeDecl表分发,该表记录于 keywords-and-builtins.md。

2.1 表面拼写(spelling)的四个来源

属性在源码中写成的表面拼写声明在 C++ 头文件里,而是来自attribute_syntax声明。修复报告 F-006 澄清了这一点的精确表述:拼写来自四个源文件——三个核心模块.meta.slang文件加一个实验性标准模块:

文件负责的属性族
core.meta.slang主体(绝大多数属性)
diff.meta.slang可微性(differentiability)属性
hlsl.meta.slangVulkan 指针属性
workgraph.slang工作图(work-graph)节点属性

这一区分对维护者意义重大:workgraph.slang位于source/standard-modules/experimental/下,是实验性标准模块,而非核心模块源。修复前文档误称"四个核心模块源",现已更正。同时这四个文件都被页面列为 watched path——任何拼写改名都会使页面标记为 stale。

三、Nodes 编目:264 个具体类的组织方式

## Nodes是文档体量最大的部分。每个具体类占一行,统一五列格式:

  • Class:类名;
  • Parent:直接父类;
  • Key fieldsname: Type形式的自有字段,若无则写(no additional state)
  • Grammar:对应语法(链接到 grammar.md#modifiers 或#attributes-and-decorations),合成节点为(none)
  • Summary:一句话说明该类承载的语义。

3.1 继承自 AttributeBase 的公共字段

页面开篇有一段重要的统一说明:每个属性都从AttributeBase继承三个字段——attributeDecl: AttributeDecl*originalIdentifierToken: Tokenargs: List<Expr*>;每个已检查(checked)的Attribute还额外带intArgVals: List<Val*>。许多属性类自身不声明任何成员,直接读取继承列表中的参数,这类行在 Key fields 列写继承的args: List<Expr*>,把每个参数的含义留给 Summary 列说明;而(no additional state)表示该类不携带任何数据。

这与源码完全吻合:slang-ast-modifier.h 第 809-820 行声明的AttributeBase正是:

// Base class for checked and unchecked `[name(arg0, ...)]` style attribute. FIDDLE(abstract) class AttributeBase : public Modifier { FIDDLE(...) FIDDLE() AttributeDecl* attributeDecl = nullptr; // The original identifier token representing the last part of the qualified name. Token originalIdentifierToken; FIDDLE() List<Expr*> args; };

3.2 26 个功能分组一览

264 个具体类分布在 26 张表中,覆盖的范畴包括:参数方向与存储类修饰符、可见性修饰符、override/require/export/import 样板、HLSL 存储类修饰符、插值模式、矩阵布局、几何/网格着色器输入、HLSL 语义(: SV_*)、GLSL 预处理/布局/格式、类型修饰符(包裹类型而非声明)、内部/合成修饰符、内在函数与目标绑定修饰符、隐式参数组机制、属性基础(AttributeBaseUncheckedAttribute)、编译期提示属性(循环/分支/优化级别)、能力/目标属性、布局/绑定属性、未检查 GLSL 布局属性、阶段特定入口点属性、工作图节点属性、光线追踪属性、可变性/自动微分注解、可微性属性、继承控制属性、CUDA/Python/FFI 属性。

以下选取几组有代表性的表格行,展示其信息密度:

HLSL 语义(: SV_*

ClassParentKey fieldsSummary
HLSLLayoutSemanticHLSLSemanticregisterName: Token,componentMask: Token影响布局(register / packoffset)的 HLSL 语义基类
HLSLRegisterSemanticHLSLLayoutSemanticspaceName: Token, plus inheritedregisterName: register(...)
HLSLPackOffsetSemanticHLSLLayoutSemanticuniformOffset: int: packoffset(...)
HLSLSimpleSemanticHLSLSemanticname: Token: NAME(无括号参数)

内在函数与目标绑定

ClassParentKey fieldsSummary
IntrinsicOpModifierModifieropToken: Token,op: uint32_t将声明绑定到 Slang IR 操作码(核心模块内在函数)
TargetIntrinsicModifierModifiertargetToken: Token,definitionString: String,predicateToken: Token,scrutineeDeclRef: DeclRef<Decl>将声明绑定到目标后端内在函数
SpecializedForTargetModifierModifiertargetToken: Token标记针对某目标特化的声明
NVAPISlotModifierModifierregisterName: String,spaceName: String来自NV_SHADER_EXTN_SLOT/NV_SHADER_EXTN_REGISTER_SPACE宏的 NVAPI 槽位绑定

编译期提示属性UnrollAttribute[unroll(N)],提示转发给下游编译器,Slang 自身不改行为)、ForceUnrollAttribute[ForceUnroll(N)],Slang 自己完成展开,输出中不留循环)、LoopAttribute[loop])、FlattenAttribute[flatten])、NoInlineAttribute[noinline])等。

工作图节点属性NodeLaunchAttribute[NodeLaunch("broadcasting" | "thread" | "coalescing")])、NodeMaxDispatchGridAttribute(动态网格上界)、NodeDispatchGridAttribute(固定网格大小)、MaxRecordsAttributeNodeIDAttributeNodeIsProgramEntryAttributeAllowSparseNodesAttributeNodeArraySizeAttribute——其拼写声明在实验性 workgraph.slang,而非core.meta.slang

四、重点节点深入(Notable nodes)

## Notable nodes是全页最具解释力的部分,其中的知识点与修复报告的多项 finding 直接对应。

4.1 修饰符与属性的解析差异

解析器按语法区分二者:修饰符是 parser 按名字认知并立即构造对应子类的裸关键字;属性则是[ident(args)]结构,parser 先构建UncheckedAttribute,checker 再通过查找AttributeDecl将其解析为具体的Attribute子类(见 declarations.md)。

有几个容易遗漏的表面拼写规则:

  • 一组可以写成[a][[a]],多个属性可共享一组;
  • 属性之间的逗号是可选的,[a, b][a b]解析结果相同;
  • parseAttributeName会把::限定名拍平为单个标识符(每个::替换为_),所以用户写[vk::binding(0)]实际解析为注册名vk_bindingAttributeDecl;前导::变成前导_

4.2 IntrinsicOpModifier:核心模块到 IR 的桥

IntrinsicOpModifier是 Slang 函数声明与它将要 lower 到的 IR 操作码之间的桥梁。例如core.meta.slangsin的声明携带IntrinsicOpModifier,其op字段就是sin的 IR 操作码;IR lowering 阶段据此发出正确的操作码,而无需按函数名特判(见 core-module.md)。

4.3 TargetIntrinsicModifier 的 target 与 predicate 之辨(对应 F-005)

这是修复报告 F-005 澄清的核心概念。源码中 slang-ast-modifier.h 第 281-301 行的TargetIntrinsicModifier声明了:

// Token that names the target that the operation is an intrinsic for. FIDDLE() Token targetToken; // A custom definition for the operation, one of either an ident or a // string (the concatenation of several string literals) Token definitionIdent; FIDDLE() String definitionString; bool isString; // A predicate to be used on an identifier to guard this intrinsic Token predicateToken; NameLoc scrutinee; FIDDLE() DeclRef<Decl> scrutineeDeclRef;

修复前的文档称谓词"仅在能力生效时应用",把目标选择错误地归因于谓词。事实上二者相互独立:targetToken命名目标后端,由它决定选择哪个目标的哪个能力;而可选的 predicate 是另一道独立守卫,通过scrutineeDeclRef解析到的声明来守卫该内在函数(例如 HLSL 的"fma"、SPIR-V 的"OpExtInst ..."这类按目标写的文本内在函数)。SpecializedForTargetModifier则是纯按目标标记的修饰符,checker 在 emit 时优先选用带它的函数声明。

4.4 GLSLLayout* 家族与绑定信息的表示(对应 F-004)

GLSL 的layout(...)限定符编译为一串布局修饰符:一个GLSLLayoutModifierGroupBegin、每个限定符一个条目(如UncheckedGLSLBindingLayoutAttributeUncheckedGLSLLocationLayoutAttribute等具体子类)、最后跟一个GLSLLayoutModifierGroupEnd。"Unchecked"前缀表示 parser 阶段的表示,checker 会把每个条目解析为Attribute根系的等价类(如GLSLBindingAttributeGLSLLocationAttribute)。

修复报告 F-004 指出:这些修饰符正是参数绑定在 AST 中的表示层。已检查的GLSLBindingAttribute持有解析后的binding: int32_tset: int32_t对;HLSL 侧以 Token 形式存储同样的信息——HLSLLayoutSemantic持有registerName: TokencomponentMask: TokenHLSLRegisterSemantic追加spaceName: Token表示寄存器空间。这些声明在 slang-ast-modifier.h 第 478-499 行附近可以得到确认。需要强调的是:修饰符只记录声明写了什么,把这些值变成实际绑定的规则属于 checking 与 layout 阶段,详见 03-semantic-check.md。

4.5 可见性与语言版本

声明是publicinternal还是private,编码为挂在声明上的VisibilityModifier子类。默认值取决于模块的ModuleDecl::languageVersiondefaultVisibility:旧版 Slang 把一切视为public,现代语言默认internal

4.6 可微性属性族:类到拼写并非一一对应

DifferentiableAttribute(头文件第 1715-1762 行)之下是一个小层级:标记属性([ForwardDifferentiable][BackwardDifferentiable])描述函数"可微"这一事实;UserDefinedDerivativeAttribute子类([ForwardDerivative(fn)][BackwardDerivative(fn)])显式绑定导数函数;DerivativeOf子类([ForwardDerivativeOf(fn)]等)声明某函数是另一函数的导数。

最容易踩坑的是类到拼写的映射不是一对一DifferentiableAttribute是实践中的抽象基类,没有自己的attribute_syntax;用户代码里最常见的[Differentiable(order = 0)]映射到BackwardDifferentiableAttribute,与显式[BackwardDifferentiable(order = 0)]完全一致,只有[ForwardDifferentiable]映射到ForwardDifferentiableAttribute。此外,该属性是前端标记而非后续阶段回读的事实:checking 接受一个[Differentiable]函数f时,会合成extension __func_as_type(f) : IForwardDifferentiable<__func_as_type(f)>形式的 conformance,checker 的可微性查询(SemanticsVisitor::isFuncForwardDifferentiable及其 backward 对应物)返回的是SubtypeWitness*而非直接读修饰符的布尔值,下游 autodiff 工作基于该 witness 展开。

4.7 合成修饰符、GLSL 内存限定符聚合、缺失的基类

  • 合成修饰符ToBeSynthesizedModifierSynthesizedModifierIgnoreForLookupModifierVarReassignedModifierExistentialOpenedOnVarModifier等不来自用户语法,由 checker 添加、后续阶段检查。
  • 内存限定符聚合:GLSL 允许对同一声明施加多个内存限定符(coherentvolatilereadonlywriteonlyrestrict),checker 可将其聚合为单个带标志位掩码的MemoryQualifierSetModifier(字段memoryQualifiers: uint32_tmemoryModifiers: List<Modifier*>),而独立的GLSLReadOnlyModifier等仍存在于 parse 阶段。
  • 缺失的基类(F-007 保留的源码事实):不存在HLSLAttributeLayoutModifierHLSLLayoutModifier类,这三个名字在source/下任何地方都查无此名。HLSL 着色器阶段属性(如NumThreadsAttributeEntryPointAttribute)直接派生自Attribute,没有 HLSL 专用中间层;布局相关职责分散在HLSLLayoutSemantic(影响布局的语义)、MatrixLayoutModifier(矩阵存储布局)与GLSLLayout*Modifier组之间。

五、审查与修复流水线:从 review report 到 remediation report

modifiers.md是一份生成式文档(front matter 标注generated: true并有警告 "Auto-generated. May drift from source. Do not edit by hand."),其质量由一条"审查报告 → 修复报告"的机器流水线保障:

  1. review:审查报告(由gpt-5.6-sol生成,2026-08-04)对照源码提交53b76e6…逐项核对,产出 8 个 finding(4 major、4 minor,无 critical),并按 checklist(factual_accuracy / cross_references / completeness / style_consistency / source_alignment / front_matter_validity)打分。
  2. remediation:修复报告(由claude-opus-5生成)对每个 finding 给出动作与修复摘要。
  3. 结论汇总:5 个修复(fixed)、2 个推迟(deferred)、1 个超范围拒绝(rejected out-of-scope)、0 个升级。修复后页面 54,950 字节,仍在其 65,536 字节上限之下。

审查环节的核实力度值得参考:它对照了全部 273 个 FIDDLE 声明的类(264 个具体类恰好各出现一次,9 个抽象类全部被排除)、抽查了 10 余处事实断言、解析了全部 73 个相对链接(21 个唯一目标)、扫描了 627 个反引号标识符与 127 个括号属性拼写,并重新计算了七文件 watched-path digest。

六、八个 finding 逐一解读

下表汇总修复报告中的全部动作,随后逐项展开。

Finding ID动作严重级别一句话摘要
F-001deferredmajor264 行跨 26 张表,未遵循"单表"契约;合并属整页级重写
F-002deferredmajorGrammar 列大量(none)未区分解析产生与纯合成节点
F-003fixedmajor15 个 Key fields 单元格改为name: Type形式
F-004fixedmajor布局修饰符补充"绑定数据如何表示"的说明
F-005fixedminor纠正TargetIntrinsicModifier的 target/predicate 混淆
F-006fixedminorworkgraph.slang不再被称为核心模块源
F-007fixedminor删除生成历史叙述,保留三个类缺失的源码事实
F-008rejected-out-of-scopeminorwatched_paths_digest归操作者的regenerate.py mark-fresh管理

6.1 F-001:单表合并(推迟)

AST 家族文档契约(prompts/_common.md 第 99 行附近)要求"一张表",而modifiers.md把 264 行拆在 26 张表中;同族的declarations.mdexpressions.mdstatements.mdtypes.md都只用一张表,本页是例外。推迟的理据很实在:合并涉及## Nodes整体重写、搬迁各分组间的过渡性说明文字、并丢失 26 个###锚点目标,绝非最小编辑;且它触及的行与 F-002 完全重叠。后续计划是让modifiers.mdvalues.md在同一周期内一起重新生成## Nodes

6.2 F-002:全页 Grammar 审计(推迟)

prompt 契约(ast-reference-modifiers.md 第 21-25 行)规定(none)只保留给纯合成修饰符,但被解析产生的属性如ForceUnrollAttribute也被标为(none)。核查这些行的成本极高:264 行中有 217 行携带(none),每一行的判定都要交叉核对 126 个attribute_syntax声明(分布在core.meta.slangdiff.meta.slanghlsl.meta.slangworkgraph.slang四个文件)外加 parser 的关键字修饰符映射表。因此 F-002 与 F-001 一样被推迟,计划并入 F-001 的表重写一并完成。

6.3 F-003:Key fields 统一为name: Type(已修复)

审查发现 15 行使用了无类型的概念性描述(如optional unroll countopcode (in args)bindingmax (in args)),违反契约强制要求的name: Type形式。修复确认:AttributeBase::args在 slang-ast-modifier.h 第 804-815 行声明为List<Expr*>,且对这 15 个类做字段提取后确认没有一个类声明自有成员——继承列表是唯一真实存储。因此 15 个 Key fields 单元格(UnrollAttributeSPIRVInstructionOpAttribute、八个UncheckedGLSL*布局行、五个细分曲面(tessellation)行)统一改为args: List<Expr*>(inherited),并同步重写了开头段落中"概念性参数约定"的句子。

6.4 F-004:布局修饰符补充绑定表示(已修复)

审查要求说明布局修饰符在参数绑定中的角色。修复在### GLSLLayout*Modifier family末尾新增一段:已检查的GLSLBindingAttribute持有binding: int32_t/set: int32_t对,HLSL 侧用 Token 存registerName/componentMask/spaceName(声明见 slang-ast-modifier.h 第 481-495 行与第 1080-1087 行附近),并把语义规则推迟到链接的语义检查页面,符合该 prompt 的 forbidden-content 条款。

6.5 F-005:TargetIntrinsicModifier 混淆纠正(已修复)

如 4.3 节所述,修复把"由 targetToken 选择目标能力"与"predicate 作为经 scrutineeDeclRef 解析的独立守卫"分开表述,删除了把目标选择归因于谓词的说法。

6.6 F-006:workgraph.slang 归类更正(已修复)

"四个核心模块源"改为"四个源——三个核心模块.meta.slang文件加一个实验性标准模块",并点名工作图子句指向实验性标准模块 workgraph.slang。

6.7 F-007:删除生成历史叙述(已修复)

"页面被要求覆盖什么""没有 manifest 路径能恢复这些名字""未来重新生成"这类生成过程评论没有源码依据,被删除(契约 prompts/_common.md 第 74-81 行禁止投机性与编辑性材料);小节更名为### Absent groupings: no HLSLAttribute or LayoutModifier base,仅保留"三个名字在source/下不存在"这一可验证事实。由于没有页面链接旧标题锚点,更名是安全的。

6.8 F-008:watched_paths_digest 拒绝处理(超范围)

watched_paths_digest是 front matter 中的校验摘要。契约(prompts/_remediate.md 第 97-100 行)把它保留给操作者的regenerate.py mark-fresh运行管理,禁止修复者编辑;审查报告自己也建议"review 期间不要编辑生成页面"。因此该 finding 被拒绝为 out-of-scope——由于本页在其他 finding 下被编辑过,mark-fresh会记录七文件摘要(当前 manifest 解析出七个 watched 文件,见 manifest.yaml)。

七、从这套流水线能学到什么

  • 文档契约先行_common.mdast-reference-modifiers.md_remediate.md三份 prompt 分别约束文档格式(单表、name: Type(none)语义)、页面专属要求(绑定角色、禁止内容)与修复者的权限边界。任何手动编辑生成页面的行为都会破坏 front matter 摘要与 digest 的一致性。
  • 修复也要讲成本:F-001/F-002 的推迟不是因为"不重要",而是因为涉及 264 行中的同一批行、且属"重新生成"级别的操作——最小编辑原则与整页重写的边界在此得到清晰示范。
  • 源码是唯一事实来源:每一次修复都落回头文件行号(如AttributeBase::args在 804-815 行、TargetIntrinsicModifier在 281-301 行、HLSLLayoutSemantic在 481-495 行)与 parser/checker 的具体函数,保证了结论可复核、可追溯。

八、延伸阅读

  • ast-reference/modifiers.md —— 被修复的参考文档本体(264 类编目 + 重点节点讲解)
  • ast-reference/modifiers.md 审查报告 —— 8 个 finding 的完整证据链
  • ast-reference/modifiers.md 修复报告 —— 本文的骨架文档
  • ast-reference/base.md ——Modifier基类
  • ast-reference/declarations.md —— 携带修饰符的声明与AttributeDecl
  • ast-reference/expressions.md ——ModifiedTypeExpr携带内联Modifiers列表
  • ast-reference/types.md ——ModifiedTypeVal
  • ast-reference/values.md ——ModifierVal
  • 语法参考 grammar.md —— 修饰符与属性的表面语法
  • 能力系统 targets.md —— 解释RequireCapabilityAttributeTargetIntrinsicModifier等的能力系统
  • 核心模块 core-module.md ——IntrinsicOpModifierBuiltinTypeModifierMagicTypeModifier如何绑定核心模块声明
  • 核心源码:slang-ast-modifier.h、slang-parser.cpp、core.meta.slang、diff.meta.slang、hlsl.meta.slang、workgraph.slang
  • 编译器
  • 图形学
  • 编程语言

【免费下载链接】slang

Making it easier to work with shaders

项目地址:https://gitcode.com/GitHub_Trending/sl/slang
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 23:56:53

BrewUI:为Homebrew打造图形界面,让包管理可视化、可操作

每天打开终端敲 brew 的日子&#xff0c;我过了快十年。真正让我下定决心给 Homebrew 配一个图形面板的&#xff0c;不是某一次升级事故&#xff0c;而是无数次“想升级又不敢升”的纠结。屏幕上brew outdated列出几十个软件包&#xff0c;我盯着版本号&#xff0c;回忆它们各自…

作者头像 李华
网站建设 2026/9/19 23:56:52

linux中怎么一次提交多条命令

在Linux上&#xff0c;如果你想要多条命令一起运行&#xff0c;有几种方式可以实现&#xff0c;但具体使用哪种方式取决于你希望这两条命令如何并行或顺序执行。 1、顺序执行&#xff1a;如果你希望第一条命令执行完毕后&#xff0c;再执行第二条命令&#xff0c;你可以简单地将…

作者头像 李华
网站建设 2026/9/19 23:56:39

ImageGlass 2026路线图前瞻:下一版本的规划、方向与社区支持

ImageGlass 2026路线图前瞻&#xff1a;下一版本的规划、方向与社区支持 【免费下载链接】ImageGlass &#x1f3de; A fast, open-source, modern image viewer for 90 formats – including WEBP, GIF, SVG, AVIF, JXL, HEIC and more – built for smooth browsing across W…

作者头像 李华
网站建设 2026/9/19 23:54:31

CodeBuddy 插件实测:VSCode AI 补全与云端协同开发配置指南

1. 为什么我最终把 CodeBuddy 留在了 VSCode 里先说结论&#xff1a;我日常主力编辑器就是 VSCode&#xff0c;前后装过、卸过的 AI 编程插件没有二十也有十五个。CodeBuddy 是少数几个我用了两周之后没有卸载、反而把它固定到侧边栏的插件。原因不复杂——它把「AI 代码补全」…

作者头像 李华