✨ feat(vscode): 代码阅读函数、导入、常量、变量块预览改成属性表
This commit is contained in:
1 parent
0c4c9b0fba
commit
dd9b39ac5d
19 files changed
+1458
-213
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import type { Outline } from '../testSupport'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { outline, readFixture, WASM_DIR, withBlocks } from '../testSupport'
|
||||
import { factsByLine, outline, readFixture, WASM_DIR, withBlocks } from '../testSupport'
|
||||
import { createPythonAdapter } from './python'
|
||||
|
||||
const adapter = createPythonAdapter(WASM_DIR)
|
||||
@@ -9,6 +9,10 @@ function tree(source: string): Promise<Outline[]> {
|
||||
return withBlocks(adapter, 'python', source, outline)
|
||||
}
|
||||
|
||||
function facts(source: string): Promise<Record<number, unknown>> {
|
||||
return withBlocks(adapter, 'python', source, factsByLine)
|
||||
}
|
||||
|
||||
// fixtures/sample.py:行号 1 起
|
||||
const SAMPLE: Outline[] = [
|
||||
{ at: 1, kind: 'comment' },
|
||||
@@ -86,3 +90,112 @@ describe('python 代码块树', () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('python 属性表信息(facts)', () => {
|
||||
it('sample.py:import / from import、模块和类里的赋值、函数和方法', async () => {
|
||||
const sample = await facts(readFixture('sample.py'))
|
||||
expect(sample[3]).toEqual({ sources: ['os'], items: [{ kind: 'module', module: 'os' }] })
|
||||
expect(sample[4]).toEqual({ sources: ['pathlib'], items: [{ kind: 'named', name: 'Path', alias: 'P' }] })
|
||||
expect(sample[6]).toEqual({ access: 'public', declarators: [{ name: 'MAX_DEPTH', value: '8' }] })
|
||||
expect(sample[10]).toEqual({
|
||||
name: 'find_env_files',
|
||||
access: 'public',
|
||||
async: false,
|
||||
decorators: [],
|
||||
parameters: [{ name: 'root', optional: false, type: 'str' }],
|
||||
returnType: 'list[str]',
|
||||
})
|
||||
expect(sample[17]).toEqual({ declarators: [{ name: 'found', value: 'found[:MAX_DEPTH]' }] })
|
||||
expect(sample[37]).toEqual({ access: 'public', declarators: [{ name: 'label', value: '\'env\'' }] })
|
||||
expect(sample[39]).toMatchObject({
|
||||
name: 'summary',
|
||||
access: 'public',
|
||||
parameters: [{ name: 'self', optional: false }, { name: 'verbose', optional: false, type: 'bool', default: 'False' }],
|
||||
returnType: 'str',
|
||||
})
|
||||
})
|
||||
|
||||
it('routes.py:future 导入、多行括号导入、带装饰器的 async def、await 赋值', async () => {
|
||||
const routes = await facts(readFixture('routes.py'))
|
||||
expect(routes[6]).toEqual({ sources: ['__future__'], items: [{ kind: 'named', name: 'annotations' }] })
|
||||
expect(routes[19]).toEqual({
|
||||
sources: ['.schemas'],
|
||||
items: [
|
||||
{ kind: 'named', name: 'UserAvatarUploadResponse' },
|
||||
{ kind: 'named', name: 'UserDetailResponse' },
|
||||
{ kind: 'named', name: 'UserUpdateRequest' },
|
||||
],
|
||||
})
|
||||
expect(routes[29]).toEqual({
|
||||
name: 'get_user',
|
||||
access: 'public',
|
||||
async: true,
|
||||
decorators: ['@router.get("/{user_id}", response_model=UserDetailResponse)'],
|
||||
parameters: [
|
||||
{ name: 'user_id', optional: false, type: 'Int4Path' },
|
||||
{ name: 'db', optional: false, type: 'AsyncSession', default: 'Depends(get_db)' },
|
||||
{ name: 'current_user', optional: false, type: 'User', default: 'Depends(get_current_user)' },
|
||||
],
|
||||
returnType: 'UserDetailResponse',
|
||||
})
|
||||
expect(routes[118]).toEqual({ declarators: [{ name: 'payload', value: 'await file.read()' }] })
|
||||
})
|
||||
|
||||
it('导入:import a.b as c, d 每个模块一个来源;from . import、from m import *', async () => {
|
||||
const source = [
|
||||
'import os.path as osp, sys',
|
||||
'from . import x',
|
||||
'from m import *',
|
||||
].join('\n')
|
||||
expect(await facts(source)).toEqual({
|
||||
1: { sources: ['os.path', 'sys'], items: [{ kind: 'module', module: 'os.path', alias: 'osp' }, { kind: 'module', module: 'sys' }] },
|
||||
2: { sources: ['.'], items: [{ kind: 'named', name: 'x' }] },
|
||||
3: { sources: ['m'], items: [{ kind: 'namespace' }] },
|
||||
})
|
||||
})
|
||||
|
||||
it('形参:*args / **kwargs 带前缀,* 和 / 分隔符不算参数', async () => {
|
||||
const found = await facts('async def f(a, /, b: int, *args: int, c=1, d: str = \'x\', **kw) -> None:\n pass')
|
||||
expect(found[1]).toEqual({
|
||||
name: 'f',
|
||||
access: 'public',
|
||||
async: true,
|
||||
decorators: [],
|
||||
parameters: [
|
||||
{ name: 'a', optional: false },
|
||||
{ name: 'b', optional: false, type: 'int' },
|
||||
{ name: '*args', optional: false, type: 'int' },
|
||||
{ name: 'c', optional: false, default: '1' },
|
||||
{ name: 'd', optional: false, type: 'str', default: '\'x\'' },
|
||||
{ name: '**kw', optional: false },
|
||||
],
|
||||
returnType: 'None',
|
||||
})
|
||||
})
|
||||
|
||||
it('访问级别:单下划线开头私有、__dunder__ 公共;解构全是私有名才私有;函数体里的没有', async () => {
|
||||
const source = [
|
||||
'_cache = {}',
|
||||
'__all__ = [\'f\']',
|
||||
'a, _b = 1, 2',
|
||||
'_c, _d = 1, 2',
|
||||
'total: int',
|
||||
'class K:',
|
||||
' def __init__(self):',
|
||||
' self.x = 1',
|
||||
' def _helper(self):',
|
||||
' def inner():',
|
||||
' pass',
|
||||
].join('\n')
|
||||
const found = await facts(source)
|
||||
expect(found[1]).toMatchObject({ access: 'private' })
|
||||
expect(found[2]).toMatchObject({ access: 'public' })
|
||||
expect(found[3]).toEqual({ access: 'public', declarators: [{ name: 'a, _b', value: '1, 2' }] })
|
||||
expect(found[4]).toMatchObject({ access: 'private' })
|
||||
expect(found[5]).toEqual({ access: 'public', declarators: [{ name: 'total', type: 'int' }] })
|
||||
expect(found[7]).toMatchObject({ name: '__init__', access: 'public' })
|
||||
expect(found[8]).toEqual({ declarators: [{ name: 'self.x', value: '1' }] })
|
||||
expect(found[9]).toMatchObject({ name: '_helper', access: 'private' })
|
||||
expect(found[10]).not.toHaveProperty('access')
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user