✨ feat(vscode): 代码阅读预览里跨几行写的列表按项拆开,列表里整项是调用的带着自己的实参表,一层层能往下套
This commit is contained in:
1 parent
7d6e7592c9
commit
2c490cac2d
22 files changed
+848
-314
No files matched your search
@@ -1336,7 +1336,8 @@ describe('跨几行写的调用', () => {
|
||||
expect(value(1)).toBe('初始值: (调用 make · 1 个参数)[0]')
|
||||
expect(value(4)).toBe('初始值: (调用 make · 1 个参数) 或者 fallback')
|
||||
expect(value(7)).toBe('初始值: fallback 或者 调用 make · 1 个参数')
|
||||
expect(value(10)).toBe('初始值: [ 调用 make · 1 个参数, b ]')
|
||||
// 跨几行写的列表按项拆开(见「跨几行写的列表」),整项是调用的不括
|
||||
expect(propertyLines(values.get(10) ?? []).slice(2)).toEqual(['初始值: 列表 2 项', ' 第 1 项 | 调用 make · 1 个参数', ' 第 1 个 | a', ' 第 2 项 | b'])
|
||||
const cell = (await translateAll(py, 'python', pythonZhTranslator, source)).get(16)
|
||||
expect(tableLines(cell?.table)).toEqual(['key | (调用 Model · 1 个参数).model_dump()', ' a | 1'])
|
||||
})
|
||||
@@ -1398,6 +1399,183 @@ describe('跨几行写的调用', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('跨几行写的列表', () => {
|
||||
/** Filter 的定义是类,别的名字还没查到。 */
|
||||
const calleeKind = (call: CallFacts): BlockKind | undefined => call.callee.text === 'Filter' ? 'class' : undefined
|
||||
|
||||
const FILTERS = [
|
||||
'async def list_sessions(archived: bool, current_user: User):',
|
||||
' filters = [',
|
||||
' Filter(field="user_id", operator=FilterOperator.EQ, value=current_user.id),',
|
||||
' Filter(',
|
||||
' field="archived_at",',
|
||||
' operator=FilterOperator.NE if archived else FilterOperator.EQ,',
|
||||
' value=None,',
|
||||
' ),',
|
||||
' ]',
|
||||
].join('\n')
|
||||
|
||||
it('初始值写「列表 N 项」,每项一个子行「第 N 项 | 值」;整项是调用的写成一行还是几行都排成「新建 / 调用 …」,实参表跟着这一项', async () => {
|
||||
const rows = (await describeAll(py, 'python', pythonZhTranslator, FILTERS, calleeKind)).get(2) ?? []
|
||||
expect(propertyLines(rows)).toEqual([
|
||||
'名称: filters',
|
||||
'初始值: 列表 2 项',
|
||||
' 第 1 项 | 新建 Filter · 3 个参数',
|
||||
' field | "user_id"',
|
||||
' operator | FilterOperator.EQ',
|
||||
' value | current_user.id',
|
||||
' 第 2 项 | 新建 Filter · 3 个参数',
|
||||
' field | "archived_at"',
|
||||
' operator | 如果 archived 则 FilterOperator.NE 否则 FilterOperator.EQ',
|
||||
' value | 空',
|
||||
])
|
||||
const value = rows.find(row => row.label === '初始值')
|
||||
expect(value?.columns).toBeUndefined()
|
||||
expect(colors(value?.value)).toEqual([{ text: '列表 2 项', role: 'hint' }])
|
||||
const [first, second] = value?.items ?? []
|
||||
expect(first?.cells[0]).toEqual([{ text: '第 1 项', role: 'hint' }])
|
||||
expect(first).not.toHaveProperty('items')
|
||||
expect(first?.table?.columns).toEqual(['参数', '值'])
|
||||
expect(second?.table?.columns).toEqual(['参数', '值'])
|
||||
// 拆开以后名字照样带源码下标(跳到定义、悬停、中文名、出现处高亮都靠它):被调用的名字、按名字传的名字、值里的名字
|
||||
const multiline = FILTERS.indexOf('Filter(\n')
|
||||
expect(inlineOf(second?.cells[1] ?? []).find(piece => 'role' in piece && piece.text === 'Filter')).toEqual({ text: 'Filter', role: 'type', offset: multiline })
|
||||
expect(second?.table?.items[0].cells[0]).toEqual([{ text: 'field', role: 'variable', offset: FILTERS.indexOf('field="archived_at"') }])
|
||||
const user = FILTERS.indexOf('current_user.id')
|
||||
expect(linked(first?.table?.items[2].cells[1])).toEqual([['current_user', user], ['id', user + 13]])
|
||||
})
|
||||
|
||||
it('写在一行里的列表照旧一行译文,里面的调用也照旧;空列表写「空列表」', async () => {
|
||||
const source = 'ids = [a, Filter(field="x")]\nempty = []'
|
||||
const rows = await describeAll(py, 'python', pythonZhTranslator, source, calleeKind)
|
||||
expect(propertyLines(rows.get(1) ?? [])).toEqual(['名称: ids', '访问级别: 公共', '初始值: [a, Filter(field="x")]'])
|
||||
expect(propertyLines(rows.get(2) ?? [])).toEqual(['名称: empty', '访问级别: 公共', '初始值: 空列表'])
|
||||
})
|
||||
|
||||
it('元组、集合各用自己的叫法,*x 写「展开」、「第 N 项」只数不是展开的;项是字典照字典拆,是跨几行的列表接着拆,一层层往下套', async () => {
|
||||
const source = [
|
||||
'PAIR = (',
|
||||
' first,',
|
||||
' *rest,',
|
||||
' second,',
|
||||
')',
|
||||
'TAGS = {',
|
||||
' "a",',
|
||||
' "b",',
|
||||
'}',
|
||||
'CONFIG = [',
|
||||
' {"name": "a", "tags": [',
|
||||
' "x",',
|
||||
' "y",',
|
||||
' ]},',
|
||||
' [1, 2],',
|
||||
' [',
|
||||
' 3,',
|
||||
' 4,',
|
||||
' ],',
|
||||
']',
|
||||
].join('\n')
|
||||
const rows = await describeAll(py, 'python', pythonZhTranslator, source)
|
||||
expect(propertyLines(rows.get(1) ?? []).slice(2)).toEqual(['值: 元组 3 项', ' 第 1 项 | first', ' 展开 | rest', ' 第 2 项 | second'])
|
||||
expect(propertyLines(rows.get(6) ?? []).slice(2)).toEqual(['值: 集合 2 项', ' 第 1 项 | "a"', ' 第 2 项 | "b"'])
|
||||
expect(propertyLines(rows.get(10) ?? []).slice(2)).toEqual([
|
||||
'值: 列表 3 项',
|
||||
' 第 1 项 | 字典 2 项',
|
||||
' "name" | "a"',
|
||||
' "tags" | 列表 2 项',
|
||||
' 第 1 项 | "x"',
|
||||
' 第 2 项 | "y"',
|
||||
' 第 2 项 | [1, 2]',
|
||||
' 第 3 项 | 列表 2 项',
|
||||
' 第 1 项 | 3',
|
||||
' 第 2 项 | 4',
|
||||
])
|
||||
// 字典的项照旧标出它讲的定义(键),列表的项不讲哪个定义
|
||||
const config = rows.get(10)?.find(row => row.label === '值')
|
||||
expect(config?.items?.[0]).not.toHaveProperty('defines')
|
||||
const tags = source.indexOf('"tags"')
|
||||
expect(config?.items?.[0].items?.[1].defines).toEqual({ startIndex: tags, endIndex: tags + 6 })
|
||||
})
|
||||
|
||||
it('字典一项的值是跨几行写的列表时也按项拆', async () => {
|
||||
const source = 'ROUTES = {"admin": [\n "users",\n "roles",\n]}'
|
||||
expect(propertyLines((await describeAll(py, 'python', pythonZhTranslator, source)).get(1) ?? []).slice(2)).toEqual([
|
||||
'值: 字典 1 项',
|
||||
' "admin" | 列表 2 项',
|
||||
' 第 1 项 | "users"',
|
||||
' 第 2 项 | "roles"',
|
||||
])
|
||||
})
|
||||
|
||||
it('实参的值是跨几行写的列表时也按项拆,嵌在那一格下面;写在一行里的列表、实参里的字典照旧一行', async () => {
|
||||
const source = [
|
||||
'session.query(',
|
||||
' filters=[',
|
||||
' Filter(field="a"),',
|
||||
' other,',
|
||||
' ],',
|
||||
' ids=[1, 2],',
|
||||
' meta={"a": 1},',
|
||||
')',
|
||||
].join('\n')
|
||||
const line = (await translateAll(py, 'python', pythonZhTranslator, source, calleeKind)).get(1)
|
||||
expect(text(line?.segments)).toBe('调用 session.query · 3 个参数')
|
||||
expect(tableLines(line?.table)).toEqual([
|
||||
'filters | 列表 2 项',
|
||||
' 第 1 项 | 新建 Filter · 1 个参数',
|
||||
' field | "a"',
|
||||
' 第 2 项 | other',
|
||||
'ids | [1, 2]',
|
||||
'meta | {"a": 1}',
|
||||
])
|
||||
expect(line?.table?.items[0].items?.[0].table?.columns).toEqual(['参数', '值'])
|
||||
})
|
||||
|
||||
it('typeScript:数组写「数组 N 项」,new 是新建、别的调用是调用,整项是调用的都排开;...x 写「展开」,对象照对象拆;实参的值是跨几行的数组也拆', async () => {
|
||||
const source = [
|
||||
'const filters = [',
|
||||
' new Filter({ field: \'user_id\' }),',
|
||||
' makeFilter(',
|
||||
' \'archived_at\',',
|
||||
' archived,',
|
||||
' ),',
|
||||
' ...rest,',
|
||||
' [1, 2],',
|
||||
' {',
|
||||
' a: 1,',
|
||||
' },',
|
||||
']',
|
||||
'const ids = [a, f(b)]',
|
||||
'register(',
|
||||
' \'x\',',
|
||||
' [',
|
||||
' a,',
|
||||
' b,',
|
||||
' ],',
|
||||
')',
|
||||
].join('\n')
|
||||
const rows = await describeAll(ts, 'typescript', typescriptZhTranslator, source)
|
||||
expect(propertyLines(rows.get(1) ?? [])).toEqual([
|
||||
'名称: filters',
|
||||
'访问级别: 私有',
|
||||
'值: 数组 5 项',
|
||||
' 第 1 项 | 新建 Filter · 1 个参数',
|
||||
' 第 1 个 | { field: \'user_id\' }',
|
||||
' 第 2 项 | 调用 makeFilter · 2 个参数',
|
||||
' 第 1 个 | \'archived_at\'',
|
||||
' 第 2 个 | archived',
|
||||
' 展开 | rest',
|
||||
' 第 3 项 | [1, 2]',
|
||||
' 第 4 项 | 对象 1 项',
|
||||
' a | 1',
|
||||
])
|
||||
expect(propertyLines(rows.get(13) ?? []).at(-1)).toBe('值: [a, f(b)]')
|
||||
const line = (await translateAll(ts, 'typescript', typescriptZhTranslator, source)).get(14)
|
||||
expect(text(line?.segments)).toBe('调用 register · 2 个参数')
|
||||
expect(tableLines(line?.table)).toEqual(['第 1 个 | \'x\'', '第 2 个 | 数组 2 项', ' 第 1 项 | a', ' 第 2 项 | b'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('中文模板引擎', () => {
|
||||
function testTranslator(table: Partial<ZhTable>): Translator {
|
||||
return createZhTemplateTranslator({
|
||||
@@ -1411,7 +1589,7 @@ describe('中文模板引擎', () => {
|
||||
operators: {},
|
||||
operatorParents: [],
|
||||
emptyLiterals: {},
|
||||
mapLiterals: {},
|
||||
collections: {},
|
||||
stringNodes: [],
|
||||
commentText: node => node.text,
|
||||
nodeTemplates: {},
|
||||
|
||||
Reference in new issue
Block a user