✨ feat(vscode): 代码阅读函数块默认折叠,函数说明进属性表「说明」行

This commit is contained in:
cruldra committed 2026-09-25 13:09:53 +08:00
1 parent 62f48fe67c
commit 9b32d4b5a8
17 files changed
+489 -100

No files matched your search

+44 -7
View File
@@ -1,4 +1,4 @@
import type { BlockShape, CodeBlock, Node, SourceSpan, TextKind } from '../contract'
import type { BlockShape, CodeBlock, FunctionBlock, Node, SourceSpan, TextKind } from '../contract'
/** 语言插件共用的建树工具:取子节点、算头部、把一串语句切成块。 */
@@ -85,9 +85,24 @@ export function compoundShape(outer: Node, bodyStart: number, items: Node[], lis
return { node: outer, header, children: list([...between, ...items], header.endRow) }
}
/** 语言怎么认注释。 */
export interface CommentRules {
/** 行注释:相邻几行合成一块。 */
isLine: (comment: Node) => boolean
/** 文档注释(JSDoc):紧贴在函数块前面时是这个函数的说明,见 FunctionFacts.doc。 */
isDoc: (comment: Node) => boolean
}
/** 函数块带上说明:说明进 facts.doc,头部往上延到它开头。 */
function documented(block: FunctionBlock, doc: Node): FunctionBlock {
return { ...extendHeader(block, doc), facts: { ...block.facts, doc } }
}
/**
* 一串语句 → 块。注释单独成块,相邻行的行注释合成一块;
* 和前一条语句(或头部)同一行的注释是行尾注释,跟着那一行显示,不单独成块。
* 文档注释和下一条语句之间只有空白、下一条语句是函数块时,它是这个函数的说明,不单独成块;
* 中间隔着的装饰器(不出块的语句)不算隔开。
* afterRow 是这串语句之前的最后一行(头部结尾行),模块顶层传 -1;
* toBlocks 的 first 表示它是这串里第一条非注释语句(Python 认 docstring 用)。
*/
@@ -95,20 +110,32 @@ export function listBlocks(
items: Node[],
afterRow: number,
toBlocks: (item: Node, first: boolean) => CodeBlock[],
isLineComment: (comment: Node) => boolean,
comments: CommentRules,
): CodeBlock[] {
const out: CodeBlock[] = []
let lastRow = afterRow
let lineRun: CodeBlock | undefined
let doc: Node | undefined
let first = true
for (const item of items) {
const flushDoc = (): void => {
if (doc)
out.push(leafBlock('comment', doc))
doc = undefined
}
items.forEach((item, i) => {
if (item.type === 'comment') {
if (item.startPosition.row === lastRow)
continue
const line = isLineComment(item)
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
}
else {
const block = leafBlock('comment', item)
out.push(block)
@@ -116,12 +143,22 @@ export function listBlocks(
}
}
else {
out.push(...toBlocks(item, first))
const blocks = toBlocks(item, first)
const [head] = blocks
if (head?.kind === 'function' && doc) {
blocks[0] = documented(head, doc)
doc = undefined
}
else if (head) {
flushDoc()
}
out.push(...blocks)
first = false
lineRun = undefined
}
lastRow = item.endPosition.row
}
})
flushDoc()
return out
}