✨ feat(vscode): 代码阅读的解析与模板直译核心
This commit is contained in:
1 parent
af56985d9a
commit
4d34b8c715
24 files changed
+2294
No files matched your search
@@ -0,0 +1,122 @@
|
||||
import type { BlockKind, CodeBlock, Node, SourceSpan } from '../contract'
|
||||
|
||||
/** 语言插件共用的建树工具:取子节点、算头部、把一串语句切成块。 */
|
||||
|
||||
export function named(node: Node | null | undefined): Node[] {
|
||||
return node ? node.namedChildren.filter((child): child is Node => child !== null) : []
|
||||
}
|
||||
|
||||
export function children(node: Node): Node[] {
|
||||
return node.children.filter((child): child is Node => child !== null)
|
||||
}
|
||||
|
||||
export function nodeSpan(node: Node): SourceSpan {
|
||||
return {
|
||||
startIndex: node.startIndex,
|
||||
endIndex: node.endIndex,
|
||||
startRow: node.startPosition.row,
|
||||
endRow: node.endPosition.row,
|
||||
}
|
||||
}
|
||||
|
||||
export function leafBlock(kind: BlockKind, node: Node): CodeBlock {
|
||||
return { kind, node, header: nodeSpan(node), children: [] }
|
||||
}
|
||||
|
||||
/** 头部往前扩到 from(装饰器)。 */
|
||||
export function extendHeader(block: CodeBlock, from: Node): CodeBlock {
|
||||
return { ...block, header: { ...block.header, startIndex: from.startIndex, startRow: from.startPosition.row } }
|
||||
}
|
||||
|
||||
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)
|
||||
continue
|
||||
if (child.childCount === 0)
|
||||
return child
|
||||
const found = lastLeafBefore(child, limit)
|
||||
if (found)
|
||||
return found
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/** 复合语句的头部:从 outer 开头到主体开始之前最后一个非注释的词为止。 */
|
||||
export function headerSpan(outer: Node, bodyStart: number): SourceSpan {
|
||||
const last = lastLeafBefore(outer, bodyStart)
|
||||
if (!last)
|
||||
return nodeSpan(outer)
|
||||
return {
|
||||
startIndex: outer.startIndex,
|
||||
endIndex: last.endIndex,
|
||||
startRow: outer.startPosition.row,
|
||||
endRow: last.endPosition.row,
|
||||
}
|
||||
}
|
||||
|
||||
/** node 里完全落在 [from, to) 之间的注释。 */
|
||||
function commentsIn(node: Node, from: number, to: number): Node[] {
|
||||
if (node.endIndex <= from || node.startIndex >= to)
|
||||
return []
|
||||
if (node.type === 'comment')
|
||||
return node.startIndex >= from && node.endIndex <= to ? [node] : []
|
||||
return children(node).flatMap(child => commentsIn(child, from, to))
|
||||
}
|
||||
|
||||
export type ListBlocks = (items: Node[], afterRow: number) => CodeBlock[]
|
||||
|
||||
/**
|
||||
* 复合语句块:头部到 bodyStart 之前,子块来自 items。
|
||||
* 头部和主体之间的注释(例如 Python 冒号后、主体前的注释)也归子块。
|
||||
*/
|
||||
export function compoundBlock(kind: BlockKind, outer: Node, bodyStart: number, items: Node[], list: ListBlocks): CodeBlock {
|
||||
const header = headerSpan(outer, bodyStart)
|
||||
const between = commentsIn(outer, header.endIndex, bodyStart)
|
||||
return { kind, node: outer, header, children: list([...between, ...items], header.endRow) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 一串语句 → 块。注释单独成块,相邻行的行注释合成一块;
|
||||
* 和前一条语句(或头部)同一行的注释是行尾注释,跟着那一行显示,不单独成块。
|
||||
* afterRow 是这串语句之前的最后一行(头部结尾行),模块顶层传 -1;
|
||||
* toBlocks 的 first 表示它是这串里第一条非注释语句(Python 认 docstring 用)。
|
||||
*/
|
||||
export function listBlocks(
|
||||
items: Node[],
|
||||
afterRow: number,
|
||||
toBlocks: (item: Node, first: boolean) => CodeBlock[],
|
||||
isLineComment: (comment: Node) => boolean,
|
||||
): CodeBlock[] {
|
||||
const out: CodeBlock[] = []
|
||||
let lastRow = afterRow
|
||||
let lineRun: CodeBlock | undefined
|
||||
let first = true
|
||||
for (const item of items) {
|
||||
if (item.type === 'comment') {
|
||||
if (item.startPosition.row === lastRow)
|
||||
continue
|
||||
const line = isLineComment(item)
|
||||
if (line && lineRun && item.startPosition.row === lineRun.header.endRow + 1) {
|
||||
lineRun.header = { ...lineRun.header, endIndex: item.endIndex, endRow: item.endPosition.row }
|
||||
}
|
||||
else {
|
||||
const block = leafBlock('comment', item)
|
||||
out.push(block)
|
||||
lineRun = line ? block : undefined
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.push(...toBlocks(item, first))
|
||||
first = false
|
||||
lineRun = undefined
|
||||
}
|
||||
lastRow = item.endPosition.row
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/** 同一个节点:web-tree-sitter 每次取子节点都会新建对象,只能按位置和类型比。 */
|
||||
export function sameNode(a: Node | null | undefined, b: Node): boolean {
|
||||
return !!a && a.startIndex === b.startIndex && a.endIndex === b.endIndex && a.type === b.type
|
||||
}
|
||||
Reference in new issue
Block a user