✨ feat(vscode): 代码阅读的链式调用卡片:语言插件读链、翻译模板排成起点和步骤
契约加 LanguageAdapter.chain、Translator.chain、RuleContext.chain、PropertyRow.chain、TextTranslation.rows;跨行写或实参里有闭包的链排成卡片,连着同名调用合成一步列出每次实参,注释挂在步骤上,闭包收起、子块挂到那一步(BlockView.step);Rust 插件读链、标准库常见方法写中文名;挤成一行时另起一行的 . 贴着前面
This commit is contained in:
1 parent
80eda7c18e
commit
ee595f3da9
16 files changed
+1041
-72
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import type { BlockView } from '../blockView'
|
||||
import type { PreviewText } from '../contract'
|
||||
import type { ChainArgument, ChainFacts, PreviewText } from '../contract'
|
||||
import type { Outline } from '../testSupport'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { blockCodes, toBlockViews } from '../blockView'
|
||||
@@ -440,7 +440,7 @@ describe('rust 函数体拆成子块', () => {
|
||||
{ at: 11, kind: 'statement', kids: [{ at: 12, kind: 'statement' }] },
|
||||
{ at: 14, kind: 'statement', kids: [{ at: 15, kind: 'statement' }] },
|
||||
{ at: 17, kind: 'variable', kids: [{ at: 17, kind: 'return' }] },
|
||||
{ at: 18, kind: 'statement', kids: [{ at: 19, kind: 'statement' }] },
|
||||
{ at: [18, 20], kind: 'statement', kids: [{ at: 19, kind: 'statement' }] },
|
||||
{ at: 21, kind: 'return' },
|
||||
] },
|
||||
])
|
||||
@@ -475,8 +475,12 @@ describe('rust 函数体拆成子块', () => {
|
||||
' 名称: task',
|
||||
' 初始值: 异步',
|
||||
' return 17: 返回 1',
|
||||
' statement 18: items.iter().for_each(|x|',
|
||||
' statement 19: println!("{x}");',
|
||||
' statement 18',
|
||||
' 起点: items',
|
||||
' 步骤: 2 个',
|
||||
' ● 遍历',
|
||||
' ● 逐个执行 | ⟦闭包||x| · 1 句⟧',
|
||||
' statement 19 @1: println!("{x}");',
|
||||
' return 21: 返回 add(1, 2)',
|
||||
])
|
||||
})
|
||||
@@ -851,3 +855,240 @@ describe('rust 注释挂到它讲的项上', () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
/** 链读出的结构写成文字:每步是 [方法名, 泛型实参, 实参, 套在外面的 .await / ?, 注释],收起来的实参写成「种类:句数」。 */
|
||||
function chainSummary(facts: ChainFacts): unknown {
|
||||
const argument = ({ node, closure, deferred }: ChainArgument): string => {
|
||||
if (deferred)
|
||||
return `${deferred.kind}:${deferred.statements}${deferred.match ? `:match ${deferred.match.value.text}·${deferred.match.arms}` : ''}`
|
||||
return closure ? `closure:${node.text}` : node.text
|
||||
}
|
||||
return {
|
||||
start: facts.start.text,
|
||||
steps: facts.steps.map(step => [
|
||||
step.method.text,
|
||||
step.typeArguments?.text ?? '',
|
||||
step.arguments.map(argument),
|
||||
step.after.map(node => node.type),
|
||||
step.comments.map(node => node.text.trim()),
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
describe('rust 链式调用', () => {
|
||||
/** 每一句的式子(let 的值、表达式语句的式子)读出的链,不是链的写 none。 */
|
||||
const chains = (source: string): Promise<unknown[]> => nodeFacts(adapter, 'rust', source, (node) => {
|
||||
const value = node.type === 'let_declaration' ? node.childForFieldName('value') : node.type === 'expression_statement' ? node.firstNamedChild : null
|
||||
if (!value)
|
||||
return undefined
|
||||
const facts = adapter.chain?.(value)
|
||||
return facts ? chainSummary(facts) : 'none'
|
||||
})
|
||||
|
||||
it('两步以上的 .方法(…) 从外往里读:.await、? 归它前面那一步,写在 . 前面的注释归这一步、行尾的归上一步;不是方法调用的式子(连同它的 ?)是起点,取字段、一步的不算链', async () => {
|
||||
const source = [
|
||||
'fn f() {',
|
||||
' let body = client',
|
||||
' // 先发请求',
|
||||
' .get::<Page>(URL, 3) // 第一页',
|
||||
' .send()',
|
||||
' .await?;',
|
||||
' let p = app.path().app_data_dir()?.join("x");',
|
||||
' x.a().b.c();',
|
||||
' one.only();',
|
||||
' foo()?.bar().baz();',
|
||||
'}',
|
||||
].join('\n')
|
||||
expect(await chains(source)).toEqual([
|
||||
{ start: 'client', steps: [
|
||||
['get', '<Page>', ['URL', '3'], [], ['// 先发请求', '// 第一页']],
|
||||
['send', '', [], ['await_expression', 'try_expression'], []],
|
||||
] },
|
||||
{ start: 'app', steps: [
|
||||
['path', '', [], [], []],
|
||||
['app_data_dir', '', [], ['try_expression'], []],
|
||||
['join', '', ['"x"'], [], []],
|
||||
] },
|
||||
'none',
|
||||
'none',
|
||||
{ start: 'foo()?', steps: [['bar', '', [], [], []], ['baz', '', [], [], []]] },
|
||||
])
|
||||
})
|
||||
|
||||
it('实参里的闭包:主体是 {}、或只有一个 match 的闭包和 async 块收起来,记下参数、句数、match 的值和几支;主体是一个式子的闭包只标出是闭包', async () => {
|
||||
const source = [
|
||||
'fn f() {',
|
||||
' rows.iter().filter(|r| r.active).for_each(|r| {',
|
||||
' show(r);',
|
||||
' // 注释不算一句',
|
||||
' log(r);',
|
||||
' });',
|
||||
' builder.build().run(|app, event| match event { A => 1, _ => 2 });',
|
||||
' items.into_iter().for_each(|x| { match x { _ => {} } });',
|
||||
' task.spawn().then(async { 1 });',
|
||||
'}',
|
||||
].join('\n')
|
||||
expect(await chains(source)).toEqual([
|
||||
{ start: 'rows', steps: [['iter', '', [], [], []], ['filter', '', ['closure:|r| r.active'], [], []], ['for_each', '', ['closure:2'], [], []]] },
|
||||
'none',
|
||||
'none',
|
||||
{ start: 'builder', steps: [['build', '', [], [], []], ['run', '', ['closure:1:match event·2'], [], []]] },
|
||||
{ start: 'items', steps: [['into_iter', '', [], [], []], ['for_each', '', ['closure:1:match x·1'], [], []]] },
|
||||
'none',
|
||||
{ start: 'task', steps: [['spawn', '', [], [], []], ['then', '', ['async:1'], [], []]] },
|
||||
])
|
||||
})
|
||||
|
||||
it('带收起来的闭包的链是一整块,头部是整句,子块是各个闭包主体里的各句(闭包主体是 match 时是那一块条件),实参是链的那一步取小链里的闭包', async () => {
|
||||
expect(await tree(readFixture('rustChain.rs'))).toEqual([
|
||||
{ at: 1, kind: 'import' },
|
||||
{ at: 2, kind: 'import' },
|
||||
{ at: 4, kind: 'function', kids: [
|
||||
{ at: 5, kind: 'variable' },
|
||||
{ at: [7, 39], kind: 'statement', kids: [
|
||||
{ at: 21, kind: 'variable' },
|
||||
{ at: 22, kind: 'statement' },
|
||||
{ at: 23, kind: 'return' },
|
||||
{ at: 26, kind: 'statement' },
|
||||
{ at: 36, kind: 'if', kids: [
|
||||
{ at: 37, kind: 'if', kids: [{ at: 37, kind: 'return' }] },
|
||||
{ at: 38, kind: 'if' },
|
||||
] },
|
||||
] },
|
||||
] },
|
||||
{ at: [42, 43], kind: 'function' },
|
||||
{ at: [45, 46], kind: 'function' },
|
||||
])
|
||||
expect(await tree('fn f() {\n app.plugin(\n b.new()\n .setup(|x| {\n go(x);\n }),\n ).run();\n}\n')).toEqual([
|
||||
{ at: 1, kind: 'function', kids: [{ at: [2, 7], kind: 'statement', kids: [{ at: 5, kind: 'statement' }] }] },
|
||||
])
|
||||
})
|
||||
|
||||
it('跨行写的链排成链式调用卡片:起点、步骤两行,一步一个「方法 | 实参」,连着同名的合成一步列出每次的实参,注释挂在步骤上,闭包收起来,子块挂在各自那一步下面', async () => {
|
||||
const [run] = (await views(readFixture('rustChain.rs'))).filter(block => block.kind === 'function')
|
||||
expect(viewLines(run ? [run] : [])).toEqual([
|
||||
'function 4',
|
||||
' 名称: run',
|
||||
' 可见性: 公开',
|
||||
' 返回: ()',
|
||||
' variable 5',
|
||||
' 名称: app_state',
|
||||
' 初始值: AppState::default()',
|
||||
' statement 7',
|
||||
' 起点: tauri::Builder::default()',
|
||||
' 步骤: 8 个',
|
||||
' ● .plugin | 4 次',
|
||||
' 注释: 新窗口链接只走主窗口 on_new_window 一条路',
|
||||
' 实参',
|
||||
' tauri_plugin_opener::Builder::new().open_js_links_on_click(false).build()',
|
||||
' tauri_plugin_dialog::init()',
|
||||
' tauri_plugin_autostart::init(tauri_plugin_autostart::MacosLauncher::LaunchAgent, None)',
|
||||
' tauri_plugin_updater::Builder::new().build()',
|
||||
' ● .setup | ⟦闭包||app| · 3 句⟧',
|
||||
' ● .on_window_event | ⟦闭包||window, event| · 1 句⟧',
|
||||
' ● .manage | app_state',
|
||||
' ● .invoke_handler | tauri::generate_handler![ device_resume, settings_get, web::preview_open ]',
|
||||
' ● .build | tauri::generate_context!()',
|
||||
' ● .expect | "error while building tauri application"',
|
||||
' ● .run | ⟦闭包||app, event| · match event · 2 支⟧',
|
||||
' variable 21 @1',
|
||||
' 名称: store',
|
||||
' 初始值: app.path().app_data_dir()?.join("tunnels.json")',
|
||||
' statement 22 @1: app.manage(Arc::new(Tunnels::new(store)));',
|
||||
' return 23 @1: 返回 Ok(())',
|
||||
' statement 26 @2: window.hide();',
|
||||
' if 36 @7: 匹配 event',
|
||||
' if 37: RunEvent::Exit',
|
||||
' return 37: 返回 app.state::<Arc<Host>>().shutdown()',
|
||||
' if 38: _',
|
||||
])
|
||||
})
|
||||
|
||||
it('写在一行里、实参里有闭包的链也排;变量的初始值是起点,链接在下面;标准库常见方法写中文名;.await、? 照常翻、跟在那一步后面;返回值同样排;单行又没闭包的照旧一行', async () => {
|
||||
const source = [
|
||||
'async fn load(client: Client, rows: Vec<Row>) -> Result<Vec<u8>, Error> {',
|
||||
' let names: Vec<String> = rows.iter().filter(|r| r.active).map(|r| r.name.clone()).collect();',
|
||||
' let body = client',
|
||||
' .get(URL)',
|
||||
' .send()',
|
||||
' .await?;',
|
||||
' let p = app.path().app_data_dir()?.join("x");',
|
||||
' body.bytes()',
|
||||
' .await',
|
||||
' .map(|b| b.to_vec())',
|
||||
'}',
|
||||
].join('\n')
|
||||
expect(viewLines((await views(source))[0]?.children ?? [])).toEqual([
|
||||
'variable 2',
|
||||
' 名称: names',
|
||||
' 类型: Vec<String>',
|
||||
' 初始值: rows',
|
||||
' ● 遍历',
|
||||
' ● 筛选 | |r| r.active',
|
||||
' ● 映射 | |r| r.name.clone()',
|
||||
' ● 收集',
|
||||
'variable 3',
|
||||
' 名称: body',
|
||||
' 初始值: client',
|
||||
' ● .get | URL',
|
||||
' ● .send | 等待?',
|
||||
'variable 7',
|
||||
' 名称: p',
|
||||
' 初始值: app.path().app_data_dir()?.join("x")',
|
||||
'return 8: 返回',
|
||||
' 起点: body',
|
||||
' 步骤: 2 个',
|
||||
' ● .bytes | 等待',
|
||||
' ● 映射 | |b| b.to_vec()',
|
||||
])
|
||||
})
|
||||
|
||||
it('实参本身是跨行写的链时缩进成一条小链;连着几次同名调用、每次几个实参时按位置分列', async () => {
|
||||
const source = [
|
||||
'fn f() {',
|
||||
' tauri::Builder::default()',
|
||||
' .plugin(',
|
||||
' opener::Builder::new()',
|
||||
' .open(false)',
|
||||
' .build(),',
|
||||
' )',
|
||||
' .manage(state);',
|
||||
' Router::new()',
|
||||
' .route("/a", get(list))',
|
||||
' .route("/b", post(create))',
|
||||
' .with_state(state);',
|
||||
'}',
|
||||
].join('\n')
|
||||
expect(viewLines((await views(source))[0]?.children ?? [])).toEqual([
|
||||
'statement 2',
|
||||
' 起点: tauri::Builder::default()',
|
||||
' 步骤: 2 个',
|
||||
' ● .plugin',
|
||||
' ○ opener::Builder::new()',
|
||||
' ○ .open(false)',
|
||||
' ○ .build()',
|
||||
' ● .manage | state',
|
||||
'statement 9',
|
||||
' 起点: Router::new()',
|
||||
' 步骤: 2 个',
|
||||
' ● .route | 2 次',
|
||||
' 第 1 个 | 第 2 个',
|
||||
' "/a" | get(list)',
|
||||
' "/b" | post(create)',
|
||||
' ● .with_state | state',
|
||||
])
|
||||
})
|
||||
|
||||
it('方法的中文名悬停看原名,⌘ 点击跳到方法名;照抄的实参、起点、方法名都带自己的下标', async () => {
|
||||
const source = 'fn f() {\n let n = rows.iter().map(|r| r.id).collect();\n}\n'
|
||||
const [variable] = (await views(source))[0]?.children ?? []
|
||||
const row = propertyRow(variable, '初始值')
|
||||
const [iter, map] = row.chain ?? []
|
||||
const label = iter?.name[0]
|
||||
expect(label && 'role' in label ? { text: label.text, refers: label.refers, tooltip: label.tooltip } : undefined)
|
||||
.toEqual({ text: '遍历', refers: { startIndex: source.indexOf('iter'), endIndex: source.indexOf('iter') + 4 }, tooltip: '.iter' })
|
||||
expect(wordsOf(row.value, 'variable')).toEqual([['rows', source.indexOf('rows')]])
|
||||
expect(wordsOf(map?.value, 'variable')).toEqual([['r', source.indexOf('|r|') + 1], ['r', source.indexOf('r.id')], ['id', source.indexOf('r.id') + 2]])
|
||||
expect(map?.range).toEqual({ startIndex: source.indexOf('.map'), endIndex: source.indexOf('.collect') })
|
||||
})
|
||||
})
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Access, AttributeFacts, AttributeItem, BlockShape, CallFacts, CfgPredicate, ClassFacts, CodeBlock, CollectionFacts, CommentBlock, EnumBlock, FunctionBlock, ImplBlock, ImportBlock, ImportItem, LanguageAdapter, Node, ParameterFacts, SourceSpan, TextKind, TokenRole, TraitBlock } from '../contract'
|
||||
import type { Access, AttributeFacts, AttributeItem, BlockShape, CallFacts, CfgPredicate, ChainArgument, ChainFacts, ChainStepFacts, ClassFacts, CodeBlock, CollectionFacts, CommentBlock, DeferredFacts, EnumBlock, FunctionBlock, ImplBlock, ImportBlock, ImportItem, LanguageAdapter, Node, ParameterFacts, SourceSpan, TextKind, TokenRole, TraitBlock } from '../contract'
|
||||
import type { CommentRules, ListBlocks } from './shared'
|
||||
import * as path from 'node:path'
|
||||
import { leadingAttributes, visibilityFacts } from '../rules/rustTree'
|
||||
import { attachComments, children, compoundShape, extendHeader, headerSpan, insideOf, isCommentNode, leafBlock, leafShape, listBlocks, named, syntaxChildren } from './shared'
|
||||
import { attachComments, children, compoundShape, extendHeader, headerSpan, insideOf, isCommentNode, leafBlock, leafShape, listBlocks, named, nodeSpan, syntaxChildren } from './shared'
|
||||
|
||||
const STORAGE = new Set(['pub', 'async', 'const', 'static', 'mut', 'let'])
|
||||
|
||||
@@ -17,9 +17,146 @@ export function createRustAdapter(wasmDir: string): LanguageAdapter {
|
||||
freshDoc,
|
||||
call: rustCall,
|
||||
collection: rustCollection,
|
||||
chain: rustChain,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 两步以上的链式调用 `a.b(…).c(…)`:从最外面往里剥,每层 `.方法(…)` 是一步,套在调用外面的 `.await`、`?` 归它里面那一步,
|
||||
* 剥到不是方法调用的式子就是起点(连同套在它外面的 `.await`、`?`)。宏调用、取字段(`x.a().b.c()` 的 `.b`)不算一步,链从那里断开。
|
||||
*/
|
||||
function rustChain(node: Node): ChainFacts | undefined {
|
||||
const steps: ChainStepFacts[] = []
|
||||
let current: Node | null = node
|
||||
let after: Node[] = []
|
||||
while (current) {
|
||||
if (current.type === 'await_expression' || current.type === 'try_expression') {
|
||||
after = [current, ...after]
|
||||
current = syntaxChildren(current)[0] ?? null
|
||||
continue
|
||||
}
|
||||
const step = methodStep(current, after)
|
||||
if (!step)
|
||||
break
|
||||
steps.unshift(step.facts)
|
||||
after = []
|
||||
current = step.receiver
|
||||
}
|
||||
const start = after[after.length - 1] ?? current
|
||||
if (steps.length < 2 || !start)
|
||||
return undefined
|
||||
return { start, steps: withTrailingComments(steps, start) }
|
||||
}
|
||||
|
||||
/** 一层 `.方法(实参)`(方法名后面可以带 `::<…>`):这一步和它的接收者;不是方法调用时没有。after 是套在它外面的 `.await`、`?`,从里往外。 */
|
||||
function methodStep(call: Node, after: Node[]): { facts: ChainStepFacts, receiver: Node } | undefined {
|
||||
const fn = call.type === 'call_expression' ? call.childForFieldName('function') : null
|
||||
const argumentList = call.childForFieldName('arguments')
|
||||
const generic = fn?.type === 'generic_function' ? fn : undefined
|
||||
const target = generic ? generic.childForFieldName('function') : fn
|
||||
const method = target?.type === 'field_expression' ? target.childForFieldName('field') : null
|
||||
const receiver = target?.childForFieldName('value')
|
||||
const dot = target && children(target).find(child => child.type === '.')
|
||||
if (!argumentList || argumentList.type !== 'arguments' || method?.type !== 'field_identifier' || !receiver || !dot)
|
||||
return undefined
|
||||
const typeArguments = generic?.childForFieldName('type_arguments') ?? undefined
|
||||
return {
|
||||
receiver,
|
||||
facts: {
|
||||
dot,
|
||||
method,
|
||||
...(typeArguments && { typeArguments }),
|
||||
call,
|
||||
argumentList,
|
||||
arguments: syntaxChildren(argumentList).filter(arg => arg.type !== 'attribute_item').map(chainArgument),
|
||||
after,
|
||||
comments: children(target).filter(child => isCommentNode(child) && child.endIndex <= dot.startIndex),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function chainArgument(node: Node): ChainArgument {
|
||||
const deferred = deferredFacts(node)
|
||||
return { node, closure: node.type === 'closure_expression' || node.type === 'async_block', ...(deferred && { deferred }) }
|
||||
}
|
||||
|
||||
/** 收起来的闭包、async 块(见 [DeferredFacts]):主体是 `{}`、或闭包主体直接是 match;`{}` 里只有一个 match 也算主体只有一个 match。 */
|
||||
function deferredFacts(node: Node): DeferredFacts | undefined {
|
||||
const parameters = node.type === 'closure_expression' ? node.childForFieldName('parameters') ?? undefined : undefined
|
||||
const kind = node.type === 'closure_expression' ? 'closure' : 'async'
|
||||
const body = deferredBody(node) ?? (node.type === 'closure_expression' ? node.childForFieldName('body') : null)
|
||||
if (!body || (body.type !== 'block' && body.type !== 'match_expression'))
|
||||
return undefined
|
||||
const items = body.type === 'block' ? statementItems(body) : [body]
|
||||
const [only] = items
|
||||
const match = items.length === 1 && only ? matchOf(only) : undefined
|
||||
return {
|
||||
kind,
|
||||
...(parameters && { parameters }),
|
||||
statements: items.length,
|
||||
...(match && { match: { node: match, value: match.childForFieldName('value') ?? match, arms: named(match.childForFieldName('body')).filter(arm => arm.type === 'match_arm').length } }),
|
||||
}
|
||||
}
|
||||
|
||||
/** 一句就是一个 match(带不带分号)时是那个 match。 */
|
||||
function matchOf(item: Node): Node | undefined {
|
||||
const expression = item.type === 'expression_statement' ? syntaxChildren(item)[0] : item
|
||||
return expression?.type === 'match_expression' ? expression : undefined
|
||||
}
|
||||
|
||||
/** `{}` 里写的各句:注释、`#[…]`、多出来的空 `;` 不算。 */
|
||||
function statementItems(block: Node): Node[] {
|
||||
return blockItems(block).filter(item => !isCommentNode(item) && item.type !== 'attribute_item' && item.type !== 'empty_statement')
|
||||
}
|
||||
|
||||
/** 写在 `.` 前面、和上一步的结尾同一行的注释是上一步的行尾注释,归上一步。 */
|
||||
function withTrailingComments(steps: ChainStepFacts[], start: Node): ChainStepFacts[] {
|
||||
return steps.map((step, i) => {
|
||||
const next = steps[i + 1]
|
||||
const trailing = next ? next.comments.filter(comment => comment.startPosition.row === stepEnd(step).endPosition.row) : []
|
||||
const before = i > 0 ? stepEnd(steps[i - 1]).endPosition.row : start.endPosition.row
|
||||
const own = step.comments.filter(comment => i === 0 || comment.startPosition.row !== before)
|
||||
return { ...step, comments: [...own, ...trailing] }
|
||||
})
|
||||
}
|
||||
|
||||
/** 一步的结尾:调用,套着 `.await`、`?` 时是最外面那层。 */
|
||||
function stepEnd(step: ChainStepFacts): Node {
|
||||
return step.after[step.after.length - 1] ?? step.call
|
||||
}
|
||||
|
||||
/**
|
||||
* 链里收起来的闭包、async 块的主体,照源码顺序:各步的实参,只有一个实参、实参本身是链的那一步取那条小链各步的实参
|
||||
* (翻译插件把它排成小链,见 [链的一步](vscode/src/codeReader/contract.ts#ChainStep) 的 lines)。
|
||||
*/
|
||||
function deferredBodies(chain: ChainFacts): Node[] {
|
||||
return chain.steps.flatMap((step) => {
|
||||
const [only] = step.arguments
|
||||
const inner = step.arguments.length === 1 && only ? rustChain(only.node) : undefined
|
||||
const args = inner ? inner.steps.flatMap(item => item.arguments) : step.arguments
|
||||
return args.flatMap(arg => arg.deferred ? [arg.node] : [])
|
||||
})
|
||||
}
|
||||
|
||||
/** 收起来的闭包、async 块主体里的各句 → 块:主体是 `{}` 时同函数体,闭包主体直接是 match 时是那一块条件。 */
|
||||
function deferredBlocks(node: Node): CodeBlock[] {
|
||||
const body = deferredBody(node)
|
||||
if (body)
|
||||
return statements(blockItems(body), children(body).find(child => child.type === '{')?.startPosition.row ?? body.startPosition.row, true)
|
||||
const match = node.childForFieldName('body')
|
||||
return match?.type === 'match_expression' ? [matchBlock(match, match, true)] : []
|
||||
}
|
||||
|
||||
/**
|
||||
* 值是带收起来的闭包、async 块的链式调用时块的形状:头部是整句(链式调用卡片讲整条链),子块是那几个闭包、async 块主体里的各句,
|
||||
* 宿主按位置挂到各自那一步下面;别的值没有。
|
||||
*/
|
||||
function chainShape(outer: Node, value: Node | null): BlockShape | undefined {
|
||||
const chain = value ? rustChain(value) : undefined
|
||||
const bodies = chain ? deferredBodies(chain) : []
|
||||
return bodies.length > 0 ? { node: outer, header: nodeSpan(outer), children: bodies.flatMap(deferredBlocks) } : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 `f(a)`、`obj.method(a)`、`Type::new(a)`、`parse::<u8>(s)` 的结构,实参都是按位置传的;
|
||||
* 元组结构体、变体(`Some(x)`)写法和调用一样,不给 construct,翻译插件问名字的定义是哪种块。
|
||||
@@ -462,6 +599,7 @@ function statementBlocks(item: Node, tail: boolean): CodeBlock[] {
|
||||
/**
|
||||
* 当一句写的式子 → 块:outer 是整句(表达式语句,或一层最后那个不带分号的式子本身)。
|
||||
* 条件、match、循环、`{}` 照控制流拆;是这一层的值(tail)时是 return;别的是 statement,带着闭包、async 块时子块来自它(见 [callbackBody])。
|
||||
* 式子(return 的值)是带收起来的闭包的链式调用时,子块来自链里那几个闭包(见 [chainShape])。
|
||||
*/
|
||||
function expressionBlocks(outer: Node, expression: Node, tail: boolean): CodeBlock[] {
|
||||
switch (expression.type) {
|
||||
@@ -483,9 +621,11 @@ function expressionBlocks(outer: Node, expression: Node, tail: boolean): CodeBlo
|
||||
case 'async_block':
|
||||
return [bodyBlock('statement', outer, deferredBody(expression), true)]
|
||||
case 'return_expression':
|
||||
return [leafBlock('return', outer)]
|
||||
return [{ kind: 'return', ...(chainShape(outer, syntaxChildren(expression)[0] ?? null) ?? leafShape(outer)) }]
|
||||
default:
|
||||
return [tail ? leafBlock('return', outer) : bodyBlock('statement', outer, callbackBody(expression), true)]
|
||||
if (tail)
|
||||
return [{ kind: 'return', ...(chainShape(outer, expression) ?? leafShape(outer)) }]
|
||||
return [{ kind: 'statement', ...(chainShape(outer, expression) ?? bodyShape(outer, callbackBody(expression), true)) }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +658,8 @@ function armBlock(arm: Node, tail: boolean): CodeBlock {
|
||||
|
||||
/**
|
||||
* `let` → variable,模式是名字;值是主体写成 `{}` 的闭包时是 function,同 TypeScript 把箭头函数赋给变量。
|
||||
* `let … else` 的 else 是它的子块;值带着闭包、async 块时子块来自它(见 [callbackBody])。
|
||||
* `let … else` 的 else 是它的子块;值是带收起来的闭包的链式调用时子块来自那几个闭包(见 [chainShape]),
|
||||
* 别的值带着闭包、async 块时子块来自它(见 [callbackBody])。
|
||||
*/
|
||||
function letBlock(node: Node): CodeBlock {
|
||||
const pattern = node.childForFieldName('pattern')
|
||||
@@ -530,7 +671,7 @@ function letBlock(node: Node): CodeBlock {
|
||||
const alternative = node.childForFieldName('alternative')
|
||||
return {
|
||||
kind: 'variable',
|
||||
...(alternative ? letElseShape(node, alternative) : bodyShape(node, value && callbackBody(value), true)),
|
||||
...(alternative ? letElseShape(node, alternative) : chainShape(node, value) ?? bodyShape(node, value && callbackBody(value), true)),
|
||||
facts: { declarators: [{ name: pattern, type: node.childForFieldName('type') ?? undefined, value: value ?? undefined }] },
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user