✨ feat(vscode): 代码阅读类块改成属性表并默认折叠,类的说明进「说明」行

This commit is contained in:
cruldra committed 2026-09-25 14:46:33 +08:00
1 parent 3329aca2e1
commit 43323659e1
22 files changed
+580 -121

No files matched your search

@@ -174,7 +174,7 @@ describe('typeScript 中文直译', () => {
'k = [\'a\'] as const',
'for (const key in obj) {}',
'has = \'k\' in obj',
'class A extends B implements C {}',
'use(class A extends B implements C {})',
'interface I extends J, K {}',
'type F<T extends X> = (t: T) => void',
'type R<T> = T extends string ? 1 : 2',
@@ -191,7 +191,7 @@ describe('typeScript 中文直译', () => {
expect(map[4]).toBe('k = [\'a\'] 断言为 常量')
expect(map[5]).toBe('遍历 (常量 key 取自 obj)')
expect(map[6]).toBe('has = \'k\' 在 obj')
expect(map[7]).toBe('类 A 继承 B 实现 C')
expect(map[7]).toBe('use(类 A 继承 B 实现 C {})')
expect(map[8]).toBe('接口 I 继承 J, K')
expect(map[9]).toBe('类型 F<T 限定为 X> = (t: T) => 无')
expect(map[10]).toBe('类型 R<T> = T 符合 字符串 ? 1 : 2')
@@ -244,7 +244,6 @@ describe('python 中文直译', () => {
28: '最终',
29: 'skipped.clear()',
30: '返回 found',
33: '类 EnvLockResult',
34: 'total: 整数',
35: 'ok: 列表[字符串] = 空列表',
36: 'failed: 字典[字符串, 字符串] = 空字典',
@@ -390,6 +389,7 @@ describe('属性表', () => {
const sample = rowTexts(await describeAll(py, 'python', pythonZhTranslator, readFixture('sample.py')))
expect(sample[10]).toEqual(['名称: find_env_files', '说明: Return every .env file under root.', '访问级别: 公共', '异步: 否', '输入参数: 1 个', ' root | 字符串 | ', '输出参数: 列表[字符串]'])
expect(sample[33]).toEqual(['名称: EnvLockResult', '访问级别: 公共'])
const port = rowTexts(await describeAll(py, 'python', pythonZhTranslator, '_default_port = 5680 if _role == "beat" else 5679'))
expect(port[1]).toEqual(['名称: _default_port', '访问级别: 私有', '初始值: 如果 _role 等于 "beat" 则 5680 否则 5679'])
})
@@ -555,10 +555,58 @@ describe('属性表', () => {
1: ['名称: 匿名', '访问级别: 公共', '异步: 是', '输入参数: 无', '输出参数: 未标注'],
2: ['名称: f', '访问级别: 私有', '异步: 否', '输入参数: 2 个', ' b? | 数值 | ', ' c | 未标注 | 默认 1', '输出参数: 无'],
3: ['名称: inner', '异步: 否', '输入参数: 无', '输出参数: 未标注'],
5: ['名称: K', '访问级别: 私有', '抽象: 否'],
6: ['名称: load', '访问级别: 受保护', '异步: 否', '装饰器: @memo()', '输入参数: 无', '输出参数: x is Foo'],
})
})
it('类:说明、抽象(只有 TypeScript 有)、装饰器、类型参数、父类、实现的接口有才显示;父类的泛型实参、类型照常翻', async () => {
const tsRows = rowTexts(await describeAll(ts, 'typescript', typescriptZhTranslator, readFixture('classes.ts.txt')))
expect(tsRows[3]).toEqual([
'名称: Repository',
'说明: 仓库基类。\n\n子类实现 `load`。',
'访问级别: 公共',
'抽象: 是',
'装饰器: @Injectable()',
'类型参数: K, T 限定为 Entity = Entity',
'父类: Base<字符串>',
'实现的接口: Loader<T>, Disposable',
])
expect(tsRows[15]).toEqual(['名称: Query', '访问级别: 公共', '抽象: 否', '父类: QueryBase'])
expect(tsRows[20]).toEqual(['名称: 匿名', '访问级别: 公共', '抽象: 否'])
expect(tsRows[24]).toEqual(['名称: Mixed', '说明: 类表达式', '访问级别: 公共', '抽象: 否', '父类: mixin(A, B)'])
expect(tsRows[28]).toEqual(['名称: Local', '抽象: 否'])
expect(tsRows[32]).toEqual(['名称: Plain', '访问级别: 私有', '抽象: 否', '装饰器: @sealed'])
const pyRows = rowTexts(await describeAll(py, 'python', pythonZhTranslator, readFixture('classes.py')))
expect(pyRows[6]).toEqual(['名称: AppException', '说明: 应用异常的基类。\n\n带上 HTTP 状态码。', '访问级别: 公共', '父类: Exception'])
expect(pyRows[18]).toEqual(['名称: NotFound', '说明: 找不到资源。', '访问级别: 公共', '父类: AppException'])
expect(pyRows[24]).toEqual(['名称: Meta', '访问级别: 公共', '装饰器: @dataclass(frozen=True)', '父类: Base, metaclass=ABCMeta'])
expect(pyRows[29]).toEqual(['名称: _Hidden', '访问级别: 私有'])
expect(pyRows[38]).toEqual(['名称: Local', '说明: 局部类。'])
})
it('类的着色:名称、父类名按类型,说明是 Markdown,抽象按 storage', async () => {
const tsRows = colors((await describeAll(ts, 'typescript', typescriptZhTranslator, readFixture('classes.ts.txt'))).get(3) ?? [])
expect(tsRows[0].value).toEqual([{ text: 'Repository', role: 'type' }])
expect(tsRows[1].value).toEqual({ markdown: '仓库基类。\n\n子类实现 `load`。' })
expect(tsRows[3].value).toEqual([{ text: '是', role: 'storage' }])
expect(valueOf(tsRows, '父类')).toEqual([
{ text: 'Base', role: 'type' },
{ text: '<', role: 'plain' },
{ text: '字符串', role: 'type' },
{ text: '>', role: 'plain' },
])
const pyRows = colors((await describeAll(py, 'python', pythonZhTranslator, readFixture('classes.py'))).get(24) ?? [])
expect(valueOf(pyRows, '父类')).toEqual([
{ text: 'Base', role: 'type' },
{ text: ', ', role: 'plain' },
{ text: 'metaclass', role: 'variable' },
{ text: '=', role: 'plain' },
{ text: 'ABCMeta', role: 'variable' },
])
})
it('变量:一条语句多个变量各占一组,没初始值写无,带语句块主体的回调只翻到主体开始之前', async () => {
const source = [
'let i = 0, n: number',
@@ -675,12 +723,35 @@ describe('源码下标(跳到定义用)', () => {
expect(marked(tsSource, tsRows.get(3))).toEqual(['名称: i', '名称: j'])
})
it('类:名称、类型参数、父类、实现的接口里的名字带下标;只有「类型参数」一行标出它讲的定义(这几个泛型参数),名称是块自己的名字、不标', async () => {
const source = 'export class Repo<K, T extends Entity> extends Base<T> implements Loader<K>, Disposable {}'
const at = (word: string): number => source.indexOf(word)
const rows = await describeAll(ts, 'typescript', typescriptZhTranslator, source)
const found = rows.get(1) ?? []
expect(linked(valueOf(found, '名称'))).toEqual([['Repo', at('Repo')]])
expect(linked(valueOf(found, '类型参数'))).toEqual([['K', at('K,')], ['T', at('T extends')], ['Entity', at('Entity')]])
expect(linked(valueOf(found, '父类'))).toEqual([['Base', at('Base')], ['T', at('T> implements')]])
expect(linked(valueOf(found, '实现的接口'))).toEqual([['Loader', at('Loader')], ['K', at('K>,')], ['Disposable', at('Disposable')]])
expect(found.filter(row => row.defines).map(row => [row.label, source.slice(row.defines?.startIndex, row.defines?.endIndex)])).toEqual([['类型参数', 'K, T extends Entity']])
const pySource = 'class NotFound(errors.AppException, metaclass=ABCMeta):\n pass'
const pyRows = await describeAll(py, 'python', pythonZhTranslator, pySource)
expect(linked(valueOf(pyRows.get(1), '父类'))).toEqual([
['errors', pySource.indexOf('errors')],
['AppException', pySource.indexOf('AppException')],
['metaclass', pySource.indexOf('metaclass')],
['ABCMeta', pySource.indexOf('ABCMeta')],
])
})
it('样例全文:带下标的只有照抄的名字和字符串,下标处的源码就是这段;变量名、函数名都带,译词、说明词都不带', async () => {
const samples: [LanguageAdapter, string, Translator, string][] = [
[ts, 'typescript', typescriptZhTranslator, 'envLock.ts.txt'],
[ts, 'typescript', typescriptZhTranslator, 'issueLock.ts.txt'],
[ts, 'typescript', typescriptZhTranslator, 'classes.ts.txt'],
[py, 'python', pythonZhTranslator, 'sample.py'],
[py, 'python', pythonZhTranslator, 'routes.py'],
[py, 'python', pythonZhTranslator, 'classes.py'],
]
for (const [adapter, languageId, translator, name] of samples) {
const source = readFixture(name)