✨ feat(vscode): 代码阅读函数、导入、常量、变量块预览改成属性表

This commit is contained in:
cruldra committed 2026-09-25 04:56:22 +08:00
1 parent 0c4c9b0fba
commit dd9b39ac5d
19 files changed
+1458 -213

No files matched your search

+114 -1
View File
@@ -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')
})
})