✨ feat(vscode): 代码阅读预览顶部加块筛选栏

语言插件按和语言无关的 id 报块属性(通用的由宿主从 facts 推),翻译插件起名,webview 只按 id 筛;
筛选整个工作区共用、记在 workspaceState;JavaScript 不再显示「抽象:否」「仅类型:否」
This commit is contained in:
cruldra committed 2026-09-25 21:31:19 +08:00
1 parent 4eaf2ae11d
commit 1c7e31c51a
29 files changed
+1364 -319

No files matched your search

+38 -11
View File
@@ -1,4 +1,4 @@
import type { Access, ClassFacts, Defines, EntryFacts, FactBlock, FunctionFacts, ImportFacts, ImportItem, Markdown, Node, PreviewText, PropertyItem, PropertyRow, Segment, SourceSpan, TextBlock, TokenRole, Translator, VariableBlock } from '../contract'
import type { Access, AttributeNames, BlockKind, ClassFacts, CommonAttribute, Defines, EntryFacts, FactBlock, FunctionFacts, ImportFacts, ImportItem, Markdown, Node, PreviewText, PropertyItem, PropertyRow, Segment, SourceSpan, TextBlock, TokenRole, Translator, VariableBlock } from '../contract'
import { commonIndent, splitLines } from '../editText'
/** 按所在语法节点区分译法的关键字,例如 Python 的 else:if 后「否则」,for / while 后「未中断时」。 */
@@ -122,6 +122,34 @@ function slotChild(node: Node, slot: Exclude<TemplatePart, string>): Node | null
return node.namedChildren.filter(child => child && !child.isExtra)[slot.namedChild]
}
const ATTRIBUTE_ZH: Record<CommonAttribute, string> = { kind: '种类', access: '访问级别', async: '异步', abstract: '抽象', typeOnly: '仅类型' }
const KIND_ZH: Record<BlockKind, string> = {
import: '导入',
function: '函数',
class: '类',
type: '类型',
field: '字段',
const: '常量',
variable: '变量',
if: '条件',
else: '否则',
loop: '循环',
try: '尝试',
catch: '捕获',
finally: '最终',
return: '返回',
statement: '语句',
comment: '注释',
}
const ACCESS_ZH: Record<Access, string> = { public: '公共', private: '私有', protected: '受保护' }
const YES_NO: Record<'true' | 'false', string> = { true: '是', false: '否' }
/** 筛选属性和值的中文叫法;属性表里的访问级别、异步、抽象、仅类型这几行,属性名和值也用这一套。 */
const ZH_ATTRIBUTE_NAMES: AttributeNames = {
attributes: ATTRIBUTE_ZH,
values: { kind: KIND_ZH, access: ACCESS_ZH, async: YES_NO, abstract: YES_NO, typeOnly: YES_NO },
}
export function createZhTemplateTranslator(table: ZhTable): Translator {
const operatorParents = new Set(table.operatorParents)
const stringNodes = new Set(table.stringNodes)
@@ -318,7 +346,7 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
}
}
return { id: table.id, label: table.label, languageIds: table.languageIds, translate, describe }
return { id: table.id, label: table.label, languageIds: table.languageIds, translate, describe, attributeNames: ZH_ATTRIBUTE_NAMES }
}
function segment(text: string, role: TokenRole, offset: number | undefined): Segment {
@@ -375,14 +403,13 @@ interface Phrases {
doc: (node: Node) => Markdown | undefined
}
const ACCESS_ZH: Record<Access, string> = { public: '公共', private: '私有', protected: '受保护' }
function hint(text: string): Segment {
return { text, role: 'hint' }
}
function yesNo(value: boolean): Segment[] {
return [{ text: value ? '是' : '否', role: 'storage' }]
/** 布尔属性的一行:没有这个概念的语言(facts 里没填)没有这一行。 */
function flagRows(attribute: 'async' | 'abstract' | 'typeOnly', value: boolean | undefined): PropertyRow[] {
return value === undefined ? [] : [{ label: ATTRIBUTE_ZH[attribute], value: [{ text: YES_NO[`${value}`], role: 'storage' }] }]
}
function count(n: number): Segment[] {
@@ -394,7 +421,7 @@ function joined(parts: Segment[][], separator: string): Segment[] {
}
function accessRows(access: Access | undefined): PropertyRow[] {
return access ? [{ label: '访问级别', value: [{ text: ACCESS_ZH[access], role: 'keyword' }] }] : []
return access ? [{ label: ATTRIBUTE_ZH.access, value: [{ text: ACCESS_ZH[access], role: 'keyword' }] }] : []
}
/** 这一行 / 子行讲的定义:几个节点(导入项是名称和别名)合起来在源码里的范围;一个节点都没有就不填。 */
@@ -422,13 +449,13 @@ function aliased(item: Segment[], alias: Node | undefined, say: Phrases): Segmen
return [...item, { text: ' ', role: 'plain' }, { text: '作为', role: 'keyword' }, { text: ' ', role: 'plain' }, ...say.translated(alias)]
}
/** 函数:名称 / 说明 / 访问级别 / 异步 / 装饰器 / 输入参数(每个参数一行:名称、类型、默认值)/ 输出参数。 */
/** 函数:名称 / 说明 / 访问级别 / 异步(只有分这个的语言才有)/ 装饰器 / 输入参数(每个参数一行:名称、类型、默认值)/ 输出参数。 */
function functionRows(facts: FunctionFacts, say: Phrases): PropertyRow[] {
return [
{ label: '名称', value: facts.name ? say.name(facts.name, 'function') : [hint('匿名')] },
...docRows(facts.doc, say),
...accessRows(facts.access),
{ label: '异步', value: yesNo(facts.async) },
...flagRows('async', facts.async),
...decoratorRows(facts.decorators, say),
{
label: '输入参数',
@@ -457,7 +484,7 @@ function classRows(facts: ClassFacts, say: Phrases): PropertyRow[] {
{ label: '名称', value: facts.name ? say.name(facts.name, 'type') : [hint('匿名')] },
...docRows(facts.doc, say),
...accessRows(facts.access),
...(facts.abstract === undefined ? [] : [{ label: '抽象', value: yesNo(facts.abstract) }]),
...flagRows('abstract', facts.abstract),
...decoratorRows(facts.decorators, say),
...listRow('类型参数', facts.typeParameters.map(say.translated), defining(...facts.typeParameters)),
...listRow('父类', facts.bases.map(base => base.typeArguments ? [...say.translated(base.value), ...say.translated(base.typeArguments)] : say.translated(base.value))),
@@ -493,7 +520,7 @@ function importRows(facts: ImportFacts, say: Phrases): PropertyRow[] {
}
return [
{ label: '来源', value: facts.sources.length > 0 ? joined(facts.sources.map(say.module), ', ') : [hint('无')] },
...(facts.typeOnly === undefined ? [] : [{ label: '仅类型', value: yesNo(facts.typeOnly) }]),
...flagRows('typeOnly', facts.typeOnly),
{ label: '导入项', value: count(facts.items.length), items: facts.items.map(item => ({ cells: [itemText(item)], ...defining(...importNodes(item)) })) },
]
}