import type { Access, BlockAttributes, Node } from '../contract' /** 具名子节点,空的跳过。 */ export function named(node: Node | null | undefined): Node[] { return node?.namedChildren.filter((child): child is Node => child !== null) ?? [] } export type RustVisibility = 'public' | 'module' | 'super' | 'crate' export function firstSentence(text: string): string { const trimmed = text.trim() const sentence = trimmed.match(/^[\s\S]*?[。!?.!?]/) return (sentence ? sentence[0] : trimmed.split('\n')[0] ?? '').trim() } /** 文档注释去掉记号后的第一句;普通注释是整段去掉记号。 */ export function commentPlain(node: Node): string { const doc = node.childForFieldName('doc') const raw = doc ? doc.text : node.text.replace(/^\/\/[/!]?\s?/, '').replace(/^\/\*[*!]?\s?/, '').replace(/\s*\*\/$/, '') return raw.replace(/\n/g, ' ').trim() } /** 说明行用的第一句。没有文字时没有。 */ export function docSentence(node: Node | undefined): string | undefined { if (!node) return undefined const sentence = firstSentence(commentPlain(node)) return sentence || undefined } /** 空白收成一个空格,方便属性表里的类型、签名。 */ export function oneLine(text: string): string { return text.replace(/\s+/g, ' ').trim() } /** 节点上的可见性。pub(in …) 不在这四档里,按本 crate 算(只本包能用)。 */ export function rustVisibility(node: Node): RustVisibility { const vis = named(node).find(child => child.type === 'visibility_modifier') if (!vis) return 'module' const words = new Set(vis.children.map(child => child?.type)) if (words.has('super')) return 'super' if (words.has('crate') || words.has('in')) return 'crate' return 'public' } /** 公开写在 facts.access 上;其余写在块的 access 属性上,盖过 facts。 */ export function visibilityFacts(node: Node): { access?: Access, attributes?: BlockAttributes } { const visibility = rustVisibility(node) return visibility === 'public' ? { access: 'public' } : { attributes: { access: visibility } } } /** 参数表括号里的原文,空白收成一行。 */ export function signatureParams(fn: Node): string { const params = fn.childForFieldName('parameters') if (!params) return '' return oneLine(params.text.replace(/^\(/, '').replace(/\)$/, '')) } /** 返回类型原文;没写是 ()。 */ export function returnText(fn: Node): string { const returned = fn.childForFieldName('return_type') return returned ? oneLine(returned.text) : '()' } /** 函数体里直接写的 match,每一支的模式和「做什么」。 */ export function matchArms(fn: Node): { pattern: string, does: string }[] { const body = fn.childForFieldName('body') if (!body) return [] const arms: { pattern: string, does: string }[] = [] for (const statement of named(body)) { const expr = statement.type === 'expression_statement' ? named(statement)[0] : statement if (expr?.type !== 'match_expression') continue const block = expr.childForFieldName('body') for (const arm of named(block).filter(child => child.type === 'match_arm')) { const pattern = arm.childForFieldName('pattern') if (pattern) arms.push({ pattern: oneLine(pattern.text), does: armDoes(arm) }) } } return arms } /** 变体携带的数据:没带是空的,元组变体是类型,结构变体是字段原文。body 是变体的 body。 */ export function variantPayload(body: Node | undefined): string | undefined { if (!body) return undefined if (body.type === 'ordered_field_declaration_list') { const types = named(body).filter(child => child.type !== 'attribute_item').map(child => oneLine(child.text)) return types.join(', ') || undefined } return oneLine(body.text) } /** 写在这条前面、紧挨着的属性(#[…]),从外往里。 */ export function leadingAttributes(node: Node): Node[] { const attrs: Node[] = [] let prev = node.previousNamedSibling while (prev?.type === 'attribute_item') { attrs.unshift(prev) prev = prev.previousNamedSibling } return attrs } /** 属性的名字:get、serde、axum::post、tauri::command。 */ export function attributeName(attributeItem: Node): string | undefined { const attribute = named(attributeItem).find(child => child.type === 'attribute') const head = attribute ? named(attribute)[0] : undefined return head?.text } /** 这条自己的属性,含写在它前面的。 */ export function itemAttributes(node: Node): Node[] { return [...leadingAttributes(node), ...named(node).filter(child => child.type === 'attribute_item')] } /** derive(...) 里列出的名字。 */ export function deriveNames(node: Node): string[] { const names: string[] = [] for (const item of itemAttributes(node)) { if (attributeName(item) !== 'derive') continue const attribute = named(item).find(child => child.type === 'attribute') const tree = attribute?.children.find(child => child?.type === 'token_tree') if (!tree) continue for (const name of named(tree)) { if (name.type === 'identifier') names.push(name.text) else if (name.type === 'scoped_identifier') names.push(name.childForFieldName('name')?.text ?? name.text) } } return names } /** 字符串字面量里的原文,不含引号。 */ export function stringContent(node: Node | undefined): string | undefined { if (!node) return undefined if (node.type === 'string_content') return node.text const content = node.children.find(child => child?.type === 'string_content') return content?.text } /** 宏调用的名字和括号里的具名实参。 */ export function macroInvocation(node: Node): { name: string, args: Node[] } | undefined { if (node.type !== 'macro_invocation') return undefined const macro = node.childForFieldName('macro') const args = node.children.find(child => child?.type === 'token_tree') if (!macro || !args) return undefined return { name: macro.text, args: named(args) } } /** 函数写在 impl 或 trait 里面,参数表叫「参数」而不是「输入参数」。 */ export function isMethod(node: Node): boolean { const parent = node.parent return parent?.type === 'declaration_list' && (parent.parent?.type === 'impl_item' || parent.parent?.type === 'trait_item') } /** 结构体的字段:名字、类型、可见性。元组结构体没有名字,不进这张表。 */ export function structFields(node: Node): { name: Node, type: Node, visibility: RustVisibility }[] { const body = node.childForFieldName('body') if (body?.type !== 'field_declaration_list') return [] return named(body).flatMap((field) => { if (field.type !== 'field_declaration') return [] const name = field.childForFieldName('name') const type = field.childForFieldName('type') return name && type ? [{ name, type, visibility: rustVisibility(field) }] : [] }) } /** trait 或 impl 主体里的函数(含只写签名的)。 */ export function bodyFunctions(node: Node): Node[] { const body = node.childForFieldName('body') return named(body).filter(child => child.type === 'function_item' || child.type === 'function_signature_item') } function armDoes(arm: Node): string { const comment = firstComment(arm) if (comment) return firstSentence(commentPlain(comment)) || '—' const value = arm.childForFieldName('value') if (!value) return '—' if (value.type === 'return_expression') { const returned = named(value)[0] return returned ? `返回 ${oneLine(returned.text)}` : '返回' } if (value.type === 'block') { const inner = firstComment(value) if (inner) return firstSentence(commentPlain(inner)) || '—' const statement = named(value)[0] return statement ? oneLine(statement.text) : '—' } return oneLine(value.text) } function firstComment(node: Node): Node | undefined { if (node.type === 'line_comment' || node.type === 'block_comment') return node for (const child of node.children) { if (!child) continue const found = firstComment(child) if (found) return found } return undefined }