✨ feat(vscode): 代码阅读加异常卡片,Python、TypeScript 的异常类画成同一张表(属性、构造参数),方法照常是子块
This commit is contained in:
1 parent
d5a092dd6f
commit
0afd012c94
21 files changed
+1118
-66
No files matched your search
@@ -1,8 +1,8 @@
|
||||
import type { Access, BlockShape, ClassFacts, CodeBlock, EntryFacts, FunctionFacts, ImportFacts, ImportItem, IterationClause, IterationFacts, IterationResult, LanguageAdapter, Node, ParameterFacts, TextKind, TokenRole } from '../contract'
|
||||
import type { CommentRules } from './shared'
|
||||
import type { CommentRules, ExceptionRules } from './shared'
|
||||
import * as path from 'node:path'
|
||||
import { pythonNameReader } from './pythonNames'
|
||||
import { children, compoundShape, leafBlock, leafShape, listBlocks, named, sameNode, syntaxChildren } from './shared'
|
||||
import { children, compoundShape, leafBlock, leafShape, listBlocks, named, sameNode, syntaxChildren, withExceptions } from './shared'
|
||||
|
||||
/** 常量、类字段只认直接写在模块 / 类主体里的赋值;这两处的声明也才有访问级别。 */
|
||||
type Scope = 'module' | 'class' | 'other'
|
||||
@@ -398,6 +398,122 @@ function tryBlocks(node: Node): CodeBlock[] {
|
||||
return blocks
|
||||
}
|
||||
|
||||
// ─── 异常类 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Python 自带的异常类(builtins 里的,含各种 Warning)。 */
|
||||
const BUILTIN_EXCEPTIONS = new Set([
|
||||
'BaseException',
|
||||
'BaseExceptionGroup',
|
||||
'GeneratorExit',
|
||||
'KeyboardInterrupt',
|
||||
'SystemExit',
|
||||
'Exception',
|
||||
'ArithmeticError',
|
||||
'FloatingPointError',
|
||||
'OverflowError',
|
||||
'ZeroDivisionError',
|
||||
'AssertionError',
|
||||
'AttributeError',
|
||||
'BufferError',
|
||||
'EOFError',
|
||||
'ExceptionGroup',
|
||||
'ImportError',
|
||||
'ModuleNotFoundError',
|
||||
'LookupError',
|
||||
'IndexError',
|
||||
'KeyError',
|
||||
'MemoryError',
|
||||
'NameError',
|
||||
'UnboundLocalError',
|
||||
'OSError',
|
||||
'EnvironmentError',
|
||||
'IOError',
|
||||
'WindowsError',
|
||||
'BlockingIOError',
|
||||
'ChildProcessError',
|
||||
'ConnectionError',
|
||||
'BrokenPipeError',
|
||||
'ConnectionAbortedError',
|
||||
'ConnectionRefusedError',
|
||||
'ConnectionResetError',
|
||||
'FileExistsError',
|
||||
'FileNotFoundError',
|
||||
'InterruptedError',
|
||||
'IsADirectoryError',
|
||||
'NotADirectoryError',
|
||||
'PermissionError',
|
||||
'ProcessLookupError',
|
||||
'TimeoutError',
|
||||
'ReferenceError',
|
||||
'RuntimeError',
|
||||
'NotImplementedError',
|
||||
'PythonFinalizationError',
|
||||
'RecursionError',
|
||||
'StopAsyncIteration',
|
||||
'StopIteration',
|
||||
'SyntaxError',
|
||||
'IndentationError',
|
||||
'TabError',
|
||||
'SystemError',
|
||||
'TypeError',
|
||||
'ValueError',
|
||||
'UnicodeError',
|
||||
'UnicodeDecodeError',
|
||||
'UnicodeEncodeError',
|
||||
'UnicodeTranslateError',
|
||||
'Warning',
|
||||
'BytesWarning',
|
||||
'DeprecationWarning',
|
||||
'EncodingWarning',
|
||||
'FutureWarning',
|
||||
'ImportWarning',
|
||||
'PendingDeprecationWarning',
|
||||
'ResourceWarning',
|
||||
'RuntimeWarning',
|
||||
'SyntaxWarning',
|
||||
'UnicodeWarning',
|
||||
'UserWarning',
|
||||
])
|
||||
/** PEP 8 的命名习惯:异常类名以 Error 结尾(别的文件里的 `AppException`、`*Warning` 也常见)。 */
|
||||
const EXCEPTION_NAME = /(?:Error|Exception|Warning)$/
|
||||
|
||||
/** 父类写的类名:`X`、`pkg.X` 取 X,泛型 `Base[T]` 取 Base 的;关键字参数、`*bases`、调用没有。 */
|
||||
function className(node: Node | null): string | undefined {
|
||||
switch (node?.type) {
|
||||
case 'identifier':
|
||||
return node.text
|
||||
case 'attribute':
|
||||
return node.childForFieldName('attribute')?.text
|
||||
case 'subscript':
|
||||
return className(node.childForFieldName('value'))
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 类属性:类主体里给一个名字赋值的语句(`x = 1`、`x: int = 1`、只写注解的 `x: int`);连着赋值(`a = b = 1`)、解构赋值不算。
|
||||
* 构造函数是 `__init__`,参数去掉第一个(self;`*args` 这类不是 self 的不去)。
|
||||
*/
|
||||
const PYTHON_EXCEPTIONS: ExceptionRules = {
|
||||
baseName: base => className(base.value),
|
||||
isExceptionName: name => BUILTIN_EXCEPTIONS.has(name) || EXCEPTION_NAME.test(name),
|
||||
field: (block) => {
|
||||
const assignment = block.kind === 'field' || block.kind === 'variable' ? assignmentIn(block.node) : undefined
|
||||
const name = assignment?.childForFieldName('left')
|
||||
const value = assignment?.childForFieldName('right')
|
||||
if (!assignment || name?.type !== 'identifier' || value?.type === 'assignment')
|
||||
return undefined
|
||||
return { name, type: assignment.childForFieldName('type') ?? undefined, value: value ?? undefined }
|
||||
},
|
||||
constructorParameters: (block) => {
|
||||
if (block.kind !== 'function' || block.facts.name?.text !== '__init__')
|
||||
return undefined
|
||||
const [receiver, ...rest] = block.facts.parameters
|
||||
return receiver?.name.type === 'identifier' ? rest : block.facts.parameters
|
||||
},
|
||||
}
|
||||
|
||||
// ─── 源码着色 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
const STORAGE_KEYWORDS = new Set(['def', 'class', 'async', 'lambda', 'global', 'nonlocal', 'del'])
|
||||
@@ -484,7 +600,7 @@ export function createPythonAdapter(wasmDir: string): LanguageAdapter {
|
||||
id: 'python',
|
||||
languageIds: ['python'],
|
||||
grammarPath: () => path.join(wasmDir, 'tree-sitter-python.wasm'),
|
||||
toBlocks: root => statements(named(root), 'module', -1),
|
||||
toBlocks: root => withExceptions(statements(named(root), 'module', -1), PYTHON_EXCEPTIONS),
|
||||
tokenRole: pythonTokenRole,
|
||||
iteration: pythonIteration,
|
||||
displayNames: pythonNameReader,
|
||||
|
||||
Reference in new issue
Block a user