590 lines
31 KiB
TypeScript
590 lines
31 KiB
TypeScript
import type { Node } from '@vscode/tree-sitter-wasm'
|
||
import type { Disposable } from 'vscode'
|
||
|
||
export type { Node }
|
||
|
||
export type BlockKind
|
||
= | 'import' | 'function' | 'class' | 'enum' | 'exception' | 'type' | 'field' | 'const' | 'variable'
|
||
| 'if' | 'else' | 'loop' | 'try' | 'catch' | 'finally' | 'return' | 'statement' | 'comment'
|
||
|
||
/** 源码里一段的下标:源码字符串下标(web-tree-sitter 的 startIndex / endIndex),end 为开区间。 */
|
||
export interface SourceRange { startIndex: number, endIndex: number }
|
||
|
||
/** 源码里的一段,带起止行号(从 0 开始)。 */
|
||
export interface SourceSpan extends SourceRange { startRow: number, endRow: number }
|
||
|
||
/** 访问级别。 */
|
||
export type Access = 'public' | 'private' | 'protected'
|
||
|
||
/** 函数的一个参数;节点都指向源码。 */
|
||
export interface ParameterFacts {
|
||
/** 参数名;解构参数是整个模式,`...rest`、`*args`、`**kwargs` 带着前缀。 */
|
||
name: Node
|
||
/** TypeScript 的可省略参数 `x?`。 */
|
||
optional: boolean
|
||
type?: Node
|
||
default?: Node
|
||
}
|
||
|
||
export interface FunctionFacts {
|
||
/** 匿名函数(`export default function () {}`)没有。 */
|
||
name?: Node
|
||
/**
|
||
* 函数自己的说明:TypeScript 是紧贴在声明前面的 JSDoc 注释(以 `/**` 开头),Python 是主体开头的 docstring(字符串节点)。
|
||
* 它算在块的头部里,不再单独成一块注释。
|
||
*/
|
||
doc?: Node
|
||
/** 嵌套在函数体、条件等语句块里的函数没有。 */
|
||
access?: Access
|
||
/** 异步函数;没有这个概念的语言不填。 */
|
||
async?: boolean
|
||
decorators: Node[]
|
||
parameters: ParameterFacts[]
|
||
returnType?: Node
|
||
}
|
||
|
||
/**
|
||
* 父类的一项:TypeScript `extends Base<T>` 里的 Base(一个表达式)和跟在它后面的泛型实参 `<T>`(语法里是两个节点);
|
||
* Python 类参数列表里的一项,`metaclass=ABCMeta` 这类关键字参数、`*bases` 也算。
|
||
*/
|
||
export interface BaseFacts {
|
||
value: Node
|
||
typeArguments?: Node
|
||
}
|
||
|
||
export interface ClassFacts {
|
||
/** 匿名类(`export default class {}`)没有;赋给变量、类字段的类表达式取变量名、字段名。 */
|
||
name?: Node
|
||
/** 类自己的说明,同 FunctionFacts.doc:TypeScript 是紧贴在前面的 JSDoc,Python 是主体开头的 docstring。 */
|
||
doc?: Node
|
||
/** 同函数:嵌套在函数体、条件等语句块里的类没有。 */
|
||
access?: Access
|
||
/** 抽象类(TypeScript 的 `abstract class`);没有这个概念的语言不填。 */
|
||
abstract?: boolean
|
||
decorators: Node[]
|
||
/** 泛型参数,每个参数一项(TypeScript `<T extends X = Y>` 里的 `T extends X = Y`)。 */
|
||
typeParameters: Node[]
|
||
/** 按源码顺序。 */
|
||
bases: BaseFacts[]
|
||
/** 实现的接口(TypeScript 的 `implements A, B<T>`),每个接口一项。 */
|
||
implements: Node[]
|
||
}
|
||
|
||
/** 枚举的一个成员;节点都指向源码。 */
|
||
export interface EnumMemberFacts {
|
||
name: Node
|
||
/** 源码里写的值;没写(TypeScript 的自增成员)、或值交给库自动取(Python 的 `auto()`)时没有。 */
|
||
value?: Node
|
||
/** 没写值、语言算得出来时的值(TypeScript 数值枚举自增:0、1、2……);算不出来时没有。 */
|
||
computed?: number
|
||
/**
|
||
* 成员的说明文字节点(注释、JSDoc、属性 docstring),照源码顺序:紧挨在它上面(从上一个成员之后起)单独成行的、
|
||
* 写在它这一行行尾的、Python 紧跟在它后面的属性 docstring。翻译插件逐个取原文连起来。
|
||
*/
|
||
docs: Node[]
|
||
}
|
||
|
||
/**
|
||
* 枚举:TypeScript 的 `enum` 由语言插件报;Python 没有枚举语法,自带 enum 库的写法(`class X(enum.Enum)`)由库规则认成这种块。
|
||
* 两边同一种块、同一张属性表。
|
||
*/
|
||
export interface EnumFacts {
|
||
name: Node
|
||
/** 枚举自己的说明,同 ClassFacts.doc。 */
|
||
doc?: Node
|
||
/** 同 ClassFacts.access。 */
|
||
access?: Access
|
||
decorators: Node[]
|
||
/** 照源码写的父类(Python 的 `str, enum.Enum`);TypeScript 没有。 */
|
||
bases: BaseFacts[]
|
||
/** 照源码顺序。 */
|
||
members: EnumMemberFacts[]
|
||
}
|
||
|
||
/** 异常类的一个类属性:Python 类主体里给一个名字赋值的语句(带不带注解、有没有值),TypeScript 的类字段(含 static);节点都指向源码。 */
|
||
export interface FieldFacts {
|
||
name: Node
|
||
type?: Node
|
||
value?: Node
|
||
/** 属性的说明文字节点,同 EnumMemberFacts.docs:紧挨在上面单独成行的注释 / JSDoc、这一行行尾的注释、Python 紧跟在后面的属性 docstring。 */
|
||
docs: Node[]
|
||
}
|
||
|
||
/**
|
||
* 异常类:父类是语言自带的异常类、同一文件里认出来的异常类,或名字按命名习惯以 Error / Exception(Python 还有 Warning)结尾。
|
||
* Python、TypeScript 都由语言插件报,同一种块、同一张属性表。块的头部是整个类(类属性在属性表里讲),留下的方法等是子块。
|
||
*/
|
||
export interface ExceptionFacts {
|
||
/** 同 ClassFacts.name。 */
|
||
name?: Node
|
||
/** 同 ClassFacts.doc。 */
|
||
doc?: Node
|
||
/** 同 ClassFacts.access。 */
|
||
access?: Access
|
||
decorators: Node[]
|
||
/** 照源码写的父类,同 ClassFacts.bases。 */
|
||
bases: BaseFacts[]
|
||
/** 类属性,照源码顺序。 */
|
||
fields: FieldFacts[]
|
||
/** 构造函数的参数(Python 的 `__init__` 去掉 self,TypeScript 的 `constructor(...)`);类里没写构造函数时没有。 */
|
||
parameters?: ParameterFacts[]
|
||
}
|
||
|
||
/** 导入的一项。 */
|
||
export type ImportItem
|
||
= | { kind: 'named', name: Node, alias?: Node } // import { a as b }、from m import a as b
|
||
| { kind: 'default', alias: Node } // import x from 'm'
|
||
| { kind: 'namespace', alias?: Node } // import * as x、from m import *
|
||
| { kind: 'module', module: Node, alias?: Node } // import a.b as c、import x = require('m')
|
||
|
||
export interface ImportFacts {
|
||
/** 来源模块,节点就是源码里的写法:TypeScript 是字符串字面量,Python 是点分模块名;Python 的 `import a, b` 有多个。 */
|
||
sources: Node[]
|
||
/** 只导入类型(TypeScript 的 `import type`);没有这个概念的语言不填。 */
|
||
typeOnly?: boolean
|
||
/** 副作用导入(`import './x'`)没有。 */
|
||
items: ImportItem[]
|
||
}
|
||
|
||
/** 字典 / 对象字面量里的一项,照源码顺序;注释不算。 */
|
||
export type EntryFacts
|
||
= | { kind: 'pair', key: Node, value: Node, entries?: EntryFacts[] } // 'a': 1、a: 1、[k]: v;TypeScript 的简写 { a } 键和值是同一个节点;entries 同 DeclaratorFacts.entries
|
||
| { kind: 'spread', value: Node } // **other、...other:value 是展开的那个表达式
|
||
| { kind: 'method', name: Node, node: Node, header: SourceSpan } // 对象字面量里的方法、getter、setter:node 是整个方法,header 同 BlockShape.header,只到主体开始之前
|
||
|
||
/** 写在表达式里的遍历得到什么:逐个产出的生成器,或一次建好的列表、集合、字典。 */
|
||
export type IterationResult = 'generator' | 'list' | 'set' | 'dict'
|
||
|
||
/** 遍历的一节,照源码顺序:从来源里逐个取出变量(async:异步地取),或只留下满足条件的。 */
|
||
export type IterationClause
|
||
= | { kind: 'for', variables: Node, source: Node, async: boolean }
|
||
| { kind: 'if', condition: Node }
|
||
|
||
/**
|
||
* 写在表达式里的遍历(Python 的列表 / 集合 / 字典推导式、生成器表达式):语言插件从语法树读出结构(LanguageAdapter.iteration),
|
||
* 翻译插件按自己的语序重排。节点都指向源码;各节、每一项取的值里还能再套遍历。
|
||
*/
|
||
export interface IterationFacts {
|
||
result: IterationResult
|
||
/** 至少一节,第一节是 for。 */
|
||
clauses: IterationClause[]
|
||
/** 每一项取的值。 */
|
||
value: Node
|
||
/** 结果是字典时每一项的键。 */
|
||
key?: Node
|
||
/**
|
||
* 遍历自己的一对括号同时是外面调用的括号时(Python `f(x for x in y)` 只写一对括号)是这对括号:
|
||
* 它们属于外面的调用,照常写在遍历外面;别的写法里遍历自己的括号、方括号、花括号不写出来。
|
||
*/
|
||
sharedParentheses?: { open: Node, close: Node }
|
||
}
|
||
|
||
export interface DeclaratorFacts {
|
||
/** 变量名;解构声明是整个模式。 */
|
||
name: Node
|
||
type?: Node
|
||
value?: Node
|
||
/** 值是字典 / 对象字面量(Python 的 dictionary、TypeScript 的 object)时是它的各项,空字面量是空数组;别的值没有。 */
|
||
entries?: EntryFacts[]
|
||
}
|
||
|
||
export interface VariableFacts {
|
||
/** 只有模块顶层(Python 还有类主体)的声明有。 */
|
||
access?: Access
|
||
/**
|
||
* 变量自己的说明,同 FunctionFacts.doc:Python 是模块、类主体里紧跟在这条赋值后面的字符串(属性 docstring,字符串节点;
|
||
* 中间可以隔空行和赋值行尾的注释)。它算在块的头部里,不再单独成一块。
|
||
*/
|
||
doc?: Node
|
||
/** 一条语句声明几个变量(`let a = 1, b = 2`)就有几项。 */
|
||
declarators: DeclaratorFacts[]
|
||
}
|
||
|
||
/**
|
||
* 筛选属性的通用词表,筛选栏按这个顺序排,语言自己的属性排在后面:
|
||
* - kind:块的种类(BlockKind);
|
||
* - access:访问级别,'public' | 'private' | 'protected',语言可以加自己的值(Kotlin 的 'internal',见 BlockShape.attributes);
|
||
* - async、abstract、typeOnly:异步、抽象、仅类型,值是 'true' | 'false'。
|
||
* 宿主从块的 kind 和 facts 推出它们(FunctionFacts.access / async、ClassFacts.access / abstract、EnumFacts.access、ExceptionFacts.access、ImportFacts.typeOnly、VariableFacts.access),
|
||
* 语言插件不用另报;facts 里没填的(语言没有这个概念、块没有这一项),块就没有这个属性,也不受这个属性的筛选影响。
|
||
*/
|
||
export const COMMON_ATTRIBUTES = ['kind', 'access', 'async', 'abstract', 'typeOnly'] as const
|
||
|
||
export type CommonAttribute = (typeof COMMON_ATTRIBUTES)[number]
|
||
|
||
/** 块的筛选属性:属性 id → 值 id,都是稳定的英文 id,不放显示文字(显示名由翻译插件给,见 AttributeNames)。 */
|
||
export type BlockAttributes = Record<string, string>
|
||
|
||
export interface BlockShape {
|
||
/** 这一块对应的语法树节点(整条语句)。 */
|
||
node: Node
|
||
/**
|
||
* 块自己显示 / 编辑的那一段:复合语句只到主体开始之前,其余是整条语句。
|
||
* 块自己的说明(facts.doc、TextBlock.doc)也算在里面:JSDoc 往上延到注释开头,docstring、属性 docstring 往下延到字符串结尾。
|
||
*/
|
||
header: SourceSpan
|
||
children: CodeBlock[]
|
||
/**
|
||
* 语言自己报的筛选属性,和宿主推出的通用属性(见 COMMON_ATTRIBUTES)合在一起:通用词表以外的属性(Rust 的 unsafe: 'true'),
|
||
* 或 facts 表达不了的通用属性值(Kotlin 的 access: 'internal',盖过从 facts 推出的)。kind 总是块的种类,这里填了不算。
|
||
*/
|
||
attributes?: BlockAttributes
|
||
}
|
||
|
||
/** 预览成属性表的块:语言插件从语法树读出结构化信息(facts),翻译插件把它排成属性表。 */
|
||
export interface FunctionBlock extends BlockShape { kind: 'function', facts: FunctionFacts }
|
||
export interface ClassBlock extends BlockShape { kind: 'class', facts: ClassFacts }
|
||
/** 枚举块的头部是整个枚举(成员都在属性表里讲),没有子块;库规则认出来的枚举(Python)是整个类,留下的方法等是子块。 */
|
||
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 }
|
||
export type FactBlock = FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | ImportBlock | VariableBlock
|
||
|
||
/** 带说明(facts.doc)的块。 */
|
||
export type DocumentedBlock = FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | VariableBlock
|
||
|
||
/** 属性表块的种类和 facts,不带块的形状(节点、头部、子块):库规则把一块认成现成的一种属性表块时给它(RuleFacts)。 */
|
||
export type BlockFacts = FactBlock extends infer B ? B extends FactBlock ? Pick<B, 'kind' | 'facts'> : never : never
|
||
|
||
export type TextKind = Exclude<BlockKind, FactBlock['kind']>
|
||
|
||
/** 预览成一行译文的块。 */
|
||
export interface TextBlock extends BlockShape {
|
||
kind: TextKind
|
||
/**
|
||
* 块自己的说明,同 VariableFacts.doc:Python 字段(类主体里带注解的赋值)后面紧跟的属性 docstring(字符串节点)。
|
||
* 算在块的头部里;预览在一行译文下面按 Markdown 显示(Translator.doc)。
|
||
*/
|
||
doc?: Node
|
||
}
|
||
|
||
export type CodeBlock = TextBlock | FactBlock
|
||
|
||
/** 着色用的词性,源码着色和译文着色共用。 */
|
||
export type TokenRole
|
||
= | 'keyword' // 流程关键字:import from export if else for return try catch await …
|
||
| 'storage' // 声明关键字与字面量关键字:const function async class interface new true false null …
|
||
| 'operator' // 被翻成文字的运算符:全等 并且 或者 非
|
||
| 'hint' // 属性表里的说明词:未标注 无 N 个 默认导出 …
|
||
| 'variable' | 'function' | 'type' | 'string' | 'number' | 'literal' | 'comment' | 'plain'
|
||
|
||
/**
|
||
* 一段着色的文字。offset:这段照抄自源码里的名字(标识符、属性名、类型名、点分模块名的一节)或字符串时,
|
||
* 它在源码里的起始下标(同 SourceSpan 的下标),预览拿它向 VS Code 查定义、跳过去;译词、提示词没有。
|
||
* reference:只有库规则给的段有,这段讲的东西由规则自己查定义(LibraryRule.resolve),不问语言扩展;这样的段必须带 offset。
|
||
* color:这段自己的颜色(暗色、亮色主题各一个),盖过词性的颜色:库规则按库自己的配色上色(FastAPI 的 HTTP 方法用 Swagger UI 的颜色)。
|
||
* refers:这段讲的是源码里别处的一个名字(那个名字的范围),语言扩展不认得这段自己的位置:写在字符串里的名字
|
||
* (FastAPI 路径 `"/{user_id}"` 里的 user_id 讲的是函数的参数 user_id)。预览把这段当那个名字:⌘ / Ctrl + 点击跳到它的定义,
|
||
* 单击高亮它的各处出现,那个名字的某处出现盖住 refers 时这段也跟着高亮。offset 照旧是这段照抄的位置。
|
||
*/
|
||
export interface Segment { text: string, role: TokenRole, offset?: number, reference?: RuleReference, color?: ThemeColor, refers?: SourceRange }
|
||
|
||
/** 框的样式,决定框头的图标和框的颜色:'iteration' 是写在表达式里的遍历,图标、颜色同「循环」块。 */
|
||
export type GroupStyle = 'iteration'
|
||
|
||
/**
|
||
* 框起来的一组词:译文里自成一体的一段(写在表达式里的遍历),预览把它画成嵌在前后文字里的一个小框,框头是图标和 label。
|
||
* 框里的词照样带源码下标,还能再套框。
|
||
*/
|
||
export interface SegmentGroup {
|
||
style: GroupStyle
|
||
/** 框头的文字(遍历的结果:生成器 / 列表 …),翻译插件给。 */
|
||
label: string
|
||
segments: Inline[]
|
||
}
|
||
|
||
/** 译文里的一段:着色的词,或框起来的一组词。 */
|
||
export type Inline = Segment | SegmentGroup
|
||
|
||
/**
|
||
* 一段 Markdown 说明文字:注释、JSDoc、docstring 去掉注释符号、引号和共同缩进后的原文,不做别的转换
|
||
* (reStructuredText 的 `:func:`、JSDoc 的 `@param` 都照抄)。预览按 Markdown 渲染成注释色的段落、列表、代码块,源码里的 HTML 当文字显示。
|
||
*/
|
||
export interface Markdown { markdown: string }
|
||
|
||
/** 预览里显示的一段文字:着色的词(一行译文、属性值),或整段 Markdown 说明(注释块、函数和类的说明)。 */
|
||
export type PreviewText = Inline[] | Markdown
|
||
|
||
/**
|
||
* 属性表里的一行或一个子行讲的是哪个定义:定义出来的那个名字的节点(取自 facts,例如参数名、导入项的名称和别名、
|
||
* 声明的变量名、类的泛型参数、字典 / 对象字面量一项的键)在源码里的范围。跳到定义时,定义处落在这个范围里就只闪这一行;不填(第三方翻译插件可以不填)就闪整块。
|
||
*/
|
||
export interface Defines { defines?: SourceRange }
|
||
|
||
/** 属性表里展开的一个子行:几列对齐的格子,例如参数的名称 / 类型 / 默认值、字典一项的键 / 值。 */
|
||
export interface PropertyItem extends Defines {
|
||
cells: Inline[][]
|
||
/** 展开在这个子行下面的下一层子行(字典 / 对象的值又是字典 / 对象时,它的各项);一层层可以一直往下套。 */
|
||
items?: PropertyItem[]
|
||
}
|
||
|
||
/**
|
||
* 属性表的一行:左列属性名,右列着色的值或 Markdown 说明;items 是展开在这一行下面的子行(输入参数、导入项、字典 / 对象的各项)。
|
||
* columns 是子行各列的列名(输入参数的名称 / 类型 / 默认值):给了列名的子行排成带表头和边框的表格,没给的排成列表;表格只有一层,子行不再往下套。
|
||
*/
|
||
export interface PropertyRow extends Defines {
|
||
label: string
|
||
value: PreviewText
|
||
items?: PropertyItem[]
|
||
columns?: string[]
|
||
}
|
||
|
||
/**
|
||
* 语言插件:语法树 → 代码块树,外加只有它懂、翻译插件翻表达式时要用的信息(叶子的词性、写在表达式里的遍历的结构)。
|
||
* 翻译插件翻一块时从 SourceDocument.language 拿到解析这份源码的语言插件,不直接依赖某个语言插件。
|
||
*/
|
||
export interface LanguageAdapter {
|
||
id: string
|
||
/** 覆盖的 VS Code languageId,例如 ['typescript', 'typescriptreact', 'javascript', 'javascriptreact']。 */
|
||
languageIds: string[]
|
||
/** 该 languageId 用的语法包 .wasm 的绝对路径(宿主负责加载,保证和解析器运行时是同一份)。 */
|
||
grammarPath: (languageId: string) => string
|
||
/** languageId 是这份源码的 languageId:一个插件覆盖几种方言时按它区分(JavaScript 没有 TypeScript 的抽象类、仅类型导入)。 */
|
||
toBlocks: (root: Node, source: string, languageId: string) => CodeBlock[]
|
||
/** 给语法树叶子节点定词性:源码着色用它,译文里照抄的词也按它着色、按它认出能查定义的名字。 */
|
||
tokenRole: (leaf: Node) => TokenRole
|
||
/** node 是写在表达式里的遍历(推导式、生成器表达式)时给出它的结构,别的节点没有;没有这种写法的语言不填。 */
|
||
iteration?: (node: Node) => IterationFacts | undefined
|
||
/** 认代码里给定义起的中文名(见「中文名」);不填就没有中文名。 */
|
||
displayNames?: DisplayNameReader
|
||
}
|
||
|
||
// ── 中文名(见「中文名」)──
|
||
|
||
/** 代码里给一个定义起的中文名:预览里这个定义和用到它的各处都显示中文名,悬停看原名。 */
|
||
export interface DisplayName {
|
||
/** 定义处那个名字(函数名、类名、参数名、变量名、字段名、枚举成员名……) */
|
||
name: SourceRange
|
||
/** 中文名 */
|
||
text: string
|
||
}
|
||
|
||
/** 一份源码里认出的中文名。 */
|
||
export interface DisplayNameFacts {
|
||
/** 起了中文名的定义,照源码顺序;一个定义几处都写了时语言插件定取哪个(明写的记号优先于说明文字) */
|
||
names: DisplayName[]
|
||
/** 写在注释里给代码阅读看的记号(`# @Comment 当前用户` 里的 `@Comment 当前用户`):宿主交给翻译插件,翻注释、说明时去掉 */
|
||
markers: SourceRange[]
|
||
}
|
||
|
||
/** 语言插件认中文名:写法(记号、说明文字里的哪一句)归语言插件;找用到它的各处、显示归宿主和预览。 */
|
||
export interface DisplayNameReader {
|
||
/** languageId → 工作区里这种语言的文件(glob,Python 是所有 .py 文件):宿主建工作区索引,认出别的文件里定义的中文名 */
|
||
files: Record<string, string>
|
||
/** 便宜的文字预筛:可能写了中文名的源码才解析 */
|
||
mentions: (source: string) => boolean
|
||
find: (root: Node, source: string, languageId: string) => DisplayNameFacts
|
||
}
|
||
|
||
/** 翻译插件翻一块时拿到的这份源码:源码字符串,和解析它的语言插件。 */
|
||
export interface SourceDocument {
|
||
source: string
|
||
language: LanguageAdapter
|
||
/** 写在注释里给代码阅读看的记号(中文名记号,见 DisplayNameFacts.markers):不是说明文字,翻注释、说明时去掉 */
|
||
markers: SourceRange[]
|
||
}
|
||
|
||
/** 筛选属性和值的显示名,筛选栏、块的种类(图标的悬停提示)用它;没给名字的显示 id。 */
|
||
export interface AttributeNames {
|
||
/** 属性 id → 属性名,例如 access → 访问级别。 */
|
||
attributes: Record<string, string>
|
||
/** 属性 id → 值 id → 值名,例如 access → public → 公共、async → true → 是、kind → function → 函数。 */
|
||
values: Record<string, Record<string, string>>
|
||
}
|
||
|
||
/** 翻译插件:代码块 → 一行译文或属性表。 */
|
||
export interface Translator {
|
||
id: string // 'typescript.zh-template'
|
||
label: string // '中文直译'
|
||
languageIds: string[]
|
||
/** 译文用的语言(BCP 47 的语言部分,例如 'zh'):库规则按它挑自己的词表。 */
|
||
displayLanguage: string
|
||
/** 只翻 header 那一段;注释块给去掉注释符号的 Markdown 原文。 */
|
||
translate: (block: TextBlock, document: SourceDocument) => PreviewText
|
||
/** 函数、类、枚举、导入、常量、变量块 → 属性表。 */
|
||
describe: (block: FactBlock, document: SourceDocument) => PropertyRow[]
|
||
/** 一个表达式或类型节点整个翻成一行译文,译法同属性表里的值:库规则翻默认值、类型时借它。 */
|
||
expression: (node: Node, document: SourceDocument) => Inline[]
|
||
/** 一段说明文字(注释、docstring、当说明用的字符串)去掉注释符号、引号和共同缩进后的 Markdown 原文,同属性表的「说明」;没有文字时没有。 */
|
||
doc: (node: Node, document: SourceDocument) => Markdown | undefined
|
||
/** 筛选属性和值叫什么,和属性表里的属性名、值用同一套叫法;不给就都显示 id。 */
|
||
attributeNames?: AttributeNames
|
||
}
|
||
|
||
/** 一种颜色在暗色、亮色主题下各用哪个(CSS 颜色值);高对比主题跟着暗 / 亮走。 */
|
||
export interface ThemeColor { dark: string, light: string }
|
||
|
||
/**
|
||
* 种类图标:插件自己定义的一组 lucide 图标(https://lucide.dev/icons 上的 kebab-case 名字)。前 18 个给内置种类,后面的给库规则的种类;
|
||
* 库规则的种类(KindBadge.icon)只能从这组里挑,要新图标就加进这组(webview 的 kindIcons.tsx 同时给它配上图标),没有按任意名字找图标的路。
|
||
*/
|
||
export const KIND_ICON_NAMES = [
|
||
'import',
|
||
'square-function',
|
||
'box',
|
||
'braces',
|
||
'list-ordered',
|
||
'triangle-alert',
|
||
'tag',
|
||
'lock',
|
||
'variable',
|
||
'split',
|
||
'corner-down-right',
|
||
'repeat',
|
||
'shield',
|
||
'shield-alert',
|
||
'shield-check',
|
||
'corner-down-left',
|
||
'code',
|
||
'message-square-text',
|
||
'database',
|
||
'package',
|
||
'server-cog',
|
||
'calendar-clock',
|
||
'globe',
|
||
] as const
|
||
|
||
export type KindIconName = (typeof KIND_ICON_NAMES)[number]
|
||
|
||
/** 库规则的种类在预览里的样子:块左边只画图标(种类色),名字是悬停提示和读屏念的文字,筛选栏里这个种类也叫它。 */
|
||
export interface KindBadge { label: string, color: ThemeColor, icon: KindIconName }
|
||
|
||
/**
|
||
* 库规则认领一块后给出的替身,预览里它代替原来那块,下面挂着原块留下的子块:
|
||
* 规则自己的卡片(RuleCard),或认成契约里现成的一种属性表块(RuleFacts)。
|
||
*/
|
||
export type RuleBlock = RuleCard | RuleFacts
|
||
|
||
/** 规则自己的卡片:一张属性表,种类的图标、颜色、名字和每一行都由规则给(用词归规则)。 */
|
||
export interface RuleCard {
|
||
/** 种类 id,写成「规则 id.名字」(sqlalchemy.table):筛选的 kind 属性值就是它,筛选记住的也是它,要稳定。 */
|
||
kind: string
|
||
/** 种类的名字、颜色和图标:筛选栏里这个种类也叫这个名字。 */
|
||
badge: KindBadge
|
||
rows: PropertyRow[]
|
||
/** 块自己的名字(类名)在源码里的范围:跳到的定义就是它时闪整块。 */
|
||
defines?: SourceRange
|
||
/** 规则自己的筛选属性,叠在宿主从原块推出的属性上(同 BlockShape.attributes);kind 总是上面的 kind,这里填了不算。 */
|
||
attributes?: BlockAttributes
|
||
/**
|
||
* 并进这一块的子块(原块 children 里的对象本身):不再单独成块,内容由 rows 讲。
|
||
* 没并进来的子块(方法、别的成员、规则不讲的注释)不管写在哪,都照源码顺序照常是这一块的子块,也照常交给各规则看。
|
||
* 这一块的编辑范围是原块整条语句(类就是整个类),盖住留下的子块:它编辑时预览不画这些子块,同一行不会同时在两处能改。
|
||
*/
|
||
absorbs: CodeBlock[]
|
||
}
|
||
|
||
/**
|
||
* 把这块认成契约里现成的一种属性表块(Python 自带 enum 库的类 → 枚举块):宿主当语言插件报的这种块处理,
|
||
* 种类、种类名、颜色、属性表、筛选属性都和语言自带的这种块一样,由翻译插件排;规则只给 facts(库的知识),不给用词。
|
||
* 块的头部是原块整条语句(facts 里的节点都在里面),absorbs 同 RuleCard.absorbs。
|
||
*/
|
||
export interface RuleFacts {
|
||
as: BlockFacts
|
||
absorbs: CodeBlock[]
|
||
}
|
||
|
||
/** 库规则看一块时宿主给的:这份源码、它的导入,借当前翻译插件翻表达式、取说明的办法,和查名字的定义在工作区索引里是哪一项的办法。 */
|
||
export interface RuleContext {
|
||
document: SourceDocument
|
||
/** 这份源码里各处导入的 facts,照源码顺序:规则拿它认出库里名字的别名(`from sqlalchemy import Enum as SQLEnum`)。 */
|
||
imports: ImportFacts[]
|
||
/** 当前翻译插件的 displayLanguage;没有翻译插件时没有。规则按它挑自己的词表,没有这种语言的词表就用自己默认的那份。 */
|
||
displayLanguage?: string
|
||
/** 用当前翻译插件把一个表达式或类型节点翻成一行译文(Translator.expression):名字带源码下标,能跳到定义、高亮各处出现;没有翻译插件时照抄源码。 */
|
||
translate: (node: Node) => Inline[]
|
||
/** 用当前翻译插件取一段说明文字(Translator.doc);没有翻译插件、没有文字时没有。 */
|
||
doc: (node: Node) => Markdown | undefined
|
||
/**
|
||
* 源码里 name 这个名字(例如 raise 后面的异常类名)指的定义在 index 里认出的那一项,规则从定义处读出的值在 IndexEntry.value 里:
|
||
* 定义由语言扩展查(同 ⌘ + 点击),落在 index 认出的某一项的名字上才算;同一份源码里同名的名字按一处查。
|
||
* 认领是同步的,查要等工作区索引建好、语言扩展答话:宿主先当没查到(undefined)交给规则排,发出预览后再查,
|
||
* 查到的和上次不一样就按新结果重排一遍、重发;文档改了先用上次查到的,再重查。没有工作区时(单元测试)总是 undefined。
|
||
*/
|
||
lookup: (index: WorkspaceIndex, name: Node) => IndexEntry | undefined
|
||
}
|
||
|
||
/** 规则自己查得到定义的一个名字:哪个规则(LibraryRule.id),和规则自己认的键(Celery 的任务名)。 */
|
||
export interface RuleReference { rule: string, key: string }
|
||
|
||
/** 文件里的一个位置:行、列从 0 起,列按 UTF-16 计(同 VS Code)。 */
|
||
export interface RulePosition { line: number, character: number }
|
||
|
||
/** 规则查到的一处定义:文件(uri 字符串)和定义处那个名字的起止位置。 */
|
||
export interface RuleLocation { uri: string, start: RulePosition, end: RulePosition }
|
||
|
||
/** 建索引时交给规则的一份文件。 */
|
||
export interface IndexedFile {
|
||
/** 相对它所在的工作区文件夹的路径,用 / 分隔。 */
|
||
path: string
|
||
source: string
|
||
/** 同一个工作区文件夹里有没有这个文件(相对路径);只认按 WorkspaceIndex.include 找到的文件。算 Python 包路径时问 `__init__.py` 用。 */
|
||
exists: (path: string) => boolean
|
||
}
|
||
|
||
/**
|
||
* 规则从一份文件里认出的一项:键,定义处那个名字在源码里的范围,和规则从定义处读出、认领别的文件时要用的值
|
||
* (FastAPI 规则:异常类的 status_code;见 RuleContext.lookup)。
|
||
*/
|
||
export interface IndexEntry { key: string, name: SourceRange, value?: string }
|
||
|
||
/**
|
||
* 规则在整个工作区里建的索引(键 → 定义处):规则只说看哪些文件、从一份源码里认出什么;
|
||
* 找文件、预筛、解析、缓存、跟着文件改动更新都归宿主。第一次查时才建,之后只重看改了的文件。
|
||
*/
|
||
export interface WorkspaceIndex {
|
||
/** 同一个 id 宿主只建一份,写成「规则 id.名字」(celery.tasks)。 */
|
||
id: string
|
||
/** 按哪种语言解析:宿主用这个 languageId 的语言插件。 */
|
||
languageId: string
|
||
/**
|
||
* 看哪些文件:相对各工作区文件夹的 glob('**\/*.py');files.exclude、search.exclude 排除的,.venv、node_modules、__pycache__ 里的,
|
||
* 嵌套的 checkout(子仓库、git worktree)里的,和忽略文件(.gitignore、.ignore,按 search.useIgnoreFiles)忽略的不看。
|
||
*/
|
||
include: string
|
||
/** 便宜的文字预筛:源码里不可能有要找的东西就不解析。 */
|
||
mentions: (source: string) => boolean
|
||
/** 从一份文件的语法树里认出的各项;语法树只在调用期间有效。 */
|
||
entries: (root: Node, file: IndexedFile) => IndexEntry[]
|
||
}
|
||
|
||
/** 宿主给规则查定义用的工作区。 */
|
||
export interface RuleWorkspace {
|
||
/** 在 index 建的索引里查 key 的各处定义,照文件、行排好。 */
|
||
lookup: (index: WorkspaceIndex, key: string) => Promise<RuleLocation[]>
|
||
}
|
||
|
||
/**
|
||
* 库规则:认出某个库的一种写法(SQLAlchemy 的模型类),把那块整个换成一张专门的卡片,或认成现成的一种属性表块
|
||
* (enum 库的类 → 枚举块);文件导入了它关心的库才启用。
|
||
* 规则只经契约拿信息:块(kind、node、header、children、facts)和 RuleContext;卡片的用词(属性名、种类名、类型的译名)属于规则自己。
|
||
*/
|
||
export interface LibraryRule {
|
||
id: string // 'sqlalchemy'
|
||
label: string // 'SQLAlchemy 数据表'
|
||
languageIds: string[]
|
||
/**
|
||
* 关心的库(模块名):这份源码里有一条导入(ImportFacts.sources,去掉引号)等于它、或以「它.」「它/」开头(它的子模块)才启用。
|
||
* 宿主按语言插件给的导入判断,规则不用自己读导入语句。
|
||
*/
|
||
libraries: string[]
|
||
/**
|
||
* 看一块:认领就返回替身,不认就返回 undefined。宿主从顶层往下每块都问(被认领的块留下的子块照样问)。
|
||
* 几个规则都认同一块时先注册的算;规则抛错、或替身不合上面的约定时,宿主记日志、当它没认,接着问下一个规则。
|
||
*/
|
||
claim: (block: CodeBlock, context: RuleContext) => RuleBlock | undefined
|
||
/**
|
||
* 查规则自己标的引用(Segment.reference 里 rule 是这个规则的)的定义:预览里 ⌘ / Ctrl 悬停、点击这样的名字时宿主问它,不问语言扩展;
|
||
* 查到几处就跳、挑,和语言扩展查到的一样。没给就不能在段上标引用。
|
||
*/
|
||
resolve?: (key: string, workspace: RuleWorkspace) => Promise<RuleLocation[]>
|
||
}
|
||
|
||
export interface CodeReaderApi {
|
||
registerLanguage: (adapter: LanguageAdapter) => Disposable
|
||
registerTranslator: (translator: Translator) => Disposable
|
||
registerLibraryRule: (rule: LibraryRule) => Disposable
|
||
}
|