✨ feat(vscode): 代码阅读支持库规则,内置 SQLAlchemy 模型类显示成数据表卡片
This commit is contained in:
1 parent
32be7d54b6
commit
5ece24cef9
26 files changed
+1768
-81
No files matched your search
@@ -1,27 +1,33 @@
|
||||
import type { BlockAttributes, BlockKind, CodeBlock, LanguageAdapter, Node, PreviewText, PropertyRow, Segment, SourceDocument, SourceRange, TokenRole, Translator } from './contract'
|
||||
import type { BlockAttributes, CodeBlock, KindBadge, LanguageAdapter, LibraryRule, Node, PreviewText, PropertyRow, RuleContext, Segment, SourceDocument, SourceRange, TokenRole, Translator } from './contract'
|
||||
import type { TreeSitterRuntime } from './runtime'
|
||||
import { blockAttributes } from './attributes'
|
||||
import { commonIndent, splitLines } from './editText'
|
||||
import { absorbedRange, activeRules, claimBlock, importsIn } from './libraryRules'
|
||||
|
||||
interface BlockViewBase {
|
||||
/** 在树里的位置,例如 '6.1.0'。 */
|
||||
id: string
|
||||
kind: BlockKind
|
||||
/** header 的起止行,0 起。 */
|
||||
/** BlockKind,或库规则认领的块的种类 id(RuleBlock.kind,带点,例如 sqlalchemy.table)。 */
|
||||
kind: string
|
||||
/** 编辑范围的起止行,0 起:一般是 header;库规则认领的块到最后一个并进来的子块为止(见 RuleBlock.absorbs)。 */
|
||||
startLine: number
|
||||
endLine: number
|
||||
/** header 所在的整行源码,去掉共同缩进。 */
|
||||
/** 编辑范围的整行源码,去掉共同缩进。 */
|
||||
code: string
|
||||
/** 源码着色(拼起来等于 code)。 */
|
||||
codeTokens: Segment[]
|
||||
/** 块自己的名字(见 ownName)在源码里的范围:跳到的定义就是它时闪整块,不闪属性表里的某一行。 */
|
||||
defines?: SourceRange
|
||||
/** 筛选属性(含 kind),见 blockAttributes。 */
|
||||
/** 筛选属性(含 kind),见 blockAttributes;库规则认领的块叠上规则给的,kind 是规则的种类 id。 */
|
||||
attributes: BlockAttributes
|
||||
/** 库规则认领的块才有:种类标签的文字和颜色(RuleBlock.badge)。预览同类块一样默认收起它的子块。 */
|
||||
badge?: KindBadge
|
||||
children: BlockView[]
|
||||
}
|
||||
|
||||
/** 发给 webview 的一块(可序列化):函数、类、导入、常量、变量块预览时是属性表,注释块是 Markdown 说明,其余是一行译文。 */
|
||||
/**
|
||||
* 发给 webview 的一块(可序列化):函数、类、导入、常量、变量块和库规则认领的块预览时是属性表,注释块是 Markdown 说明,其余是一行译文。
|
||||
*/
|
||||
export type BlockView = BlockViewBase & ({ translation: PreviewText } | { properties: PropertyRow[] })
|
||||
|
||||
interface Line { start: number, end: number }
|
||||
@@ -102,23 +108,64 @@ function ownName(block: CodeBlock): Node | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
/** 代码块树 → 视图数据:header 整行源码去共同缩进、按语法树着色、调用翻译插件。 */
|
||||
export function toBlockViews(blocks: CodeBlock[], source: string, adapter: LanguageAdapter, translator: Translator | undefined): BlockView[] {
|
||||
/** 用哪些库规则(某 languageId 注册的,按注册顺序;这份源码导入了规则关心的库才启用),规则出错时怎么记。 */
|
||||
export interface RuleRun {
|
||||
rules: readonly LibraryRule[]
|
||||
report: (message: string) => void
|
||||
}
|
||||
|
||||
const NO_RULES: RuleRun = { rules: [], report: () => {} }
|
||||
|
||||
/**
|
||||
* 代码块树 → 视图数据:header 整行源码去共同缩进、按语法树着色、调用翻译插件。
|
||||
* 每块先问启用的库规则,认领了就换成规则给的替身(见 RuleBlock),并进去的子块不再单独成块。
|
||||
*/
|
||||
export function toBlockViews(blocks: CodeBlock[], source: string, adapter: LanguageAdapter, translator: Translator | undefined, rules: RuleRun = NO_RULES): BlockView[] {
|
||||
const lines = lineRanges(source)
|
||||
const texts = splitLines(source)
|
||||
const document: SourceDocument = { source, language: adapter }
|
||||
const build = (block: CodeBlock, id: string): BlockView => {
|
||||
const { startRow, endRow } = block.header
|
||||
const imports = importsIn(blocks)
|
||||
const active = activeRules(rules.rules, imports)
|
||||
const context: RuleContext = {
|
||||
document,
|
||||
imports,
|
||||
...(translator && { displayLanguage: translator.displayLanguage }),
|
||||
translate: node => translator ? translator.expression(node, document) : [{ text: node.text, role: 'plain' }],
|
||||
doc: node => translator?.doc(node, document),
|
||||
}
|
||||
/** startRow..endRow 这几行整行源码去共同缩进,和它的着色。 */
|
||||
const sourceRows = (root: Node, startRow: number, endRow: number): Pick<BlockView, 'startLine' | 'endLine' | 'code' | 'codeTokens'> => {
|
||||
const rows = texts.slice(startRow, endRow + 1)
|
||||
const indent = commonIndent(rows).length
|
||||
return {
|
||||
startLine: startRow,
|
||||
endLine: endRow,
|
||||
code: rows.map(row => row.slice(Math.min(indent, row.length))).join('\n'),
|
||||
codeTokens: colorLines(root, source, lines, startRow, endRow, indent, adapter.tokenRole),
|
||||
}
|
||||
}
|
||||
const build = (block: CodeBlock, id: string): BlockView => {
|
||||
const root = block.node.tree.rootNode
|
||||
const claim = active.length > 0 ? claimBlock(block, active, context, rules.report) : undefined
|
||||
if (claim) {
|
||||
const { kind, badge, rows, defines, attributes } = claim.block
|
||||
const { endRow, kept } = absorbedRange(block, claim.block.absorbs)
|
||||
return {
|
||||
id,
|
||||
kind,
|
||||
...sourceRows(root, block.header.startRow, endRow),
|
||||
...(defines && { defines: { startIndex: defines.startIndex, endIndex: defines.endIndex } }),
|
||||
attributes: { ...blockAttributes(block), ...attributes, kind },
|
||||
badge,
|
||||
children: kept.map((child, i) => build(child, `${id}.${i}`)),
|
||||
properties: rows,
|
||||
}
|
||||
}
|
||||
const name = ownName(block)
|
||||
return {
|
||||
id,
|
||||
kind: block.kind,
|
||||
startLine: startRow,
|
||||
endLine: endRow,
|
||||
code: rows.map(row => row.slice(Math.min(indent, row.length))).join('\n'),
|
||||
codeTokens: colorLines(block.node.tree.rootNode, source, lines, startRow, endRow, indent, adapter.tokenRole),
|
||||
...sourceRows(root, block.header.startRow, block.header.endRow),
|
||||
...(name && { defines: { startIndex: name.startIndex, endIndex: name.endIndex } }),
|
||||
attributes: blockAttributes(block),
|
||||
children: block.children.map((child, i) => build(child, `${id}.${i}`)),
|
||||
@@ -137,10 +184,11 @@ export async function readBlockViews(
|
||||
translator: Translator | undefined,
|
||||
languageId: string,
|
||||
source: string,
|
||||
rules: RuleRun = NO_RULES,
|
||||
): Promise<BlockView[]> {
|
||||
const tree = await runtime.parse(adapter, languageId, source)
|
||||
try {
|
||||
return toBlockViews(adapter.toBlocks(tree.rootNode, source, languageId), source, adapter, translator)
|
||||
return toBlockViews(adapter.toBlocks(tree.rootNode, source, languageId), source, adapter, translator, rules)
|
||||
}
|
||||
finally {
|
||||
tree.delete()
|
||||
|
||||
Reference in new issue
Block a user