🐛 fix(vscode): Rust 连着写的行注释合成一块注释,和别的语言走同一套拆块
This commit is contained in:
1 parent
51da724e68
commit
2f610e9cb8
11 files changed
+233
-121
No files matched your search
@@ -24,6 +24,20 @@ export function nodeSpan(node: Node): SourceSpan {
|
||||
}
|
||||
}
|
||||
|
||||
const COMMENT_TYPES = new Set(['comment', 'line_comment', 'block_comment'])
|
||||
|
||||
/** 注释节点:多数语法包叫 comment,Rust 分行注释 line_comment 和块注释 block_comment。 */
|
||||
export function isCommentNode(node: Node): boolean {
|
||||
return COMMENT_TYPES.has(node.type)
|
||||
}
|
||||
|
||||
/** 注释节点的范围,不算末尾的换行:Rust 的 `///`、`//!` 把行尾换行也算进节点,不去掉就多占下一行。 */
|
||||
function commentNodeSpan(comment: Node): SourceSpan {
|
||||
const span = nodeSpan(comment)
|
||||
const newline = /\r?\n$/.exec(comment.text)?.[0].length ?? 0
|
||||
return newline ? { ...span, endIndex: span.endIndex - newline, endRow: span.endRow - 1 } : span
|
||||
}
|
||||
|
||||
export function leafShape(node: Node): BlockShape {
|
||||
return { node, header: nodeSpan(node), children: [] }
|
||||
}
|
||||
@@ -40,7 +54,7 @@ export function extendHeader<B extends CodeBlock>(block: B, from: Node): B {
|
||||
function lastLeafBefore(node: Node, limit: number): Node | undefined {
|
||||
for (let i = node.childCount - 1; i >= 0; i--) {
|
||||
const child = node.child(i)
|
||||
if (!child || child.startIndex >= limit || child.type === 'comment' || child.startIndex === child.endIndex)
|
||||
if (!child || child.startIndex >= limit || isCommentNode(child) || child.startIndex === child.endIndex)
|
||||
continue
|
||||
if (child.childCount === 0)
|
||||
return child
|
||||
@@ -68,7 +82,7 @@ export function headerSpan(outer: Node, bodyStart: number): SourceSpan {
|
||||
function commentsIn(node: Node, from: number, to: number): Node[] {
|
||||
if (node.endIndex <= from || node.startIndex >= to)
|
||||
return []
|
||||
if (node.type === 'comment')
|
||||
if (isCommentNode(node))
|
||||
return node.startIndex >= from && node.endIndex <= to ? [node] : []
|
||||
return children(node).flatMap(child => commentsIn(child, from, to))
|
||||
}
|
||||
@@ -87,21 +101,33 @@ export function compoundShape(outer: Node, bodyStart: number, items: Node[], lis
|
||||
|
||||
/** 语言怎么认注释。 */
|
||||
export interface CommentRules {
|
||||
/** 行注释:相邻几行合成一块。 */
|
||||
isLine: (comment: Node) => boolean
|
||||
/** 文档注释(JSDoc):紧贴在函数、类块前面时是它的说明,见 FunctionFacts.doc、ClassFacts.doc。 */
|
||||
/**
|
||||
* 行注释的写法(`//`、`#`),块注释没有:相邻行同一写法的行注释合成一块。
|
||||
* 写法不同的不合,例如 Rust 的 `//`、讲下一项的 `///`、讲所在模块的 `//!` 各是一种。
|
||||
*/
|
||||
lineStyle: (comment: Node) => string | undefined
|
||||
/**
|
||||
* 文档注释(JSDoc、Rust 的 `///`):紧贴在 documents 这几种块前面时是它的说明,见 FunctionFacts.doc、ClassFacts.doc;
|
||||
* 连着几行的行文档注释合成一段,第一条注释节点是说明。
|
||||
*/
|
||||
isDoc: (comment: Node) => boolean
|
||||
/** 文档注释能当哪几种块的说明。 */
|
||||
documents: ReadonlySet<DocumentedBlock['kind']>
|
||||
}
|
||||
|
||||
/** 函数、类块带上说明:说明进 facts.doc,头部往上延到它开头。 */
|
||||
/** 块带上说明:说明进 facts.doc,头部往上延到它开头。 */
|
||||
function documented<B extends DocumentedBlock>(block: B, doc: Node): B {
|
||||
return { ...extendHeader(block, doc), facts: { ...block.facts, doc } }
|
||||
}
|
||||
|
||||
function documentable(block: CodeBlock, kinds: ReadonlySet<string>): block is DocumentedBlock {
|
||||
return kinds.has(block.kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* 一串语句 → 块。注释单独成块,相邻行的行注释合成一块;
|
||||
* 一串语句 → 块。注释单独成块,相邻行同一写法的行注释合成一块(见 CommentRules.lineStyle);
|
||||
* 和前一条语句(或头部)同一行的注释是行尾注释,跟着那一行显示,不单独成块。
|
||||
* 文档注释和下一条语句之间只有空白、下一条语句是函数或类块时,它是这个函数、类的说明,不单独成块;
|
||||
* 文档注释(连着几行的算一段)和下一条语句之间只有空白、下一条语句是 CommentRules.documents 里的块时,它是这一块的说明,不单独成块;
|
||||
* 中间隔着的装饰器(不出块的语句)不算隔开。
|
||||
* afterRow 是这串语句之前的最后一行(头部结尾行),模块顶层传 -1;
|
||||
* toBlocks 可以返回空数组:这条语句不出块(装饰器、并进上一块的属性 docstring),和它同一行的注释照样算行尾注释。
|
||||
@@ -114,46 +140,53 @@ export function listBlocks(
|
||||
): CodeBlock[] {
|
||||
const out: CodeBlock[] = []
|
||||
let lastRow = afterRow
|
||||
let lineRun: CodeBlock | undefined
|
||||
let doc: Node | undefined
|
||||
let run: { block: CodeBlock, style: string } | undefined
|
||||
/** 还没定归属的文档注释:下一条语句能带说明就是它的说明,不然单独成块。 */
|
||||
let doc: CodeBlock | undefined
|
||||
const flushDoc = (): void => {
|
||||
if (doc)
|
||||
out.push(leafBlock('comment', doc))
|
||||
out.push(doc)
|
||||
doc = undefined
|
||||
}
|
||||
/** 接在 run 下一行、写法相同的行注释。 */
|
||||
const continues = (comment: Node): boolean =>
|
||||
!!run && comments.lineStyle(comment) === run.style && comment.startPosition.row === run.block.header.endRow + 1
|
||||
items.forEach((item, i) => {
|
||||
if (item.type === 'comment') {
|
||||
if (item.startPosition.row === lastRow)
|
||||
if (isCommentNode(item)) {
|
||||
const span = commentNodeSpan(item)
|
||||
if (span.startRow === lastRow)
|
||||
return
|
||||
flushDoc()
|
||||
const line = comments.isLine(item)
|
||||
const next = items[i + 1]
|
||||
if (line && lineRun && item.startPosition.row === lineRun.header.endRow + 1) {
|
||||
lineRun.header = { ...lineRun.header, endIndex: item.endIndex, endRow: item.endPosition.row }
|
||||
}
|
||||
else if (comments.isDoc(item) && next && next.type !== 'comment' && sameNode(item.nextSibling, next)) {
|
||||
doc = item
|
||||
lineRun = undefined
|
||||
if (run && continues(item)) {
|
||||
run.block.header = { ...run.block.header, endIndex: span.endIndex, endRow: span.endRow }
|
||||
}
|
||||
else {
|
||||
const block = leafBlock('comment', item)
|
||||
out.push(block)
|
||||
lineRun = line ? block : undefined
|
||||
}
|
||||
}
|
||||
else {
|
||||
const blocks = toBlocks(item)
|
||||
const [head] = blocks
|
||||
if ((head?.kind === 'function' || head?.kind === 'class' || head?.kind === 'enum') && doc) {
|
||||
blocks[0] = documented(head, doc)
|
||||
doc = undefined
|
||||
}
|
||||
else if (head) {
|
||||
flushDoc()
|
||||
const block: CodeBlock = { kind: 'comment', node: item, header: span, children: [] }
|
||||
const style = comments.lineStyle(item)
|
||||
run = style === undefined ? undefined : { block, style }
|
||||
if (comments.isDoc(item))
|
||||
doc = block
|
||||
else
|
||||
out.push(block)
|
||||
}
|
||||
out.push(...blocks)
|
||||
lineRun = undefined
|
||||
// 文档注释后面紧跟的得是下一条语句或它自己的下一行,隔着别的注释就只是一块注释
|
||||
const next = items[i + 1]
|
||||
if (!next || !sameNode(item.nextSibling, next) || (isCommentNode(next) && !continues(next)))
|
||||
flushDoc()
|
||||
lastRow = span.endRow
|
||||
return
|
||||
}
|
||||
const blocks = toBlocks(item)
|
||||
const [head] = blocks
|
||||
if (head && doc && documentable(head, comments.documents)) {
|
||||
blocks[0] = documented(head, doc.node)
|
||||
doc = undefined
|
||||
}
|
||||
else if (head) {
|
||||
flushDoc()
|
||||
}
|
||||
out.push(...blocks)
|
||||
run = undefined
|
||||
lastRow = item.endPosition.row
|
||||
})
|
||||
flushDoc()
|
||||
@@ -194,10 +227,10 @@ function leadingComments(children: readonly CodeBlock[], child: CodeBlock): Code
|
||||
|
||||
/** 注释块里的各条注释(几条行注释连成的一块是好几个节点);Python 当注释用的字符串是那个字符串。 */
|
||||
function commentNodes(block: CodeBlock): Node[] {
|
||||
if (block.node.type !== 'comment')
|
||||
if (!isCommentNode(block.node))
|
||||
return syntaxChildren(block.node).slice(0, 1)
|
||||
const nodes: Node[] = []
|
||||
for (let node: Node | null = block.node; node?.type === 'comment' && node.endIndex <= block.header.endIndex; node = node.nextSibling)
|
||||
for (let node: Node | null = block.node; node && isCommentNode(node) && node.startIndex < block.header.endIndex; node = node.nextSibling)
|
||||
nodes.push(node)
|
||||
return nodes
|
||||
}
|
||||
@@ -238,7 +271,7 @@ function fieldDocs(children: readonly CodeBlock[], block: CodeBlock): Node[] {
|
||||
const doc = ownDoc(block)
|
||||
return [
|
||||
...leadingComments(children, block).flatMap(commentNodes),
|
||||
...(trailing?.type === 'comment' && trailing.startPosition.row === block.node.endPosition.row ? [trailing] : []),
|
||||
...(trailing && isCommentNode(trailing) && trailing.startPosition.row === block.node.endPosition.row ? [trailing] : []),
|
||||
...(doc ? [doc] : []),
|
||||
]
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user