✨ feat(vscode): 代码阅读的调用嵌套:实参的值又是跨多行的调用时在值那一格里写它的一行、套它的实参表,一层层往下套;埋在式子里后面还跟着东西的调用括起来
This commit is contained in:
1 parent
dc100692b3
commit
285d711740
16 files changed
+471
-55
No files matched your search
@@ -89,6 +89,8 @@ interface Collector {
|
||||
|
||||
const CONTEXT_SEPARATOR = ' > '
|
||||
const CLOSING = new Set([')', ']', '}'])
|
||||
/** 跟在一个式子后面、不属于这个式子的标点:右括号、逗号、分号、冒号。 */
|
||||
const SEPARATORS = new Set([...CLOSING, ',', ';', ':'])
|
||||
const HEADER_OPENERS = new Set(['{', ':'])
|
||||
/** 源码着色里代表名字的词性:着这几种色的具名叶子是标识符、属性名、类型名,能查定义。 */
|
||||
const NAME_ROLES = new Set<TokenRole>(['variable', 'function', 'type'])
|
||||
@@ -117,6 +119,25 @@ function withoutMarkers(node: Node, markers: readonly SourceRange[]): string {
|
||||
return text + node.text.slice(cursor - node.startIndex)
|
||||
}
|
||||
|
||||
function sameSpan(a: Node, b: Node): boolean {
|
||||
return a.startIndex === b.startIndex && a.endIndex === b.endIndex && a.type === b.type
|
||||
}
|
||||
|
||||
/**
|
||||
* 在要翻的 root 里,node 后面还紧跟着包着它的式子的别的部分(`.model_dump()`、`[0]`、`or x`):从 node 往上找第一个后面有东西的节点,
|
||||
* 那个东西不是右括号、逗号、分号、冒号(语法之外的注释不算)。
|
||||
*/
|
||||
function continued(node: Node, root: Node): boolean {
|
||||
for (let current: Node | null = node; current && !sameSpan(current, root); current = current.parent) {
|
||||
let next = current.nextSibling
|
||||
while (next?.isExtra)
|
||||
next = next.nextSibling
|
||||
if (next)
|
||||
return !SEPARATORS.has(next.type)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function isComment(token: Token): boolean {
|
||||
return token.text === '' && !token.group
|
||||
}
|
||||
@@ -245,9 +266,14 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return parts.every((part): part is string | Node => part != null) ? parts : undefined
|
||||
}
|
||||
|
||||
/** 源码里上一个词到 index 之间有空白,下一个词前面就空一格。 */
|
||||
/**
|
||||
* 源码里上一个词到 index 之间有空白,下一个词前面就空一格;跨行写的一对圆括号挤成一行时贴着里面的内容
|
||||
* (`f(\n x\n)` 写成 `f(x)`),方括号、花括号照旧空一格。
|
||||
*/
|
||||
function advance(out: Collector, index: number): void {
|
||||
out.gap ||= /\s/.test(out.document.source.slice(out.cursor, index))
|
||||
const between = out.document.source.slice(out.cursor, index)
|
||||
const hugging = between.includes('\n') && (out.tokens.at(-1)?.raw === '(' || out.document.source[index] === ')')
|
||||
out.gap ||= !hugging && /\s/.test(between)
|
||||
out.cursor = index
|
||||
}
|
||||
|
||||
@@ -344,12 +370,22 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return facts && facts.argumentList.startPosition.row !== facts.argumentList.endPosition.row ? facts : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* node 落在 span 里的那一段翻成一行译文,里面跨几行写的第一个调用排成一行「新建 / 调用 …」,实参摊成表(见 emitCall)。
|
||||
* 一行译文的块、常量 / 变量的值、实参表里每个值各翻各的,各自排自己里面的那个调用。
|
||||
*/
|
||||
function expandedText(node: Node, span: SourceSpan, document: SourceDocument): { text: Inline[], table?: PreviewTable } {
|
||||
const calls: CallSlot = {}
|
||||
const text = joinTokens(tokensIn(node, span, document, calls))
|
||||
return { text, ...(calls.table && { table: calls.table }) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 实参表:参数 | 值,每个实参一行,照源码顺序。按名字传的写名字,按位置传的写「第 N 个」(只数按位置传的),
|
||||
* 展开的写「展开」(*args、...args)、「展开关键字」(**kwargs);值照常翻,里面的调用不再排成表。
|
||||
* 展开的写「展开」(*args、...args)、「展开关键字」(**kwargs)。值照常翻;值里有跨几行写的调用时同样排成一行,
|
||||
* 它的实参表排在值那一格里(PropertyItem.table),一层层往下套。
|
||||
*/
|
||||
function argumentTable(facts: CallFacts, span: SourceSpan, document: SourceDocument): PreviewTable {
|
||||
const value = (node: Node): Inline[] => joinTokens(tokensIn(node, span, document))
|
||||
let position = 0
|
||||
const label = (arg: CallArgument): Inline[] => {
|
||||
switch (arg.kind) {
|
||||
@@ -364,18 +400,26 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
return [hint('展开关键字')]
|
||||
}
|
||||
}
|
||||
return { columns: ARGUMENT_COLUMNS, items: facts.arguments.map(arg => ({ cells: [label(arg), value(arg.value)] })) }
|
||||
const row = (arg: CallArgument): PropertyItem => {
|
||||
const { text, table } = expandedText(arg.value, span, document)
|
||||
return { cells: [label(arg), text], ...(table && { table }) }
|
||||
}
|
||||
return { columns: ARGUMENT_COLUMNS, items: facts.arguments.map(row) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用排成一行「新建 / 调用 被调用的名字 · N 个参数」,实参摊成表记在 calls 里。被调用的名字照常翻、带源码下标,
|
||||
* 最后那个名字新建时按类型、调用时按函数着色,前面几节按变量着色。新建还是调用看写法(CallFacts.construct),
|
||||
* 写法看不出来的问名字的定义:是类(含异常、枚举)就是新建,还没查到、查不到按调用算。
|
||||
* 调用后面还紧跟着同一个式子的别的部分(见 continued)时整行括起来:「(新建 X · 3 个参数).model_dump()」。
|
||||
*/
|
||||
function emitCall(out: Collector, calls: CallSlot, node: Node, facts: CallFacts, span: SourceSpan): void {
|
||||
calls.table = argumentTable(facts, span, out.document)
|
||||
const construct = facts.construct ?? CONSTRUCTED.has(out.document.calleeKind?.(facts))
|
||||
const wrapped = continued(node, out.root)
|
||||
advance(out, node.startIndex)
|
||||
if (wrapped)
|
||||
emit(out, { text: '(', role: 'plain', translated: true, raw: '' })
|
||||
emit(out, { text: construct ? '新建' : '调用', role: 'storage', translated: true, raw: '' })
|
||||
out.gap = true
|
||||
out.cursor = facts.callee.startIndex
|
||||
@@ -387,6 +431,8 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
}
|
||||
out.gap = true
|
||||
emit(out, { text: `· ${facts.arguments.length} 个参数`, role: 'hint', translated: true, raw: '' })
|
||||
if (wrapped)
|
||||
emit(out, { text: ')', role: 'plain', translated: true, raw: '' })
|
||||
out.cursor = node.endIndex
|
||||
}
|
||||
|
||||
@@ -512,11 +558,7 @@ export function createZhTemplateTranslator(table: ZhTable): Translator {
|
||||
const phrases: Phrases = {
|
||||
translated: node => within(node, nodeSpan(node)),
|
||||
within,
|
||||
expanded: (node) => {
|
||||
const calls: CallSlot = {}
|
||||
const text = joinTokens(tokensIn(node, clipped(nodeSpan(node)), document, calls))
|
||||
return { text, ...(calls.table && { table: calls.table }) }
|
||||
},
|
||||
expanded: node => expandedText(node, clipped(nodeSpan(node)), document),
|
||||
entryCount: (literal, size) => {
|
||||
const name = table.mapLiterals[literal.type]
|
||||
return [hint(name ? `${name} ${size} 项` : `${size} 项`)]
|
||||
|
||||
Reference in new issue
Block a user