✨ 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

+74
View File
@@ -0,0 +1,74 @@
import type { AttributeNames, BlockAttributes, CodeBlock, CommonAttribute } from './contract'
import { COMMON_ATTRIBUTES } from './contract'
/** 这份文件里出现过的一个属性值:显示名和有几块是这个值。 */
export interface AttributeValueCount {
id: string
name: string
count: number
}
/** 这份文件里出现过的一个属性:显示名,出现过的值按第一次出现的先后排。 */
export interface FileAttribute {
id: string
name: string
values: AttributeValueCount[]
}
function flag(value: boolean | undefined): string | undefined {
return value === undefined ? undefined : String(value)
}
/** 从 facts 推出的通用属性(kind 以外);facts 里没填的是 undefined。 */
function factAttributes(block: CodeBlock): Partial<Record<CommonAttribute, string>> {
switch (block.kind) {
case 'function':
return { access: block.facts.access, async: flag(block.facts.async) }
case 'class':
return { access: block.facts.access, abstract: flag(block.facts.abstract) }
case 'import':
return { typeOnly: flag(block.facts.typeOnly) }
case 'const':
case 'variable':
return { access: block.facts.access }
default:
return {}
}
}
/** 块的筛选属性:从 facts 推出的通用属性,叠上语言插件自己报的(同一属性以它为准),kind 总是块的种类。 */
export function blockAttributes(block: CodeBlock): BlockAttributes {
const derived = Object.entries(factAttributes(block)).filter((entry): entry is [string, string] => entry[1] !== undefined)
return { ...Object.fromEntries(derived), ...block.attributes, kind: block.kind }
}
interface Attributed {
attributes: BlockAttributes
children: Attributed[]
}
/**
* 整份文件(所有层的块)里出现过的属性,每个值有几块。通用属性按 COMMON_ATTRIBUTES 的顺序排在前面,
* 语言自己的属性按第一次出现的先后排在后面。显示名取翻译插件的 names,没给的用 id。
*/
export function fileAttributes(blocks: readonly Attributed[], names: AttributeNames | undefined): FileAttribute[] {
const counts = new Map<string, Map<string, number>>()
const visit = (list: readonly Attributed[]): void => {
for (const block of list) {
for (const [id, value] of Object.entries(block.attributes)) {
const values = counts.get(id) ?? new Map<string, number>()
counts.set(id, values.set(value, (values.get(value) ?? 0) + 1))
}
visit(block.children)
}
}
visit(blocks)
const common: readonly string[] = COMMON_ATTRIBUTES
const rank = (id: string): number => common.includes(id) ? common.indexOf(id) : common.length
// sort 是稳定的:语言自己的属性同排在最后,保持第一次出现的先后
return [...counts].sort(([a], [b]) => rank(a) - rank(b)).map(([id, values]) => ({
id,
name: names?.attributes[id] ?? id,
values: [...values].map(([value, count]) => ({ id: value, name: names?.values[id]?.[value] ?? value, count })),
}))
}