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> { 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 'enum': case 'exception': case 'const': case 'variable': return { access: block.facts.access } case 'import': return { typeOnly: flag(block.facts.typeOnly) } 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 /** 库规则认领的块的种类标签:这个种类在筛选栏里也叫它。 */ badge?: { label: string } children: Attributed[] } /** * 整份文件(所有层的块)里出现过的属性,每个值有几块。通用属性按 COMMON_ATTRIBUTES 的顺序排在前面, * 语言自己的属性按第一次出现的先后排在后面。显示名取翻译插件的 names,没给的用 id;库规则的种类用规则给的标签。 */ export function fileAttributes(blocks: readonly Attributed[], names: AttributeNames | undefined): FileAttribute[] { const counts = new Map>() const badges = new Map() 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() counts.set(id, values.set(value, (values.get(value) ?? 0) + 1)) } if (block.badge) badges.set(block.attributes.kind, block.badge.label) visit(block.children) } } visit(blocks) const common: readonly string[] = COMMON_ATTRIBUTES const rank = (id: string): number => common.includes(id) ? common.indexOf(id) : common.length const valueName = (id: string, value: string): string => (id === 'kind' ? badges.get(value) : undefined) ?? names?.values[id]?.[value] ?? value // 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: valueName(id, value), count })), })) }