✨ feat(vscode): Rust 的模块、常量和外部函数拆成卡片

This commit is contained in:
cruldra committed 2026-10-04 20:16:51 +08:00
1 parent e6345e3309
commit 04c4b9f710
13 files changed
+220 -92

No files matched your search

+58 -26
View File
@@ -1,4 +1,4 @@
import type { Access, ClassBlock, ClassFacts, CodeBlock, EnumBlock, FunctionBlock, ImplBlock, ImportBlock, ImportItem, LanguageAdapter, Node, ParameterFacts, TokenRole, TraitBlock } from '../contract'
import type { Access, ClassBlock, ClassFacts, CodeBlock, EnumBlock, FunctionBlock, ImplBlock, ImportBlock, ImportItem, LanguageAdapter, ModuleBlock, Node, ParameterFacts, TokenRole, TraitBlock, VariableBlock } from '../contract'
import * as path from 'node:path'
import { leadingAttributes, visibilityFacts } from '../rules/rustTree'
import { compoundShape, extendHeader, headerSpan, leafBlock, leafShape, named } from './shared'
@@ -15,26 +15,35 @@ export function createRustAdapter(wasmDir: string): LanguageAdapter {
}
}
/** extern 块本身不成一块源码,里面的签名跟所在这一层的别的项走同一条路。 */
function unwrapExtern(item: Node): Node[] {
if (item.type !== 'foreign_mod_item')
return [item]
const body = item.childForFieldName('body')
return body ? named(body).flatMap(unwrapExtern) : []
}
function topBlocks(items: Node[]): CodeBlock[] {
const list = items.flatMap(unwrapExtern)
const blocks: CodeBlock[] = []
let index = 0
while (index < items.length) {
const item = items[index]
while (index < list.length) {
const item = list[index]
if (isDoc(item)) {
const docs = [item]
let next = index + 1
while (next < items.length && isDoc(items[next]) && items[next].startPosition.row <= docs[docs.length - 1].endPosition.row + 1)
docs.push(items[next++])
while (next < items.length && items[next].type === 'attribute_item')
while (next < list.length && isDoc(list[next]) && list[next].startPosition.row <= docs[docs.length - 1].endPosition.row + 1)
docs.push(list[next++])
while (next < list.length && list[next].type === 'attribute_item')
next++
const target = items[next]
if (target && target.type !== 'line_comment' && target.type !== 'block_comment') {
const target = list[next]
if (target && target.type !== 'line_comment' && target.type !== 'block_comment' && target.type !== 'empty_statement') {
blocks.push(withPreamble(itemBlock(target), docs))
index = next + 1
continue
}
}
if (item.type === 'attribute_item') {
if (item.type === 'attribute_item' || item.type === 'empty_statement') {
index++
continue
}
@@ -59,13 +68,50 @@ function itemBlock(item: Node): CodeBlock {
return implBlock(item)
case 'trait_item':
return traitBlock(item)
case 'mod_item':
return moduleBlock(item)
case 'const_item':
return valueBlock(item, 'const')
case 'static_item':
return valueBlock(item, 'variable')
case 'function_item':
case 'function_signature_item':
return functionBlock(item)
default:
return leafBlock('statement', item)
}
}
/** 没有身体的 `mod 名字;` 不拆。有身体的模块,身体和文件顶层同一套规则。 */
function moduleBlock(node: Node): CodeBlock {
const body = node.childForFieldName('body')
if (!body)
return leafBlock('statement', node)
const { access, attributes } = visibilityFacts(node)
return {
kind: 'module',
...compoundShape(node, body.startIndex, named(body), items => topBlocks(items)),
...(attributes && { attributes }),
facts: classFacts(node, access),
}
}
/** const 是常量,static 是变量;名称、类型、值交给现成的常量 / 变量属性表。 */
function valueBlock(node: Node, kind: 'const' | 'variable'): CodeBlock {
const name = node.childForFieldName('name')
if (!name)
return leafBlock('statement', node)
const type = node.childForFieldName('type') ?? undefined
const value = node.childForFieldName('value') ?? undefined
const { access, attributes } = visibilityFacts(node)
return {
kind,
...leafShape(node),
...(attributes && { attributes }),
facts: { access, declarators: [{ name, type, value }] },
}
}
/** 文档注释和写在前面的属性算进头部。文档注释的第一句是说明。 */
function withPreamble(block: CodeBlock, docs: Node[]): CodeBlock {
const [doc] = docs
@@ -79,8 +125,8 @@ function withPreamble(block: CodeBlock, docs: Node[]): CodeBlock {
return next
}
function isDocumentedKind(block: CodeBlock): block is FunctionBlock | ClassBlock | TraitBlock | ImplBlock | EnumBlock {
return block.kind === 'function' || block.kind === 'class' || block.kind === 'trait' || block.kind === 'impl' || block.kind === 'enum'
function isDocumentedKind(block: CodeBlock): block is FunctionBlock | ClassBlock | TraitBlock | ImplBlock | ModuleBlock | EnumBlock | VariableBlock {
return block.kind === 'function' || block.kind === 'class' || block.kind === 'trait' || block.kind === 'impl' || block.kind === 'module' || block.kind === 'enum' || block.kind === 'const' || block.kind === 'variable'
}
function isDoc(node: Node): boolean {
@@ -178,21 +224,7 @@ function traitBlock(node: Node): TraitBlock {
function implBlock(node: Node): ImplBlock {
const { access, attributes } = visibilityFacts(node)
const body = node.childForFieldName('body')
const children: CodeBlock[] = []
let docs: Node[] = []
for (const child of named(body)) {
if (isDoc(child)) {
docs.push(child)
continue
}
if (child.type === 'attribute_item')
continue
if (child.type === 'function_item')
children.push(withPreamble(functionBlock(child), docs))
else if (child.type === 'const_item' || child.type === 'type_item')
children.push(leafBlock('statement', child))
docs = []
}
const children = body ? topBlocks(named(body)) : []
return {
kind: 'impl',
...compoundShape(node, body?.startIndex ?? node.endIndex, [], () => children),