✨ feat(vscode): 代码阅读跨文件继承的 Pydantic 模型等索引查到后再换成数据模型卡片

This commit is contained in:
cruldra committed 2026-10-03 03:11:47 +08:00
1 parent 4d8b99db7b
commit 597ab94562
2 files changed
+109 -13

No files matched your search

+39 -4
View File
@@ -1,5 +1,5 @@
import type { BlockView } from '../blockView'
import type { PropertyRow, Segment } from '../contract'
import type { IndexEntry, PropertyRow, RuleContext, Segment } from '../contract'
import { describe, expect, it } from 'vitest'
import { fileAttributes } from '../attributes'
import { toBlockViews } from '../blockView'
@@ -11,7 +11,7 @@ import { celeryRule } from './celery'
import { dataclassRule } from './dataclass'
import { enumRule } from './enum'
import { fastapiRule } from './fastapi'
import { pydanticRule } from './pydantic'
import { MODEL_INDEX, pydanticRule } from './pydantic'
import { sqlalchemyRule } from './sqlalchemy'
const py = createPythonAdapter(WASM_DIR)
@@ -20,9 +20,13 @@ const SHARE = readFixture('createShareRequest.py')
/** 和扩展里注册的顺序一样。 */
const RULES = [sqlalchemyRule, celeryRule, enumRule, dataclassRule, pydanticRule, fastapiRule]
async function views(source: string, reports: string[] = []): Promise<BlockView[]> {
async function views(source: string, reports: string[] = [], lookup?: RuleContext['lookup']): Promise<BlockView[]> {
return withBlocks(py, 'python', source, blocks =>
toBlockViews(blocks, { source, language: py, markers: [] }, pythonZhTranslator, { rules: RULES, report: message => reports.push(message) }))
toBlockViews(blocks, { source, language: py, markers: [] }, pythonZhTranslator, { rules: RULES, report: message => reports.push(message), lookup }))
}
async function indexEntries(source: string): Promise<IndexEntry[]> {
return withBlocks(py, 'python', source, blocks => MODEL_INDEX.entries(blocks[0].node.tree.rootNode, { path: 'sample.py', source, exists: () => false }))
}
function properties(view: BlockView | undefined): PropertyRow[] {
@@ -204,6 +208,37 @@ describe('pydantic 库规则(Python)', () => {
expect(linkedSegments(properties(share)).map(segment => segment.text)).toEqual(expect.arrayContaining(['ShareTargetType']))
})
it('别的文件里的父类:索引认本地能确定的模型;查不到先当普通类,查到再换成数据模型卡片', async () => {
expect(MODEL_INDEX.mentions(AIGC)).toBe(true)
expect(MODEL_INDEX.mentions('from luffy_agent.model.aigc import AIGCMetadata\n\nclass Child(AIGCMetadata):\n x: int\n')).toBe(false)
const entries = await indexEntries(`${AIGC}\n\nclass Plain:\n x: str\n`)
expect(entries.map(entry => [entry.key, entry.value])).toEqual([['AIGCMetadata', 'model'], ['TextTrace', 'model']])
const alias = await indexEntries('from pydantic import BaseModel as Model\n\nclass Alias(Model):\n x: int\n')
expect(alias.map(entry => entry.key)).toEqual(['Alias'])
const child = [
'from luffy_agent.model.aigc import AIGCMetadata',
'',
'class Child(AIGCMetadata):',
' extra: int = 1',
'',
'class Other(NotAModel):',
' x: int',
'',
].join('\n')
const pending = await views(child)
expect(pending.map(view => view.kind)).toEqual(['import', 'class', 'class'])
const model = (index: { id: string }, node: { text: string }): boolean => index.id === MODEL_INDEX.id && node.text === 'AIGCMetadata'
const found = await views(child, [], (index, node) => model(index, node)
? { key: node.text, name: { startIndex: node.startIndex, endIndex: node.endIndex }, value: 'model' }
: undefined)
const card = named(found, 'Child')
expect(card.kind).toBe('pydantic.model')
expect(propertyLines(properties(card)).slice(-2)).toEqual(['字段: 1 个', ' extra | 整数 | 1 | '])
expect(named(found, 'Other').kind).toBe('class')
})
it('给模型加一个字段、改一个字段的默认值:字段行标新增,默认值那一格标改过', async () => {
const after = AIGC
.replace(' ReservedCode2: str\n', ' ReservedCode2: str\n Extra: str\n')