🐛 fix(vscode): Rust 连着写的行注释合成一块注释,和别的语言走同一套拆块

This commit is contained in:
cruldra committed 2026-10-08 07:17:25 +08:00
1 parent 51da724e68
commit 2f610e9cb8
11 files changed
+233 -121

No files matched your search

+32 -56
View File
@@ -1,7 +1,8 @@
import type { Access, ClassBlock, ClassFacts, CodeBlock, EnumBlock, FunctionBlock, ImplBlock, ImportBlock, ImportItem, LanguageAdapter, ModuleBlock, Node, ParameterFacts, TokenRole, TraitBlock, VariableBlock } from '../contract'
import type { Access, ClassFacts, CodeBlock, EnumBlock, FunctionBlock, ImplBlock, ImportBlock, ImportItem, LanguageAdapter, Node, ParameterFacts, TokenRole, TraitBlock } from '../contract'
import type { CommentRules } from './shared'
import * as path from 'node:path'
import { leadingAttributes, visibilityFacts } from '../rules/rustTree'
import { compoundShape, extendHeader, headerSpan, leafBlock, leafShape, named } from './shared'
import { compoundShape, extendHeader, headerSpan, leafBlock, leafShape, listBlocks, named } from './shared'
const STORAGE = new Set(['pub', 'async', 'const', 'static', 'mut', 'let'])
@@ -10,7 +11,7 @@ export function createRustAdapter(wasmDir: string): LanguageAdapter {
id: 'rust',
languageIds: ['rust'],
grammarPath: () => path.join(wasmDir, 'tree-sitter-rust.wasm'),
toBlocks: root => topBlocks(named(root)),
toBlocks: root => topBlocks(named(root), -1),
tokenRole: rustTokenRole,
}
}
@@ -23,37 +24,28 @@ function unwrapExtern(item: Node): Node[] {
return body ? named(body).flatMap(unwrapExtern) : []
}
function topBlocks(items: Node[]): CodeBlock[] {
const list = items.flatMap(unwrapExtern)
const blocks: CodeBlock[] = []
let index = 0
while (index < list.length) {
const item = list[index]
if (isDoc(item)) {
const docs = [item]
let next = index + 1
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 = 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' || item.type === 'empty_statement') {
index++
continue
}
if (item.type === 'line_comment' || item.type === 'block_comment')
blocks.push(leafBlock('comment', item))
else
blocks.push(withPreamble(itemBlock(item), []))
index++
}
return blocks
/**
* `//` 普通注释、讲所在模块的 `//!`、讲下一项的 `///` 各是一种写法,只和同一种连成一块。
* `///` 和 `/**` 开头的块注释是外部文档注释,讲紧跟着的那一项(中间可以隔着 `#[…]`)。
*/
const COMMENTS: CommentRules = {
lineStyle: comment => comment.type === 'line_comment' ? `//${docMarker(comment)}` : undefined,
isDoc: comment => named(comment).some(child => child.type === 'outer_doc_comment_marker'),
documents: new Set(['function', 'class', 'trait', 'impl', 'module', 'enum', 'const', 'variable']),
}
/** 文档注释记号:外部的 `/`(块注释是 `*`)、内部的 `!`;普通注释没有。 */
function docMarker(comment: Node): string {
return named(comment).find(child => child.type === 'outer_doc_comment_marker' || child.type === 'inner_doc_comment_marker')?.text ?? ''
}
/** 一层的各项(文件顶层、模块身体、impl 身体)→ 块;afterRow 同 listBlocks。`#[…]` 和多出来的空 `;` 不出块。 */
function topBlocks(items: Node[], afterRow: number): CodeBlock[] {
return listBlocks(items.flatMap(unwrapExtern), afterRow, (item) => {
if (item.type === 'attribute_item' || item.type === 'empty_statement')
return []
return [withAttributes(itemBlock(item))]
}, COMMENTS)
}
function itemBlock(item: Node): CodeBlock {
@@ -90,7 +82,7 @@ function moduleBlock(node: Node): CodeBlock {
const { access, attributes } = visibilityFacts(node)
return {
kind: 'module',
...compoundShape(node, body.startIndex, named(body), items => topBlocks(items)),
...compoundShape(node, body.startIndex, named(body), topBlocks),
...(attributes && { attributes }),
facts: classFacts(node, access),
}
@@ -112,25 +104,10 @@ function valueBlock(node: Node, kind: 'const' | 'variable'): CodeBlock {
}
}
/** 文档注释和写在前面的属性算进头部。文档注释的第一句是说明。 */
function withPreamble(block: CodeBlock, docs: Node[]): CodeBlock {
const [doc] = docs
const attribute = leadingAttributes(block.node)[0]
let next = block
if (doc && isDocumentedKind(next))
next = { ...extendHeader(next, doc), facts: { ...next.facts, doc } } as typeof next
const from = doc ?? attribute
if (from && next.header.startIndex > from.startIndex)
next = extendHeader(next, from)
return next
}
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 {
return (node.type === 'line_comment' || node.type === 'block_comment') && named(node).some(child => child.type === 'outer_doc_comment_marker')
/** 写在前面的 `#[…]` 算进头部。 */
function withAttributes(block: CodeBlock): CodeBlock {
const [attribute] = leadingAttributes(block.node)
return attribute && block.header.startIndex > attribute.startIndex ? extendHeader(block, attribute) : block
}
function useBlock(node: Node): ImportBlock {
@@ -224,10 +201,9 @@ function traitBlock(node: Node): TraitBlock {
function implBlock(node: Node): ImplBlock {
const { access, attributes } = visibilityFacts(node)
const body = node.childForFieldName('body')
const children = body ? topBlocks(named(body)) : []
return {
kind: 'impl',
...compoundShape(node, body?.startIndex ?? node.endIndex, [], () => children),
...compoundShape(node, body?.startIndex ?? node.endIndex, named(body), topBlocks),
...(attributes && { attributes }),
facts: classFacts(node, access, implType(node)),
}