✨ feat(vscode): 代码阅读把说明里的 @param / @return / @throws 拆到参数表、输出、异常表
This commit is contained in:
1 parent
c2982537d7
commit
0958506681
43 files changed
+1300
-323
No files matched your search
@@ -3,8 +3,6 @@ import { attributeRows, enumRows, functionRows, implRows, importRows, moduleRows
|
||||
import { isCrateRoot } from '../rules/rustTree'
|
||||
import { createZhTemplateTranslator, KIND_ZH, ZH_ATTRIBUTE_NAMES } from './zhTemplate'
|
||||
|
||||
const say = plainSay()
|
||||
|
||||
const template = createZhTemplateTranslator({
|
||||
id: 'rust.zh-template',
|
||||
label: '中文直译',
|
||||
@@ -55,6 +53,7 @@ const template = createZhTemplateTranslator({
|
||||
mapLiterals: {},
|
||||
stringNodes: ['string_literal'],
|
||||
commentText,
|
||||
docNodes,
|
||||
nodeTemplates: {},
|
||||
})
|
||||
|
||||
@@ -82,6 +81,7 @@ export const rustZhTranslator: Translator = {
|
||||
}
|
||||
|
||||
function describe(block: FactBlock, document: SourceDocument): PropertyRow[] {
|
||||
const say = plainSay(node => template.doc(node, document))
|
||||
switch (block.kind) {
|
||||
case 'import':
|
||||
return importRows(block, say)
|
||||
@@ -130,6 +130,17 @@ function original(node: Node, span: SourceSpan, { source, language }: SourceDocu
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* 一项的文档注释:`///` 一行一个节点,从第一条起往下连着写的(下一行紧跟着另一条 `///`)都算这一段;块注释本来就是一个节点。
|
||||
*/
|
||||
function docNodes(first: Node): Node[] {
|
||||
const outer = (node: Node): boolean => node.type === 'line_comment' && node.namedChildren.some(child => child?.type === 'outer_doc_comment_marker')
|
||||
const nodes = [first]
|
||||
for (let next = first.nextSibling; outer(first) && next && outer(next) && next.startPosition.row === nodes.at(-1)!.startPosition.row + 1; next = next.nextSibling)
|
||||
nodes.push(next)
|
||||
return nodes
|
||||
}
|
||||
|
||||
/** 行注释去掉 `//`(文档注释连 `/`、`!`)和后面一个空格,缩进留给 Markdown;块注释去掉开头结尾的注释符号。 */
|
||||
function commentText(node: Node, text: string): string {
|
||||
if (node.type === 'line_comment')
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { BlockKind, CallFacts, Inline, LanguageAdapter, PreviewTable, PropertyItem, PropertyRow, Segment, SourceDocument, TokenRole, Translator } from '../contract'
|
||||
import type { BlockKind, CallFacts, Inline, LanguageAdapter, PreviewTable, PreviewText, PropertyItem, PropertyRow, Segment, SourceDocument, TokenRole, Translator } from '../contract'
|
||||
import type { ZhTable } from './zhTemplate'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createPythonAdapter } from '../languages/python'
|
||||
import { createTypeScriptAdapter } from '../languages/typescript'
|
||||
import { flatSegments, propertyLines, readFixture, segmentText, tableLines, walk, WASM_DIR, withBlocks } from '../testSupport'
|
||||
import { flatSegments, inlineOf, previewText, propertyLines, readFixture, tableLines, walk, WASM_DIR, withBlocks } from '../testSupport'
|
||||
import { pythonZhTranslator } from './pythonZh'
|
||||
import { typescriptZhTranslator } from './typescriptZh'
|
||||
import { createZhTemplateTranslator } from './zhTemplate'
|
||||
@@ -58,14 +58,14 @@ function valueOf(rows: PropertyRow[] | undefined, label: string): Inline[] {
|
||||
return value
|
||||
}
|
||||
|
||||
/** 译文写成文字,框写成 `⟦框头|框里的文字⟧`。 */
|
||||
function text(segments: Inline[] | undefined): string {
|
||||
return segmentText(segments ?? [])
|
||||
/** 译文写成文字,框写成 `⟦框头|框里的文字⟧`;Markdown 取原文。 */
|
||||
function text(segments: PreviewText | undefined): string {
|
||||
return previewText(segments ?? [])
|
||||
}
|
||||
|
||||
/** 带下标的段(框里的也算),按出现顺序写成 [文字, 下标]。 */
|
||||
function linked(segments: Inline[] | undefined): [string, number][] {
|
||||
return flatSegments(segments ?? []).flatMap(segment => segment.offset === undefined ? [] : [[segment.text, segment.offset] as [string, number]])
|
||||
function linked(segments: PreviewText | undefined): [string, number][] {
|
||||
return flatSegments(inlineOf(segments ?? [])).flatMap(segment => segment.offset === undefined ? [] : [[segment.text, segment.offset] as [string, number]])
|
||||
}
|
||||
|
||||
function texts(map: Map<number, Translated>): Record<number, string> {
|
||||
@@ -578,7 +578,7 @@ describe('属性表', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('说明:紧跟名称,去掉注释符号、引号、共同缩进和首尾空行,保留换行和 @ 标签行;没有文字不显示', async () => {
|
||||
it('说明:紧跟名称,去掉注释符号、引号、共同缩进和首尾空行,保留换行;块标签拆到参数表、输出参数后面;没有文字不显示', async () => {
|
||||
const tsSource = [
|
||||
'class K {',
|
||||
' /**',
|
||||
@@ -595,7 +595,8 @@ describe('属性表', () => {
|
||||
'}',
|
||||
].join('\n')
|
||||
const tsRows = rowTexts(await describeAll(ts, 'typescript', typescriptZhTranslator, tsSource))
|
||||
expect(tsRows[2].slice(0, 3)).toEqual(['名称: load', '说明: 读配置。\n\n例子:\n load(\'a\')\n@param path 文件路径\n@returns 配置', '访问级别: 公共'])
|
||||
expect(tsRows[2].slice(0, 3)).toEqual(['名称: load', '说明: 读配置。\n\n例子:\n load(\'a\')', '访问级别: 公共'])
|
||||
expect(tsRows[2].slice(-3)).toEqual(['输入参数: 1 个', ' path | 字符串 | | 文件路径', '输出参数: 未标注 — 配置'])
|
||||
expect(tsRows[11].slice(0, 2)).toEqual(['名称: empty', '访问级别: 公共'])
|
||||
|
||||
const pySource = [
|
||||
@@ -615,7 +616,7 @@ describe('属性表', () => {
|
||||
expect(pyRows[8].slice(0, 2)).toEqual(['名称: g', '访问级别: 公共'])
|
||||
})
|
||||
|
||||
it('注释块和函数的说明是 Markdown 原文:只去注释符号、引号和共同缩进,reST 角色、双反引号、JSDoc 标签、HTML 照抄', async () => {
|
||||
it('注释块和函数的说明是 Markdown 原文:只去注释符号、引号和共同缩进,reST 角色、双反引号、{@link}、HTML 照抄,块标签不进正文', async () => {
|
||||
const pySource = [
|
||||
'"""手机号规范化 / 脱敏工具.',
|
||||
'',
|
||||
@@ -662,7 +663,7 @@ describe('属性表', () => {
|
||||
expect(tsText[3]).toBe('单行块注释')
|
||||
const tsRows = await describeAll(ts, 'typescript', typescriptZhTranslator, tsSource)
|
||||
expect(tsRows.get(4)?.find(row => row.label === '说明')?.value).toEqual({
|
||||
markdown: '读配置,见 {@link load}。\n\n```ts\nload(\'a\')\n```\n@param path 文件路径',
|
||||
markdown: '读配置,见 {@link load}。\n\n```ts\nload(\'a\')\n```',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -854,12 +855,12 @@ describe('属性表', () => {
|
||||
})
|
||||
|
||||
describe('源码下标(跳到定义用)', () => {
|
||||
function itemCells(rows: PropertyRow[] | undefined, label: string): Inline[][] {
|
||||
function itemCells(rows: PropertyRow[] | undefined, label: string): PreviewText[] {
|
||||
return rows?.find(row => row.label === label)?.items?.flatMap(item => item.cells) ?? []
|
||||
}
|
||||
|
||||
function propertySegments(rows: PropertyRow[]): Segment[] {
|
||||
const itemSegments = (items: PropertyItem[] | undefined): Segment[] => (items ?? []).flatMap(item => [...flatSegments(item.cells.flat()), ...itemSegments(item.items)])
|
||||
const itemSegments = (items: PropertyItem[] | undefined): Segment[] => (items ?? []).flatMap(item => [...flatSegments(item.cells.flatMap(inlineOf)), ...itemSegments(item.items)])
|
||||
return rows.flatMap(row => [...('markdown' in row.value ? [] : flatSegments(row.value)), ...itemSegments(row.items)])
|
||||
}
|
||||
|
||||
@@ -1033,6 +1034,82 @@ describe('源码下标(跳到定义用)', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('说明里的块标签', () => {
|
||||
it('render_conversation_brief:@param 进输入参数表的说明列,@return 跟在输出参数后面,说明里只剩正文', async () => {
|
||||
const rows = await describeAll(py, 'python', pythonZhTranslator, readFixture('conversationBrief.py'))
|
||||
const lines = rowTexts(rows)[25]
|
||||
expect(lines.find(line => line.startsWith('说明'))).toBe('说明: 把对话转写成「前情摘要 + 近期消息」一段文字; 什么都没有时返回空串.')
|
||||
expect(lines.join('\n')).not.toMatch(/@param|@return/)
|
||||
const params = rows.get(25)?.find(row => row.label === '输入参数')
|
||||
expect(params?.columns).toEqual(['名称', '类型', '默认值', '说明'])
|
||||
expect(params?.items?.map(item => [text(item.cells[0]), text(item.cells[3])])).toEqual([
|
||||
['messages', '对话历史 (`messages` 状态).'],
|
||||
['summary', '更早对话的压缩摘要, 没有就传 `None`.'],
|
||||
['speakers', '转写谁说的话: [USER] / [ASSISTANT]; 只要用户原话时只传 [USER].'],
|
||||
['max_lines', '近期消息最多留几行, 多了从头丢弃并注明.'],
|
||||
])
|
||||
expect(params?.items?.[3].cells[3]).toEqual({ markdown: '近期消息最多留几行, 多了从头丢弃并注明.' })
|
||||
const output = rows.get(25)?.find(row => row.label === '输出参数')
|
||||
expect(output?.value).toEqual([{ text: '字符串', role: 'type' }])
|
||||
expect(output?.note).toEqual({ markdown: '以 `[会话进展]` 开头的转写.' })
|
||||
expect(lines.some(line => line.startsWith('对不上的参数'))).toBe(false)
|
||||
})
|
||||
|
||||
it('gate.py:@throws 成「异常」表,第一行带完整链接,第二行是不带路径的引用', async () => {
|
||||
const rows = await describeAll(py, 'python', pythonZhTranslator, readFixture('localRuntimeGate.py'))
|
||||
const lines = rowTexts(rows)[18]
|
||||
expect(lines.join('\n')).not.toMatch(/@throws/)
|
||||
expect(lines.slice(-3)).toEqual([
|
||||
'异常: 2 个',
|
||||
' [DesktopOnly](backend/src/luffy_agent/sandbox/local_runtime/visibility.py#DesktopOnly) | 请求不是桌面端发来的',
|
||||
' [LocalRuntimeDisabled] | 总开关关着',
|
||||
])
|
||||
const throws = rows.get(18)?.find(row => row.label === '异常')
|
||||
expect(throws?.columns).toEqual(['异常', '触发条件'])
|
||||
expect(throws?.items?.[1].cells).toEqual([{ markdown: '[LocalRuntimeDisabled]' }, { markdown: '总开关关着' }])
|
||||
})
|
||||
|
||||
it('名字对不上的 @param 排在说明下面,提示色,悬停「没有这个参数」;没有对上的参数时参数表不加说明列', async () => {
|
||||
const source = [
|
||||
'/**',
|
||||
' * 读文件。',
|
||||
' * @param pth 路径',
|
||||
' * @throws Error 读不到',
|
||||
' */',
|
||||
'export function read(path: string): string { return \'\' }',
|
||||
].join('\n')
|
||||
const rows = await describeAll(ts, 'typescript', typescriptZhTranslator, source)
|
||||
const lines = rowTexts(rows)[1]
|
||||
expect(lines.slice(0, 4)).toEqual(['名称: read', '说明: 读文件。', '对不上的参数: 1 个', ' pth | 路径'])
|
||||
const unmatched = rows.get(1)?.find(row => row.label === '对不上的参数')
|
||||
expect(unmatched?.items?.[0].cells[0]).toEqual([{ text: 'pth', role: 'hint', tooltip: '没有这个参数' }])
|
||||
expect(rows.get(1)?.find(row => row.label === '输入参数')?.columns).toEqual(['名称', '类型', '默认值'])
|
||||
expect(lines.slice(-2)).toEqual(['异常: 1 个', ' [Error] | 读不到'])
|
||||
})
|
||||
|
||||
it('类:没有构造参数表可对,@param 照写成「构造参数」表;异常类的 @param 对到构造参数表,*args 去掉星号对名字', async () => {
|
||||
const source = [
|
||||
'class K:',
|
||||
' """类。',
|
||||
'',
|
||||
' @param a 甲',
|
||||
' """',
|
||||
'',
|
||||
'class E(Exception):',
|
||||
' """出错了。',
|
||||
'',
|
||||
' @param code 错误码',
|
||||
' @param args 其余参数',
|
||||
' """',
|
||||
' def __init__(self, code: int, *args):',
|
||||
' pass',
|
||||
].join('\n')
|
||||
const lines = rowTexts(await describeAll(py, 'python', pythonZhTranslator, source))
|
||||
expect(lines[1]).toEqual(['名称: K', '说明: 类。', '访问级别: 公共', '构造参数: 1 个', ' a | 甲'])
|
||||
expect(lines[7].slice(-3)).toEqual(['构造参数: 2 个', ' code | 整数 | | 错误码', ' *args | 未标注 | | 其余参数'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('跨几行写的调用', () => {
|
||||
const routes = readFixture('routes.py')
|
||||
const calls = readFixture('calls.ts.txt')
|
||||
@@ -1189,10 +1266,10 @@ describe('跨几行写的调用', () => {
|
||||
expect(asked).toEqual(expect.arrayContaining(['PaymentCapabilityDiagnostic', 'CredentialPresence']))
|
||||
const nested = line?.table?.items[6]
|
||||
expect(nested?.table?.columns).toEqual(['参数', '值'])
|
||||
expect(nested?.cells[1].find(piece => 'role' in piece && piece.text === 'CredentialPresence')).toEqual({ text: 'CredentialPresence', role: 'type', offset: payment.indexOf('CredentialPresence(', payment.indexOf('credentials_present=')) })
|
||||
expect(inlineOf(nested?.cells[1] ?? []).find(piece => 'role' in piece && piece.text === 'CredentialPresence')).toEqual({ text: 'CredentialPresence', role: 'type', offset: payment.indexOf('CredentialPresence(', payment.indexOf('credentials_present=')) })
|
||||
expect(nested?.table?.items[0].cells[0]).toEqual([{ text: 'mp_app_secret', role: 'variable', offset: payment.indexOf('mp_app_secret=') }])
|
||||
const unresolved = (await translateAll(py, 'python', pythonZhTranslator, payment)).get(64)
|
||||
expect(segmentText(unresolved?.table?.items[6].cells[1] ?? [])).toBe('调用 CredentialPresence · 4 个参数')
|
||||
expect(text(unresolved?.table?.items[6].cells[1])).toBe('调用 CredentialPresence · 4 个参数')
|
||||
})
|
||||
|
||||
it('嵌套一层层往下套;await 的调用写「等待」', async () => {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { Access, AttributeNames, BaseFacts, BlockKind, CallArgument, CallFacts, ClassFacts, CommandContext, CommandFacts, CommandForm, CommandReading, CommandRule, CommandWord, CommonAttribute, DeclaratorFacts, Defines, DirectiveFacts, EmbeddedReading, EntryFacts, EnumFacts, ExceptionFacts, FactBlock, FunctionFacts, HealthFacts, ImportFacts, ImportItem, Inline, InstructionFlag, InterpreterFacts, IterationClause, IterationFacts, IterationResult, LanguageAdapter, Markdown, Node, OptionsFacts, ParameterFacts, PositionalParameter, PreviewTable, PropertyItem, PropertyRow, Redirect, Segment, SegmentGroup, SourceDocument, SourceRange, SourceSpan, StageFacts, StartFacts, TextBlock, TextTranslation, TokenRole, Translator, VariableBlock } from '../contract'
|
||||
import type { Access, AttributeNames, BaseFacts, BlockKind, CallArgument, CallFacts, ClassFacts, CommandContext, CommandFacts, CommandForm, CommandReading, CommandRule, CommandWord, CommonAttribute, DeclaratorFacts, Defines, DirectiveFacts, Doc, EmbeddedReading, EntryFacts, EnumFacts, ExceptionFacts, FactBlock, FunctionFacts, HealthFacts, ImportFacts, ImportItem, Inline, InstructionFlag, InterpreterFacts, IterationClause, IterationFacts, IterationResult, LanguageAdapter, Markdown, Node, OptionsFacts, ParameterFacts, PositionalParameter, PreviewTable, PropertyItem, PropertyRow, Redirect, Segment, SegmentGroup, SourceDocument, SourceRange, SourceSpan, StageFacts, StartFacts, TextBlock, TextTranslation, TokenRole, Translator, VariableBlock } from '../contract'
|
||||
import type { ParamDocs } from '../rules/docRows'
|
||||
import { readDoc } from '../docTags'
|
||||
import { commonIndent, splitLines } from '../editText'
|
||||
import { docBodyRows, paramDocCell, paramDocs, returnNote, throwsRows, unmatchedParamRows, writtenParamRows } from '../rules/docRows'
|
||||
|
||||
/** 按所在语法节点区分译法的关键字,例如 Python 的 else:if 后「否则」,for / while 后「未中断时」。 */
|
||||
export interface ContextualWord {
|
||||
@@ -46,6 +49,11 @@ export interface ZhTable {
|
||||
stringNodes: string[]
|
||||
/** 注释(或 docstring)节点去掉注释符号后的原文,可以多行;text 是节点的源码去掉代码阅读的记号(SourceDocument.markers)以后剩下的。 */
|
||||
commentText: (node: Node, text: string) => string
|
||||
/**
|
||||
* 说明是连着几条行注释写的(Rust 的 `///`)时,从第一条(facts.doc)起这一段的各条注释节点,照源码顺序;
|
||||
* 不填就是那一个节点(JSDoc、docstring 本来就是一个节点)。
|
||||
*/
|
||||
docNodes?: (first: Node) => Node[]
|
||||
/**
|
||||
* 节点模板:节点类型 → 按中文语序重排的译文。命中的节点只输出模板里的译词和槽位,节点自己的关键字、标点不再输出;
|
||||
* 槽位里的子节点照常递归翻译。模板引用的子节点缺了,或节点没整个落在要翻的这一段里,就不重排、照常翻。
|
||||
@@ -661,9 +669,9 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return joinTokens(tokensIn(node, nodeSpan(node), document))
|
||||
}
|
||||
|
||||
function doc(node: Node, document: SourceDocument): Markdown | undefined {
|
||||
const markdown = docText([commentOf(node, document)])
|
||||
return markdown ? { markdown } : undefined
|
||||
function doc(node: Node, document: SourceDocument): Doc | undefined {
|
||||
const markdown = docText((table.docNodes?.(node) ?? [node]).map(part => commentOf(part, document)))
|
||||
return markdown ? readDoc(markdown) : undefined
|
||||
}
|
||||
|
||||
function describe(block: FactBlock, document: SourceDocument): PropertyRow[] {
|
||||
@@ -817,8 +825,8 @@ interface Phrases {
|
||||
module: (node: Node) => Segment[]
|
||||
/** 源码里的一个名字原样照抄、按 role 着色(函数、类的名称)。 */
|
||||
name: (node: Node, role: TokenRole) => Segment[]
|
||||
/** 函数、类的说明(JSDoc、docstring):去掉注释符号、引号和共同缩进的 Markdown 原文;没有文字时没有。 */
|
||||
doc: (node: Node) => Markdown | undefined
|
||||
/** 函数、类的说明(JSDoc、docstring):去掉注释符号、引号和共同缩进的 Markdown 原文拆成正文和块标签;没有文字时没有。 */
|
||||
doc: (node: Node) => Doc | undefined
|
||||
/** 一条要执行的命令:数组写法交给命令规则读,shell 写法是宿主读好的嵌着的代码(SourceDocument.embedded),可以带一张选项表。 */
|
||||
command: (form: CommandForm) => { text: Inline[], table?: PreviewTable }
|
||||
}
|
||||
@@ -907,38 +915,50 @@ function aliased(item: Inline[], alias: Node | undefined, say: Phrases): Inline[
|
||||
}
|
||||
|
||||
/**
|
||||
* 函数:名称 / 说明 / 访问级别 / 异步(只有分这个的语言才有)/ 装饰器 / 输入参数 / 输出参数。
|
||||
* 输入参数是一张表,列名是名称 / 类型 / 默认值,每个参数一行;没有默认值的格子留空。
|
||||
* 函数:名称 / 说明 / 对不上的参数 / 访问级别 / 异步(只有分这个的语言才有)/ 装饰器 / 输入参数 / 输出参数 / 异常。
|
||||
* 输入参数是一张表,列名是名称 / 类型 / 默认值,每个参数一行;没有默认值的格子留空。说明里的块标签拆开放:
|
||||
* `@param` 进输入参数表多出的「说明」列(有参数对上才有这一列),名字对不上的排在说明下面;`@return` 跟在输出参数的类型后面;
|
||||
* `@throws` 是「异常」一行。
|
||||
*/
|
||||
function functionRows(facts: FunctionFacts, say: Phrases): PropertyRow[] {
|
||||
const doc = facts.doc && say.doc(facts.doc)
|
||||
const names = facts.positional ? facts.positional.flatMap(param => param.name ? [param.name.text] : []) : facts.parameters.map(param => param.name.text)
|
||||
const docs = paramDocs(doc, names)
|
||||
return [
|
||||
{ label: '名称', value: facts.name ? say.name(facts.name, 'function') : [hint('匿名')] },
|
||||
...docRows(facts.doc, say),
|
||||
...docBodyRows(doc),
|
||||
...unmatchedParamRows(docs.unmatched),
|
||||
...accessRows(facts.access),
|
||||
...flagRows('async', facts.async),
|
||||
...decoratorRows(facts.decorators, say),
|
||||
...(facts.positional
|
||||
? positionalRows(facts, facts.positional, say)
|
||||
? positionalRows(facts, facts.positional, say, docs, doc)
|
||||
: [
|
||||
parameterRow('输入参数', facts.parameters, say),
|
||||
{ label: '输出参数', value: facts.returnType ? say.translated(facts.returnType) : [hint('未标注')] },
|
||||
parameterRow('输入参数', facts.parameters, say, docs),
|
||||
{ label: '输出参数', value: facts.returnType ? say.translated(facts.returnType) : [hint('未标注')], ...returnNote(doc) },
|
||||
]),
|
||||
...throwsRows(doc),
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* 按位置取参数的函数(shell):输入参数写「N 个,按位置」,下面一张表「位置 | 函数里叫」(没起名的写 $N);
|
||||
* 按位置取参数的函数(shell):输入参数写「N 个,按位置」,下面一张表「位置 | 函数里叫」(没起名的写 $N;`@param` 按起的名字对上时多一列说明);
|
||||
* 输出是它打印的那几句(调用处用 $(函数 …) 拿去用时注明);返回是写了的 return 值,没写 return、也不打印时是最后一句成不成功。
|
||||
*/
|
||||
function positionalRows(facts: FunctionFacts, positional: PositionalParameter[], say: Phrases): PropertyRow[] {
|
||||
function positionalRows(facts: FunctionFacts, positional: PositionalParameter[], say: Phrases, docs: ParamDocs, doc: Doc | undefined): PropertyRow[] {
|
||||
const described = positional.some(param => param.name && docs.of(param.name.text))
|
||||
const inputs: PropertyRow = positional.length === 0
|
||||
? { label: '输入参数', value: [hint('无')] }
|
||||
: {
|
||||
label: '输入参数',
|
||||
value: [hint(`${positional.length} 个,按位置`)],
|
||||
columns: ['位置', '函数里叫'],
|
||||
columns: described ? ['位置', '函数里叫', '说明'] : ['位置', '函数里叫'],
|
||||
items: positional.map(param => ({
|
||||
cells: [[hint(`第 ${param.position} 个`)], param.name ? say.name(param.name, 'variable') : [segment(`$${param.position}`, 'variable', undefined)]],
|
||||
cells: [
|
||||
[hint(`第 ${param.position} 个`)],
|
||||
param.name ? say.name(param.name, 'variable') : [segment(`$${param.position}`, 'variable', undefined)],
|
||||
...(described ? [param.name ? paramDocCell(docs, param.name.text) : []] : []),
|
||||
],
|
||||
...defining(param.name),
|
||||
})),
|
||||
}
|
||||
@@ -950,7 +970,9 @@ function positionalRows(facts: FunctionFacts, positional: PositionalParameter[],
|
||||
const result: PropertyRow[] = values.length > 0
|
||||
? [{ label: '返回', value: joined(values, '、') }]
|
||||
: prints.length === 0 ? [{ label: '返回', value: [hint('最后一句成不成功')] }] : []
|
||||
return [inputs, ...output, ...result]
|
||||
// `@return` 跟在返回那一行后面;只有打印、没写 return 时跟在输出那一行后面
|
||||
const [noted] = [...result, ...output]
|
||||
return [inputs, ...[...output, ...result].map(row => row === noted ? { ...row, ...returnNote(doc) } : row)]
|
||||
}
|
||||
|
||||
/** 解释器:叫什么,怎么找到它(按 PATH 找,或写死的路径)。 */
|
||||
@@ -991,7 +1013,7 @@ function flagValue(flag: InstructionFlag): Inline[] {
|
||||
* 接着前面某个阶段建的写「阶段 X」)/ 平台(写了 --platform 才有)。
|
||||
*/
|
||||
function stageRows(facts: StageFacts, say: Phrases): PropertyRow[] {
|
||||
const doc = (facts.doc ?? []).map(node => say.doc(node)?.markdown).filter(Boolean).join('\n')
|
||||
const doc = (facts.doc ?? []).map(node => say.doc(node)?.body?.markdown).filter(Boolean).join('\n')
|
||||
const platform = facts.flags.find(flag => flag.name === 'platform')
|
||||
const image: Inline[] = facts.stage
|
||||
? [segment('阶段', 'keyword', undefined), segment(' ', 'plain', undefined), ...say.name(facts.image, 'variable')]
|
||||
@@ -1078,17 +1100,22 @@ function scopedVariableRows(declarators: DeclaratorFacts[], scope: 'build' | 'en
|
||||
}]
|
||||
}
|
||||
|
||||
/** 参数表:列名是名称 / 类型 / 默认值,每个参数一行,讲的定义是参数名;没有默认值的格子留空。 */
|
||||
function parameterRow(label: string, parameters: ParameterFacts[], say: Phrases): PropertyRow {
|
||||
/**
|
||||
* 参数表:列名是名称 / 类型 / 默认值,每个参数一行,讲的定义是参数名;没有默认值的格子留空。
|
||||
* 说明里有参数对上了(docs)时多一列「说明」,没对上的格子留空。
|
||||
*/
|
||||
function parameterRow(label: string, parameters: ParameterFacts[], say: Phrases, docs: ParamDocs): PropertyRow {
|
||||
const described = parameters.some(param => docs.of(param.name.text))
|
||||
return {
|
||||
label,
|
||||
value: count(parameters.length),
|
||||
columns: ['名称', '类型', '默认值'],
|
||||
columns: described ? ['名称', '类型', '默认值', '说明'] : ['名称', '类型', '默认值'],
|
||||
items: parameters.map(param => ({
|
||||
cells: [
|
||||
param.optional ? [...say.translated(param.name), { text: '?', role: 'plain' }] : say.translated(param.name),
|
||||
param.type ? say.translated(param.type) : [hint('未标注')],
|
||||
param.default ? say.translated(param.default) : [],
|
||||
...(described ? [paramDocCell(docs, param.name.text)] : []),
|
||||
],
|
||||
...defining(param.name),
|
||||
})),
|
||||
@@ -1098,19 +1125,23 @@ function parameterRow(label: string, parameters: ParameterFacts[], say: Phrases)
|
||||
/**
|
||||
* 类:名称 / 说明 / 访问级别 / 抽象(只有分这个的语言才有)/ 装饰器 / 类型参数 / 父类 / 实现的接口,
|
||||
* 说明及以后各行有才显示;类型参数、父类、实现的接口各是一行,多项用逗号隔开。类型参数那一行讲的是这几个泛型参数的定义。
|
||||
* 类没有构造参数表可对,说明里的 `@param` 照写成「构造参数」一张「名称 | 说明」表;`@throws` 是「异常」一行。
|
||||
*/
|
||||
function classRows(facts: ClassFacts, say: Phrases): PropertyRow[] {
|
||||
const listRow = (label: string, parts: Inline[][], defines: Defines = {}): PropertyRow[] =>
|
||||
parts.length > 0 ? [{ label, value: joined(parts, ', '), ...defines }] : []
|
||||
const doc = facts.doc && say.doc(facts.doc)
|
||||
return [
|
||||
{ label: '名称', value: facts.name ? say.name(facts.name, 'type') : [hint('匿名')] },
|
||||
...docRows(facts.doc, say),
|
||||
...docBodyRows(doc),
|
||||
...accessRows(facts.access),
|
||||
...flagRows('abstract', facts.abstract),
|
||||
...decoratorRows(facts.decorators, say),
|
||||
...listRow('类型参数', facts.typeParameters.map(say.translated), defining(...facts.typeParameters)),
|
||||
...listRow('父类', facts.bases.map(base => baseText(base, say))),
|
||||
...listRow('实现的接口', facts.implements.map(say.translated)),
|
||||
...writtenParamRows(doc?.params ?? [], '构造参数'),
|
||||
...throwsRows(doc),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1129,7 +1160,7 @@ function enumRows(facts: EnumFacts, say: Phrases): PropertyRow[] {
|
||||
const described = docs.some(Boolean)
|
||||
return [
|
||||
{ label: '名称', value: say.name(facts.name, 'type') },
|
||||
...docRows(facts.doc, say),
|
||||
...docBodyRows(facts.doc && say.doc(facts.doc)),
|
||||
...accessRows(facts.access),
|
||||
...decoratorRows(facts.decorators, say),
|
||||
...baseRows(facts.bases, say),
|
||||
@@ -1153,6 +1184,7 @@ function enumRows(facts: EnumFacts, say: Phrases): PropertyRow[] {
|
||||
* 异常(Python、TypeScript 同一张表):名称 / 说明 / 访问级别 / 装饰器 / 继承(照写的父类),说明及以后有才显示;
|
||||
* 类属性收成一张表:属性 | 类型 | 值 | 说明,一个属性都没写类型时没有「类型」列、都没有说明时没有「说明」列,没写的格子留空;
|
||||
* 构造参数同函数的输入参数表。没有类属性、没写构造函数时没有这两行。
|
||||
* 说明里的 `@param` 对到构造参数表(同函数的输入参数,对不上的排在说明下面;没写构造函数时同类,照写成一张表),`@throws` 是「异常」一行。
|
||||
*/
|
||||
function exceptionRows(facts: ExceptionFacts, say: Phrases): PropertyRow[] {
|
||||
const docs = facts.fields.map(field => docsText(field.docs, say))
|
||||
@@ -1172,14 +1204,18 @@ function exceptionRows(facts: ExceptionFacts, say: Phrases): PropertyRow[] {
|
||||
...defining(field.name),
|
||||
})),
|
||||
}
|
||||
const doc = facts.doc && say.doc(facts.doc)
|
||||
const params = facts.parameters && paramDocs(doc, facts.parameters.map(param => param.name.text))
|
||||
return [
|
||||
{ label: '名称', value: facts.name ? say.name(facts.name, 'type') : [hint('匿名')] },
|
||||
...docRows(facts.doc, say),
|
||||
...docBodyRows(doc),
|
||||
...unmatchedParamRows(params?.unmatched ?? []),
|
||||
...accessRows(facts.access),
|
||||
...decoratorRows(facts.decorators, say),
|
||||
...baseRows(facts.bases, say),
|
||||
...(facts.fields.length > 0 ? [fields] : []),
|
||||
...(facts.parameters ? [parameterRow('构造参数', facts.parameters, say)] : []),
|
||||
...(facts.parameters && params ? [parameterRow('构造参数', facts.parameters, say, params)] : writtenParamRows(doc?.params ?? [], '构造参数')),
|
||||
...throwsRows(doc),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1188,21 +1224,15 @@ function baseRows(bases: BaseFacts[], say: Phrases): PropertyRow[] {
|
||||
return bases.length > 0 ? [{ label: '继承', value: joined(bases.map(base => baseText(base, say)), ', ') }] : []
|
||||
}
|
||||
|
||||
/** 表格里一格说明:几段说明文字的原文用空格连起来。 */
|
||||
/** 表格里一格说明:几段说明文字的正文用空格连起来。 */
|
||||
function docsText(nodes: Node[], say: Phrases): string {
|
||||
return nodes.map(node => say.doc(node)?.markdown).filter(Boolean).join(' ')
|
||||
return nodes.map(node => say.doc(node)?.body?.markdown).filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
function docCell(text: string): Inline[] {
|
||||
return text ? [segment(text, 'comment', undefined)] : []
|
||||
}
|
||||
|
||||
/** 函数、类、常量、变量的说明:有文字才有这一行。 */
|
||||
function docRows(doc: Node | undefined, say: Phrases): PropertyRow[] {
|
||||
const markdown = doc && say.doc(doc)
|
||||
return markdown ? [{ label: '说明', value: markdown }] : []
|
||||
}
|
||||
|
||||
/** 装饰器:源码原样照抄,多个换行。 */
|
||||
function decoratorRows(decorators: Node[], say: Phrases): PropertyRow[] {
|
||||
return decorators.length > 0 ? [{ label: '装饰器', value: joined(decorators.map(say.verbatim), '\n') }] : []
|
||||
@@ -1238,7 +1268,7 @@ function variableRows(block: VariableBlock, say: Phrases): PropertyRow[] {
|
||||
const valueLabel = block.kind === 'const' ? '值' : '初始值'
|
||||
return block.facts.declarators.flatMap((declarator, i) => [
|
||||
{ label: '名称', value: say.translated(declarator.name), ...defining(declarator.name) },
|
||||
...(i === 0 ? docRows(block.facts.doc, say) : []),
|
||||
...(i === 0 ? docBodyRows(block.facts.doc && say.doc(block.facts.doc)) : []),
|
||||
...accessRows(block.facts.access),
|
||||
...(declarator.type ? [{ label: '类型', value: say.translated(declarator.type) }] : []),
|
||||
{ label: valueLabel, ...(declarator.value ? declaredValue(declarator.value, declarator.entries, say) : { value: [hint('无')] }) },
|
||||
|
||||
Reference in new issue
Block a user