✨ feat(vscode): 代码阅读预览顶部加块筛选栏
语言插件按和语言无关的 id 报块属性(通用的由宿主从 facts 推),翻译插件起名,webview 只按 id 筛; 筛选整个工作区共用、记在 workspaceState;JavaScript 不再显示「抽象:否」「仅类型:否」
This commit is contained in:
1 parent
4eaf2ae11d
commit
1c7e31c51a
29 files changed
+1364
-319
No files matched your search
@@ -532,4 +532,15 @@ describe('typeScript 类', () => {
|
||||
expect(js[1]).toMatchObject({ name: 'A', bases: [{ value: 'mixin(B)' }], implements: [] })
|
||||
expect(js[2]).toMatchObject({ access: 'public', bases: [{ value: 'C' }] })
|
||||
})
|
||||
|
||||
it('javaScript / JSX 没有抽象类、仅类型导入:facts 里不填 abstract、typeOnly(不是填 false)', async () => {
|
||||
for (const languageId of ['javascript', 'javascriptreact']) {
|
||||
const js = await facts('import { a } from \'a\'\nclass K {}', languageId)
|
||||
expect(js[1]).toEqual({ sources: ['\'a\''], items: [{ kind: 'named', name: 'a' }] })
|
||||
expect(js[2]).toEqual({ name: 'K', access: 'private', decorators: [], typeParameters: [], bases: [], implements: [] })
|
||||
}
|
||||
const tsx = await facts('import { a } from \'a\'\nclass K {}', 'typescriptreact')
|
||||
expect(tsx[1]).toMatchObject({ typeOnly: false })
|
||||
expect(tsx[2]).toMatchObject({ abstract: false })
|
||||
})
|
||||
})
|
||||
@@ -10,6 +10,9 @@ const GRAMMARS: Record<string, string> = {
|
||||
javascriptreact: 'tree-sitter-javascript.wasm',
|
||||
}
|
||||
|
||||
/** 分得出抽象类、仅类型导入的 languageId;JavaScript / JSX 没有这两个概念。 */
|
||||
const TYPED_LANGUAGES = new Set(['typescript', 'typescriptreact'])
|
||||
|
||||
const FUNCTION_VALUES = new Set(['arrow_function', 'function_expression', 'function', 'generator_function'])
|
||||
const LOOPS = new Set(['for_statement', 'for_in_statement', 'while_statement', 'do_statement'])
|
||||
const CLASSES = new Set(['class_declaration', 'abstract_class_declaration', 'class'])
|
||||
@@ -41,118 +44,6 @@ function callbackBody(expression: Node | null | undefined): Node | null {
|
||||
return null
|
||||
}
|
||||
|
||||
function statements(items: Node[], top: boolean, afterRow: number): CodeBlock[] {
|
||||
return listBlocks(items, afterRow, item => statementBlocks(item, top), COMMENTS)
|
||||
}
|
||||
|
||||
/** 主体是语句块时子块来自它的语句,否则主体本身就是唯一的子语句(`if (x) return`)。 */
|
||||
function bodyShape(outer: Node, body: Node | null): BlockShape {
|
||||
if (!body)
|
||||
return leafShape(outer)
|
||||
const items = body.type === 'statement_block' ? named(body) : [body]
|
||||
return compoundShape(outer, body.startIndex, items, (list, afterRow) => statements(list, false, afterRow))
|
||||
}
|
||||
|
||||
function bodyBlock(kind: TextKind, outer: Node, body: Node | null): CodeBlock {
|
||||
return { kind, ...bodyShape(outer, body) }
|
||||
}
|
||||
|
||||
function statementBlocks(node: Node, top: boolean): CodeBlock[] {
|
||||
switch (node.type) {
|
||||
case 'import_statement':
|
||||
return [{ kind: 'import', ...leafShape(node), facts: importFacts(node) }]
|
||||
case 'export_statement':
|
||||
return exportBlocks(node, top)
|
||||
case 'if_statement':
|
||||
return ifBlocks(node, node, 'if')
|
||||
case 'try_statement':
|
||||
return tryBlocks(node)
|
||||
case 'switch_statement':
|
||||
return [switchBlock(node)]
|
||||
case 'switch_case':
|
||||
case 'switch_default':
|
||||
return [caseBlock(node)]
|
||||
case 'return_statement':
|
||||
return [leafBlock('return', node)]
|
||||
case 'expression_statement':
|
||||
return [bodyBlock('statement', node, callbackBody(node.firstNamedChild))]
|
||||
case 'labeled_statement': {
|
||||
const [first, ...rest] = statementBlocks(node.childForFieldName('body') ?? node, top)
|
||||
return first ? [{ ...extendHeader(first, node), node }, ...rest] : []
|
||||
}
|
||||
case 'statement_block':
|
||||
return statements(named(node), top, node.startPosition.row)
|
||||
case 'empty_statement':
|
||||
return []
|
||||
default:
|
||||
if (LOOPS.has(node.type))
|
||||
return [bodyBlock('loop', node, node.childForFieldName('body'))]
|
||||
return declarationBlocks(node, node, top)
|
||||
}
|
||||
}
|
||||
|
||||
/** export 包着声明时按声明定种类,块节点取整个 export_statement(头部包含 export)。 */
|
||||
function exportBlocks(node: Node, top: boolean): CodeBlock[] {
|
||||
const declaration = node.childForFieldName('declaration')
|
||||
if (declaration)
|
||||
return declarationBlocks(declaration, node, top)
|
||||
const value = node.childForFieldName('value')
|
||||
if (value && CLASSES.has(value.type))
|
||||
return [classBlock(node, value, value.childForFieldName('name'), 'public')]
|
||||
if (value && functionBody(value))
|
||||
return [functionBlock(node, value, value.childForFieldName('name'), 'public')]
|
||||
return [leafBlock('statement', node)]
|
||||
}
|
||||
|
||||
function declarationBlocks(declaration: Node, outer: Node, top: boolean): CodeBlock[] {
|
||||
switch (declaration.type) {
|
||||
case 'function_declaration':
|
||||
case 'generator_function_declaration':
|
||||
case 'function_signature':
|
||||
return [functionBlock(outer, declaration, declaration.childForFieldName('name'), declarationAccess(outer, top))]
|
||||
case 'lexical_declaration':
|
||||
case 'variable_declaration':
|
||||
return [variableBlock(declaration, outer, top)]
|
||||
case 'interface_declaration':
|
||||
case 'enum_declaration':
|
||||
return [membersBlock(declaration, outer)]
|
||||
case 'type_alias_declaration':
|
||||
return [leafBlock('type', outer)]
|
||||
default: {
|
||||
if (CLASSES.has(declaration.type))
|
||||
return [classBlock(outer, declaration, declaration.childForFieldName('name'), declarationAccess(outer, top))]
|
||||
const body = declaration.childForFieldName('body')
|
||||
return [body?.type === 'statement_block' ? bodyBlock('statement', outer, body) : leafBlock('statement', outer)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 唯一声明值是带语句块主体的函数 → function,是类表达式 → class;否则模块顶层的 const → const,其余 → variable。 */
|
||||
function variableBlock(declaration: Node, outer: Node, top: boolean): CodeBlock {
|
||||
const declarators = named(declaration).filter(child => child.type === 'variable_declarator')
|
||||
const access = declarationAccess(outer, top)
|
||||
const value = declarators.length === 1 ? declarators[0].childForFieldName('value') : null
|
||||
if (value && CLASSES.has(value.type))
|
||||
return classBlock(outer, value, declarators[0].childForFieldName('name'), access)
|
||||
if (value && functionBody(value))
|
||||
return functionBlock(outer, value, declarators[0].childForFieldName('name'), access)
|
||||
const isConst = declaration.type === 'lexical_declaration' && declaration.childForFieldName('kind')?.type === 'const'
|
||||
return {
|
||||
kind: top && isConst ? 'const' : 'variable',
|
||||
...bodyShape(outer, callbackBody(value)),
|
||||
facts: { access, declarators: declarators.map(declaratorFacts) },
|
||||
}
|
||||
}
|
||||
|
||||
/** 函数块:fn 是声明、方法或函数值,带语句块主体时子块来自主体;name 是它在源码里的名字(变量名、方法名)。 */
|
||||
function functionBlock(outer: Node, fn: Node, name: Node | null, access: Access | undefined, decorators: Node[] = []): CodeBlock {
|
||||
return {
|
||||
kind: 'function',
|
||||
...bodyShape(outer, fn.childForFieldName('body')),
|
||||
facts: functionFacts(fn, name, access, decorators),
|
||||
}
|
||||
}
|
||||
|
||||
/** 模块里的声明:export 了是公共,顶层没 export 是私有,嵌套在语句块里的没有访问级别。 */
|
||||
function declarationAccess(outer: Node, top: boolean): Access | undefined {
|
||||
if (outer.type === 'export_statement')
|
||||
@@ -242,8 +133,8 @@ function objectEntries(value: Node | null): EntryFacts[] | undefined {
|
||||
})
|
||||
}
|
||||
|
||||
/** import 语句:默认导入、`* as x`、`{ a as b }`、`import x = require('m')`;`import type` 只导入类型。 */
|
||||
function importFacts(node: Node): ImportFacts {
|
||||
/** import 语句:默认导入、`* as x`、`{ a as b }`、`import x = require('m')`;TypeScript(typed)的 `import type` 只导入类型。 */
|
||||
function importFacts(node: Node, typed: boolean): ImportFacts {
|
||||
const require = named(node).find(child => child.type === 'import_require_clause')
|
||||
const source = (require ?? node).childForFieldName('source')
|
||||
const items: ImportItem[] = []
|
||||
@@ -265,87 +156,22 @@ function importFacts(node: Node): ImportFacts {
|
||||
}
|
||||
}
|
||||
}
|
||||
return { sources: source ? [source] : [], typeOnly: children(node).some(child => child.type === 'type'), items }
|
||||
}
|
||||
|
||||
/** if 的 else / else if 展平成紧跟其后的兄弟块。 */
|
||||
function ifBlocks(ifNode: Node, outer: Node, kind: 'if' | 'else'): CodeBlock[] {
|
||||
const blocks = [bodyBlock(kind, outer, ifNode.childForFieldName('consequence'))]
|
||||
const alternative = ifNode.childForFieldName('alternative')
|
||||
if (alternative) {
|
||||
const inner = named(alternative).find(child => child.type !== 'comment') ?? null
|
||||
if (inner?.type === 'if_statement')
|
||||
blocks.push(...ifBlocks(inner, alternative, 'else'))
|
||||
else
|
||||
blocks.push(bodyBlock('else', alternative, inner))
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
function tryBlocks(node: Node): CodeBlock[] {
|
||||
const blocks = [bodyBlock('try', node, node.childForFieldName('body'))]
|
||||
const handler = node.childForFieldName('handler')
|
||||
if (handler)
|
||||
blocks.push(bodyBlock('catch', handler, handler.childForFieldName('body')))
|
||||
const finalizer = node.childForFieldName('finalizer')
|
||||
if (finalizer)
|
||||
blocks.push(bodyBlock('finally', finalizer, finalizer.childForFieldName('body')))
|
||||
return blocks
|
||||
}
|
||||
|
||||
function switchBlock(node: Node): CodeBlock {
|
||||
const body = node.childForFieldName('body')
|
||||
if (!body)
|
||||
return leafBlock('if', node)
|
||||
return { kind: 'if', ...compoundShape(node, body.startIndex, named(body), (list, afterRow) => statements(list, false, afterRow)) }
|
||||
}
|
||||
|
||||
/** case / default:头部到冒号为止,子块来自它下面的语句。 */
|
||||
function caseBlock(node: Node): CodeBlock {
|
||||
const [first] = node.childrenForFieldName('body')
|
||||
const bodyStart = first?.startIndex ?? node.endIndex
|
||||
const items = named(node).filter(child => child.startIndex >= bodyStart)
|
||||
return { kind: 'if', ...compoundShape(node, bodyStart, items, (list, afterRow) => statements(list, false, afterRow)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 类块:declaration 是类声明或类表达式,name 是它在源码里的名字(类名;赋给变量、类字段的类表达式取变量名、字段名),
|
||||
* decorators 是类主体里排在它前面的装饰器(类字段的值是类表达式时)。
|
||||
*/
|
||||
function classBlock(outer: Node, declaration: Node, name: Node | null, access: Access | undefined, decorators: Node[] = []): CodeBlock {
|
||||
const body = declaration.childForFieldName('body')
|
||||
const facts = classFacts(outer, declaration, name, access, decorators)
|
||||
if (!body)
|
||||
return { kind: 'class', ...leafShape(outer), facts }
|
||||
return { kind: 'class', ...compoundShape(outer, body.startIndex, named(body), classMembers), facts }
|
||||
}
|
||||
|
||||
/** 类成员:成员前面的装饰器是类主体里的兄弟节点,算进成员头部,也交给成员当装饰器。 */
|
||||
function classMembers(list: Node[], afterRow: number): CodeBlock[] {
|
||||
let decorators: Node[] = []
|
||||
return listBlocks(list, afterRow, (member) => {
|
||||
if (member.type === 'decorator') {
|
||||
decorators.push(member)
|
||||
return []
|
||||
}
|
||||
const blocks = memberBlocks(member, decorators)
|
||||
const [from] = decorators
|
||||
decorators = []
|
||||
return from ? blocks.map((block, i) => i === 0 ? extendHeader(block, from) : block) : blocks
|
||||
}, COMMENTS)
|
||||
const sources = source ? [source] : []
|
||||
return typed ? { sources, typeOnly: children(node).some(child => child.type === 'type'), items } : { sources, items }
|
||||
}
|
||||
|
||||
/**
|
||||
* 类的装饰器写在 export 前面时挂在 export 语句上,写在 class 前面时挂在类声明上;
|
||||
* 父类、实现的接口在 class_heritage 里:TypeScript 分 extends_clause / implements_clause,JavaScript 直接是 extends 后面的表达式。
|
||||
* 父类、实现的接口在 class_heritage 里:TypeScript 分 extends_clause / implements_clause,JavaScript 直接是 extends 后面的表达式;
|
||||
* 只有 TypeScript(typed)分抽象类。
|
||||
*/
|
||||
function classFacts(outer: Node, declaration: Node, name: Node | null, access: Access | undefined, decorators: Node[]): ClassFacts {
|
||||
function classFacts(outer: Node, declaration: Node, name: Node | null, access: Access | undefined, decorators: Node[], typed: boolean): ClassFacts {
|
||||
const holders = sameNode(outer, declaration) ? [declaration] : [outer, declaration]
|
||||
const heritage = syntaxChildren(named(declaration).find(child => child.type === 'class_heritage'))
|
||||
return {
|
||||
name: name ?? undefined,
|
||||
access,
|
||||
abstract: declaration.type === 'abstract_class_declaration',
|
||||
...(typed && { abstract: declaration.type === 'abstract_class_declaration' }),
|
||||
decorators: [...decorators, ...holders.flatMap(holder => named(holder).filter(child => child.type === 'decorator'))],
|
||||
typeParameters: syntaxChildren(declaration.childForFieldName('type_parameters')),
|
||||
bases: heritage.flatMap((part) => {
|
||||
@@ -370,35 +196,222 @@ function extendsBases(clause: Node): BaseFacts[] {
|
||||
return bases
|
||||
}
|
||||
|
||||
function memberBlocks(member: Node, decorators: Node[]): CodeBlock[] {
|
||||
switch (member.type) {
|
||||
case 'method_definition':
|
||||
case 'method_signature':
|
||||
case 'abstract_method_signature':
|
||||
return [functionBlock(member, member, member.childForFieldName('name'), memberAccess(member), decorators)]
|
||||
case 'class_static_block':
|
||||
return [bodyBlock('statement', member, member.childForFieldName('body'))]
|
||||
default: {
|
||||
const value = member.childForFieldName('value')
|
||||
if (value && CLASSES.has(value.type))
|
||||
return [classBlock(member, value, member.childForFieldName('name'), memberAccess(member), decorators)]
|
||||
if (value && functionBody(value))
|
||||
return [functionBlock(member, value, member.childForFieldName('name'), memberAccess(member), decorators)]
|
||||
return [leafBlock('field', member)]
|
||||
/**
|
||||
* 读代码块树的一套函数。typed:TypeScript(typescript、typescriptreact),分得出抽象类、仅类型导入;
|
||||
* JavaScript 没有这两个概念,facts 里不填(见 classFacts、importFacts)。
|
||||
*/
|
||||
function blockReader(typed: boolean): (root: Node) => CodeBlock[] {
|
||||
function statements(items: Node[], top: boolean, afterRow: number): CodeBlock[] {
|
||||
return listBlocks(items, afterRow, item => statementBlocks(item, top), COMMENTS)
|
||||
}
|
||||
|
||||
/** 主体是语句块时子块来自它的语句,否则主体本身就是唯一的子语句(`if (x) return`)。 */
|
||||
function bodyShape(outer: Node, body: Node | null): BlockShape {
|
||||
if (!body)
|
||||
return leafShape(outer)
|
||||
const items = body.type === 'statement_block' ? named(body) : [body]
|
||||
return compoundShape(outer, body.startIndex, items, (list, afterRow) => statements(list, false, afterRow))
|
||||
}
|
||||
|
||||
function bodyBlock(kind: TextKind, outer: Node, body: Node | null): CodeBlock {
|
||||
return { kind, ...bodyShape(outer, body) }
|
||||
}
|
||||
|
||||
function statementBlocks(node: Node, top: boolean): CodeBlock[] {
|
||||
switch (node.type) {
|
||||
case 'import_statement':
|
||||
return [{ kind: 'import', ...leafShape(node), facts: importFacts(node, typed) }]
|
||||
case 'export_statement':
|
||||
return exportBlocks(node, top)
|
||||
case 'if_statement':
|
||||
return ifBlocks(node, node, 'if')
|
||||
case 'try_statement':
|
||||
return tryBlocks(node)
|
||||
case 'switch_statement':
|
||||
return [switchBlock(node)]
|
||||
case 'switch_case':
|
||||
case 'switch_default':
|
||||
return [caseBlock(node)]
|
||||
case 'return_statement':
|
||||
return [leafBlock('return', node)]
|
||||
case 'expression_statement':
|
||||
return [bodyBlock('statement', node, callbackBody(node.firstNamedChild))]
|
||||
case 'labeled_statement': {
|
||||
const [first, ...rest] = statementBlocks(node.childForFieldName('body') ?? node, top)
|
||||
return first ? [{ ...extendHeader(first, node), node }, ...rest] : []
|
||||
}
|
||||
case 'statement_block':
|
||||
return statements(named(node), top, node.startPosition.row)
|
||||
case 'empty_statement':
|
||||
return []
|
||||
default:
|
||||
if (LOOPS.has(node.type))
|
||||
return [bodyBlock('loop', node, node.childForFieldName('body'))]
|
||||
return declarationBlocks(node, node, top)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** interface / enum:成员(属性、方法签名、枚举项)→ field。 */
|
||||
function membersBlock(declaration: Node, outer: Node): CodeBlock {
|
||||
const body = declaration.childForFieldName('body')
|
||||
if (!body)
|
||||
return leafBlock('type', outer)
|
||||
return {
|
||||
kind: 'type',
|
||||
...compoundShape(outer, body.startIndex, named(body), (list, afterRow) =>
|
||||
listBlocks(list, afterRow, member => [leafBlock('field', member)], COMMENTS)),
|
||||
/** export 包着声明时按声明定种类,块节点取整个 export_statement(头部包含 export)。 */
|
||||
function exportBlocks(node: Node, top: boolean): CodeBlock[] {
|
||||
const declaration = node.childForFieldName('declaration')
|
||||
if (declaration)
|
||||
return declarationBlocks(declaration, node, top)
|
||||
const value = node.childForFieldName('value')
|
||||
if (value && CLASSES.has(value.type))
|
||||
return [classBlock(node, value, value.childForFieldName('name'), 'public')]
|
||||
if (value && functionBody(value))
|
||||
return [functionBlock(node, value, value.childForFieldName('name'), 'public')]
|
||||
return [leafBlock('statement', node)]
|
||||
}
|
||||
|
||||
function declarationBlocks(declaration: Node, outer: Node, top: boolean): CodeBlock[] {
|
||||
switch (declaration.type) {
|
||||
case 'function_declaration':
|
||||
case 'generator_function_declaration':
|
||||
case 'function_signature':
|
||||
return [functionBlock(outer, declaration, declaration.childForFieldName('name'), declarationAccess(outer, top))]
|
||||
case 'lexical_declaration':
|
||||
case 'variable_declaration':
|
||||
return [variableBlock(declaration, outer, top)]
|
||||
case 'interface_declaration':
|
||||
case 'enum_declaration':
|
||||
return [membersBlock(declaration, outer)]
|
||||
case 'type_alias_declaration':
|
||||
return [leafBlock('type', outer)]
|
||||
default: {
|
||||
if (CLASSES.has(declaration.type))
|
||||
return [classBlock(outer, declaration, declaration.childForFieldName('name'), declarationAccess(outer, top))]
|
||||
const body = declaration.childForFieldName('body')
|
||||
return [body?.type === 'statement_block' ? bodyBlock('statement', outer, body) : leafBlock('statement', outer)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 唯一声明值是带语句块主体的函数 → function,是类表达式 → class;否则模块顶层的 const → const,其余 → variable。 */
|
||||
function variableBlock(declaration: Node, outer: Node, top: boolean): CodeBlock {
|
||||
const declarators = named(declaration).filter(child => child.type === 'variable_declarator')
|
||||
const access = declarationAccess(outer, top)
|
||||
const value = declarators.length === 1 ? declarators[0].childForFieldName('value') : null
|
||||
if (value && CLASSES.has(value.type))
|
||||
return classBlock(outer, value, declarators[0].childForFieldName('name'), access)
|
||||
if (value && functionBody(value))
|
||||
return functionBlock(outer, value, declarators[0].childForFieldName('name'), access)
|
||||
const isConst = declaration.type === 'lexical_declaration' && declaration.childForFieldName('kind')?.type === 'const'
|
||||
return {
|
||||
kind: top && isConst ? 'const' : 'variable',
|
||||
...bodyShape(outer, callbackBody(value)),
|
||||
facts: { access, declarators: declarators.map(declaratorFacts) },
|
||||
}
|
||||
}
|
||||
|
||||
/** 函数块:fn 是声明、方法或函数值,带语句块主体时子块来自主体;name 是它在源码里的名字(变量名、方法名)。 */
|
||||
function functionBlock(outer: Node, fn: Node, name: Node | null, access: Access | undefined, decorators: Node[] = []): CodeBlock {
|
||||
return {
|
||||
kind: 'function',
|
||||
...bodyShape(outer, fn.childForFieldName('body')),
|
||||
facts: functionFacts(fn, name, access, decorators),
|
||||
}
|
||||
}
|
||||
|
||||
/** if 的 else / else if 展平成紧跟其后的兄弟块。 */
|
||||
function ifBlocks(ifNode: Node, outer: Node, kind: 'if' | 'else'): CodeBlock[] {
|
||||
const blocks = [bodyBlock(kind, outer, ifNode.childForFieldName('consequence'))]
|
||||
const alternative = ifNode.childForFieldName('alternative')
|
||||
if (alternative) {
|
||||
const inner = named(alternative).find(child => child.type !== 'comment') ?? null
|
||||
if (inner?.type === 'if_statement')
|
||||
blocks.push(...ifBlocks(inner, alternative, 'else'))
|
||||
else
|
||||
blocks.push(bodyBlock('else', alternative, inner))
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
function tryBlocks(node: Node): CodeBlock[] {
|
||||
const blocks = [bodyBlock('try', node, node.childForFieldName('body'))]
|
||||
const handler = node.childForFieldName('handler')
|
||||
if (handler)
|
||||
blocks.push(bodyBlock('catch', handler, handler.childForFieldName('body')))
|
||||
const finalizer = node.childForFieldName('finalizer')
|
||||
if (finalizer)
|
||||
blocks.push(bodyBlock('finally', finalizer, finalizer.childForFieldName('body')))
|
||||
return blocks
|
||||
}
|
||||
|
||||
function switchBlock(node: Node): CodeBlock {
|
||||
const body = node.childForFieldName('body')
|
||||
if (!body)
|
||||
return leafBlock('if', node)
|
||||
return { kind: 'if', ...compoundShape(node, body.startIndex, named(body), (list, afterRow) => statements(list, false, afterRow)) }
|
||||
}
|
||||
|
||||
/** case / default:头部到冒号为止,子块来自它下面的语句。 */
|
||||
function caseBlock(node: Node): CodeBlock {
|
||||
const [first] = node.childrenForFieldName('body')
|
||||
const bodyStart = first?.startIndex ?? node.endIndex
|
||||
const items = named(node).filter(child => child.startIndex >= bodyStart)
|
||||
return { kind: 'if', ...compoundShape(node, bodyStart, items, (list, afterRow) => statements(list, false, afterRow)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 类块:declaration 是类声明或类表达式,name 是它在源码里的名字(类名;赋给变量、类字段的类表达式取变量名、字段名),
|
||||
* decorators 是类主体里排在它前面的装饰器(类字段的值是类表达式时)。
|
||||
*/
|
||||
function classBlock(outer: Node, declaration: Node, name: Node | null, access: Access | undefined, decorators: Node[] = []): CodeBlock {
|
||||
const body = declaration.childForFieldName('body')
|
||||
const facts = classFacts(outer, declaration, name, access, decorators, typed)
|
||||
if (!body)
|
||||
return { kind: 'class', ...leafShape(outer), facts }
|
||||
return { kind: 'class', ...compoundShape(outer, body.startIndex, named(body), classMembers), facts }
|
||||
}
|
||||
|
||||
/** 类成员:成员前面的装饰器是类主体里的兄弟节点,算进成员头部,也交给成员当装饰器。 */
|
||||
function classMembers(list: Node[], afterRow: number): CodeBlock[] {
|
||||
let decorators: Node[] = []
|
||||
return listBlocks(list, afterRow, (member) => {
|
||||
if (member.type === 'decorator') {
|
||||
decorators.push(member)
|
||||
return []
|
||||
}
|
||||
const blocks = memberBlocks(member, decorators)
|
||||
const [from] = decorators
|
||||
decorators = []
|
||||
return from ? blocks.map((block, i) => i === 0 ? extendHeader(block, from) : block) : blocks
|
||||
}, COMMENTS)
|
||||
}
|
||||
|
||||
function memberBlocks(member: Node, decorators: Node[]): CodeBlock[] {
|
||||
switch (member.type) {
|
||||
case 'method_definition':
|
||||
case 'method_signature':
|
||||
case 'abstract_method_signature':
|
||||
return [functionBlock(member, member, member.childForFieldName('name'), memberAccess(member), decorators)]
|
||||
case 'class_static_block':
|
||||
return [bodyBlock('statement', member, member.childForFieldName('body'))]
|
||||
default: {
|
||||
const value = member.childForFieldName('value')
|
||||
if (value && CLASSES.has(value.type))
|
||||
return [classBlock(member, value, member.childForFieldName('name'), memberAccess(member), decorators)]
|
||||
if (value && functionBody(value))
|
||||
return [functionBlock(member, value, member.childForFieldName('name'), memberAccess(member), decorators)]
|
||||
return [leafBlock('field', member)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** interface / enum:成员(属性、方法签名、枚举项)→ field。 */
|
||||
function membersBlock(declaration: Node, outer: Node): CodeBlock {
|
||||
const body = declaration.childForFieldName('body')
|
||||
if (!body)
|
||||
return leafBlock('type', outer)
|
||||
return {
|
||||
kind: 'type',
|
||||
...compoundShape(outer, body.startIndex, named(body), (list, afterRow) =>
|
||||
listBlocks(list, afterRow, member => [leafBlock('field', member)], COMMENTS)),
|
||||
}
|
||||
}
|
||||
|
||||
return root => statements(named(root), true, -1)
|
||||
}
|
||||
|
||||
// ─── 源码着色 ─────────────────────────────────────────────────────────────────
|
||||
@@ -509,7 +522,7 @@ export function createTypeScriptAdapter(wasmDir: string): LanguageAdapter {
|
||||
throw new Error(`TypeScript 语言插件不支持 ${languageId}`)
|
||||
return path.join(wasmDir, file)
|
||||
},
|
||||
toBlocks: root => statements(named(root), true, -1),
|
||||
toBlocks: (root, _source, languageId) => blockReader(TYPED_LANGUAGES.has(languageId))(root),
|
||||
tokenRole: typescriptTokenRole,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user