✨ feat(vscode): 代码阅读把字典、对象字面量的值展开成键值树
This commit is contained in:
1 parent
51f2ef7bfa
commit
4930dc0d42
20 files changed
+578
-103
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import type { LanguageAdapter, PropertyRow, Segment, Translator } from '../contract'
|
||||
import type { LanguageAdapter, PropertyItem, PropertyRow, Segment, Translator } from '../contract'
|
||||
import type { ZhTable } from './zhTemplate'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createPythonAdapter } from '../languages/python'
|
||||
@@ -362,7 +362,7 @@ describe('属性表', () => {
|
||||
expect(env[15]).toEqual(['名称: ENV_PATTERN', '访问级别: 私有', '值: \'**/.env*\''])
|
||||
expect(env[27]).toEqual(['名称: include', '初始值: 新建 RelativePattern(folder, ENV_PATTERN)'])
|
||||
expect(env[28]).toEqual(['名称: uris', '初始值: 等待 workspace.findFiles(include, EXCLUDE_PATTERN)'])
|
||||
expect(env[40]).toEqual(['名称: result', '类型: EnvLockResult', '初始值: { total: files.length, ok: 空数组, failed: 空数组 }'])
|
||||
expect(env[40]).toEqual(['名称: result', '类型: EnvLockResult', '初始值: 对象 3 项', ' total | files.length', ' ok | 空数组', ' failed | 空数组'])
|
||||
expect(issue[1]).toEqual(['来源: \'../gitea/types\'', '仅类型: 是', '导入项: 1 个', ' Issue'])
|
||||
expect(issue[14]).toEqual(['名称: issue', '初始值: issues.find(i => i.number 全等 issueNumber)'])
|
||||
expect(routes[19]).toEqual([
|
||||
@@ -620,6 +620,86 @@ describe('属性表', () => {
|
||||
expect(rows[1]).toEqual(['名称: i', '访问级别: 私有', '初始值: 0', '名称: n', '访问级别: 私有', '类型: 数值', '初始值: 无'])
|
||||
expect(rows[3]).toEqual(['名称: v', '初始值: useMemo(() =>'])
|
||||
})
|
||||
|
||||
it('字典:值写「字典 N 项」,每项一个子行「键 | 值」,嵌套的字典一层层往下展开;**展开的键写「展开」,空字典照旧一行', async () => {
|
||||
const source = [
|
||||
'_CONTEST_STATUS_KEYS = {',
|
||||
' "published": "contests:publish",',
|
||||
' "ended": "contests:end",',
|
||||
' "draft": "contests:update",',
|
||||
'}',
|
||||
'CONFIG = {"db": {"host": host, "opts": {}}, **defaults, 1: await load()}',
|
||||
'EMPTY = {}',
|
||||
].join('\n')
|
||||
const rows = rowTexts(await describeAll(py, 'python', pythonZhTranslator, source))
|
||||
expect(rows[1]).toEqual([
|
||||
'名称: _CONTEST_STATUS_KEYS',
|
||||
'访问级别: 私有',
|
||||
'初始值: 字典 3 项',
|
||||
' "published" | "contests:publish"',
|
||||
' "ended" | "contests:end"',
|
||||
' "draft" | "contests:update"',
|
||||
])
|
||||
expect(rows[6]).toEqual([
|
||||
'名称: CONFIG',
|
||||
'访问级别: 公共',
|
||||
'值: 字典 3 项',
|
||||
' "db" | 字典 2 项',
|
||||
' "host" | host',
|
||||
' "opts" | 空字典',
|
||||
' 展开 | defaults',
|
||||
' 1 | 等待 load()',
|
||||
])
|
||||
expect(rows[7]).toEqual(['名称: EMPTY', '访问级别: 公共', '值: 空字典'])
|
||||
})
|
||||
|
||||
it('对象:值写「对象 N 项」,键照源码写法(计算属性名照常翻),简写的值就是它自己,方法的值是它的头部,嵌套的一层层往下展开;空对象一行「空对象」', async () => {
|
||||
const source = [
|
||||
'export const handlers = {',
|
||||
' total: files.length,',
|
||||
' \'x-y\': await load(),',
|
||||
' [key]: !ok,',
|
||||
' files,',
|
||||
' ...rest,',
|
||||
' async load(id: string): Promise<void> {',
|
||||
' await run(id)',
|
||||
' },',
|
||||
' nested: { a: [], b: { c: 1 } },',
|
||||
'}',
|
||||
'let none = {}',
|
||||
].join('\n')
|
||||
const described = await describeAll(ts, 'typescript', typescriptZhTranslator, source)
|
||||
const rows = rowTexts(described)
|
||||
expect(rows[1]).toEqual([
|
||||
'名称: handlers',
|
||||
'访问级别: 公共',
|
||||
'值: 对象 7 项',
|
||||
' total | files.length',
|
||||
' \'x-y\' | 等待 load()',
|
||||
' [key] | 非 ok',
|
||||
' files | files',
|
||||
' 展开 | rest',
|
||||
' load | 异步 load(id: 字符串): Promise<无>',
|
||||
' nested | 对象 2 项',
|
||||
' a | 空数组',
|
||||
' b | 对象 1 项',
|
||||
' c | 1',
|
||||
])
|
||||
expect(rows[12]).toEqual(['名称: none', '访问级别: 私有', '初始值: 空对象'])
|
||||
|
||||
const value = colors(described.get(1) ?? []).find(row => row.label === '值')
|
||||
expect(value?.value).toEqual([{ text: '对象 7 项', role: 'hint' }])
|
||||
expect(value?.items?.map(item => item.cells[0])).toEqual([
|
||||
[{ text: 'total', role: 'variable' }],
|
||||
[{ text: '\'x-y\'', role: 'string' }],
|
||||
[{ text: '[', role: 'plain' }, { text: 'key', role: 'variable' }, { text: ']', role: 'plain' }],
|
||||
[{ text: 'files', role: 'variable' }],
|
||||
[{ text: '展开', role: 'hint' }],
|
||||
[{ text: 'load', role: 'function' }],
|
||||
[{ text: 'nested', role: 'variable' }],
|
||||
])
|
||||
expect(value?.items?.[6].cells[1]).toEqual([{ text: '对象 2 项', role: 'hint' }])
|
||||
})
|
||||
})
|
||||
|
||||
describe('源码下标(跳到定义用)', () => {
|
||||
@@ -633,7 +713,8 @@ describe('源码下标(跳到定义用)', () => {
|
||||
}
|
||||
|
||||
function propertySegments(rows: PropertyRow[]): Segment[] {
|
||||
return rows.flatMap(row => [...('markdown' in row.value ? [] : row.value), ...(row.items ?? []).flatMap(item => item.cells.flat())])
|
||||
const itemSegments = (items: PropertyItem[] | undefined): Segment[] => (items ?? []).flatMap(item => [...item.cells.flat(), ...itemSegments(item.items)])
|
||||
return rows.flatMap(row => [...('markdown' in row.value ? [] : row.value), ...itemSegments(row.items)])
|
||||
}
|
||||
|
||||
it('python:点分模块名每一节、导入项、别名各自带下标,点号不带;前面有中文注释也对得上', async () => {
|
||||
@@ -723,6 +804,37 @@ describe('源码下标(跳到定义用)', () => {
|
||||
expect(marked(tsSource, tsRows.get(3))).toEqual(['名称: i', '名称: j'])
|
||||
})
|
||||
|
||||
it('字典 / 对象字面量:键和值里的名字、字符串带下标,嵌套的也是;每项标出它讲的定义是键,展开没有', async () => {
|
||||
const source = [
|
||||
'const o = {',
|
||||
' total: files.length,',
|
||||
' "k": v,',
|
||||
' inner: { deep: x },',
|
||||
' ...rest,',
|
||||
' load() {},',
|
||||
'}',
|
||||
].join('\n')
|
||||
const at = (word: string): number => source.indexOf(word)
|
||||
const items = (await describeAll(ts, 'typescript', typescriptZhTranslator, source)).get(1)?.find(row => row.label === '值')?.items ?? []
|
||||
expect(items.map(item => item.cells.map(linked))).toEqual([
|
||||
[[['total', at('total')]], [['files', at('files')], ['length', at('length')]]],
|
||||
[[['"k"', at('"k"')]], [['v', at('v,')]]],
|
||||
[[['inner', at('inner')]], []],
|
||||
[[], [['rest', at('rest')]]],
|
||||
[[['load', at('load')]], [['load', at('load')]]],
|
||||
])
|
||||
expect(items[2].items?.map(item => item.cells.map(linked))).toEqual([[[['deep', at('deep')]], [['x', at('x }')]]]])
|
||||
expect(items.map(item => item.defines && source.slice(item.defines.startIndex, item.defines.endIndex))).toEqual(['total', '"k"', 'inner', undefined, 'load'])
|
||||
expect(items[2].items?.[0].defines).toEqual({ startIndex: at('deep'), endIndex: at('deep') + 'deep'.length })
|
||||
|
||||
const pySource = 'X = {"a": b, **c}'
|
||||
const pyItems = (await describeAll(py, 'python', pythonZhTranslator, pySource)).get(1)?.find(row => row.label === '值')?.items ?? []
|
||||
expect(pyItems.map(item => item.cells.map(linked))).toEqual([
|
||||
[[['"a"', pySource.indexOf('"a"')]], [['b', pySource.indexOf('b')]]],
|
||||
[[], [['c', pySource.indexOf('c')]]],
|
||||
])
|
||||
})
|
||||
|
||||
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)
|
||||
@@ -789,6 +901,7 @@ describe('中文模板引擎', () => {
|
||||
operators: {},
|
||||
operatorParents: [],
|
||||
emptyLiterals: {},
|
||||
mapLiterals: {},
|
||||
stringNodes: [],
|
||||
commentText: node => node.text,
|
||||
nodeTemplates: {},
|
||||
|
||||
Reference in new issue
Block a user