✨ feat(vscode): 代码阅读加 FastAPI 接口卡片:请求表(URL、方法、路径 / 查询参数、请求头、Cookie、请求体)、按状态码的响应表(成功、responses、函数里抛的异常、自动带的 422);别的文件里的异常状态码、类型别名经工作区索引和语言扩展查

This commit is contained in:
cruldra committed 2026-09-27 20:19:24 +08:00
1 parent 1dfc0c7b1e
commit 0798f7d1c7
18 files changed
+1745 -28

No files matched your search

+32 -6
View File
@@ -1,11 +1,13 @@
import type { CustomTextEditorProvider, Disposable, TextDocument, TextDocumentShowOptions, Uri, WebviewPanel } from 'vscode'
import type { ClaudeImage } from '../../cc/spawnClaude'
import type { BlockView } from '../blockView'
import type { RuleLocation, RuleReference } from '../contract'
import type { IndexedName, NameSources } from '../nameResolution'
import type { RuleLocation, RuleReference, WorkspaceIndex } from '../contract'
import type { DefinitionStart, IndexedName, NameSources } from '../nameResolution'
import type { CodeQa } from '../qa/codeQa'
import type { CodeReaderRegistry } from '../registry'
import type { LookupSources } from '../ruleLookups'
import type { TreeSitterRuntime } from '../runtime'
import type { IndexedEntry } from '../workspaceIndex'
import type { BlockFilter } from './blockFilter'
import type { Definition, FoundLocation } from './definitions'
import type { ExtensionToWebview, Reveal, WebviewToExtension } from './messages'
@@ -16,6 +18,7 @@ import { blockReplacement } from '../editText'
import { locate } from '../folderStore'
import { DocumentNames, resolveNames } from '../nameResolution'
import { askPrompt, followUpPrompt } from '../qa/prompt'
import { DocumentLookups } from '../ruleLookups'
import { definitionAction } from './definitionAction'
import { findDefinitions, languageDefinitions, pickDefinition, ruleDefinitions } from './definitions'
import { DefinitionSources } from './definitionSources'
@@ -50,6 +53,8 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
readonly qa: CodeQa,
/** 工作区里别的文件定义的、原名叫 name 的中文名(languageId 是查的这份文档的语言) */
readonly indexedNames: (languageId: string, name: string) => Promise<IndexedName[]>,
/** 库规则的工作区索引里键是 key 的各项(规则查名字的定义时用,见 ruleLookups.ts) */
readonly indexed: (index: WorkspaceIndex, key: string) => Promise<IndexedEntry[]>,
) {}
resolveCustomTextEditor(document: TextDocument, panel: WebviewPanel): void {
@@ -124,6 +129,8 @@ class Preview {
private sent: { blocks: BlockView[], source: string } | undefined
/** 这份文档各版本显示成中文名的名字 */
private readonly names: DocumentNames
/** 库规则问的名字的定义查到了什么:查到的变了就重发文档 */
private readonly lookups: DocumentLookups
constructor(
private readonly document: TextDocument,
@@ -146,6 +153,7 @@ class Preview {
if (this.ready && version === document.version)
this.post({ type: 'displayNames', version, names })
})
this.lookups = new DocumentLookups(() => this.schedulePush())
this.disposables = [
panel.webview.onDidReceiveMessage((msg: WebviewToExtension) => this.handleMessage(msg)),
panel.onDidChangeViewState(() => {
@@ -214,9 +222,10 @@ class Preview {
const request = ++this.latest
const { languageId, version } = this.document
const source = this.document.getText()
const session = this.lookups.session()
const { message, displayNames } = await documentMessage({ languageId, version, getText: () => source }, this.registry, this.runtime, (text) => {
logger.add({ level: 'warn', source: 'codeReader', message: `${this.document.uri.fsPath}:${text}` })
})
}, session.lookup)
if (request !== this.latest || !this.ready)
return
this.definitions.remember(message.version, message.blocks)
@@ -225,8 +234,17 @@ class Preview {
this.pendingReveal = undefined
this.post({ ...message, names: this.names.known(version), ...(reveal && { reveal }) })
// 文档在解析期间又改了的,等下一次发文档再查
if (version === this.document.version)
if (version === this.document.version) {
this.names.resolve(version, report => resolveNames(displayNames, message.blocks, this.nameSources(), report))
this.lookups.resolve(version, session.requests(), this.lookupSources())
}
}
/** 这份文档里 offset 处名字的定义(语言扩展查的)落在哪儿。 */
private async definitionStarts(offset: number): Promise<DefinitionStart[]> {
const { document } = this
return (await languageDefinitions(document.uri, document.positionAt(offset)))
.map(({ uri, range }) => ({ uri: uri.toString(), line: range.start.line, character: range.start.character }))
}
/** 找中文名落在哪些名字上时问的:这份文档的语言扩展、工作区里的中文名索引。 */
@@ -235,12 +253,19 @@ class Preview {
return {
uri: document.uri.toString(),
occurrences: async offset => (await occurrencesOf(document, offset)).map(({ start, end }) => ({ start, end })),
definitions: async offset => (await languageDefinitions(document.uri, document.positionAt(offset)))
.map(({ uri, range }) => ({ uri: uri.toString(), line: range.start.line, character: range.start.character })),
definitions: offset => this.definitionStarts(offset),
indexed: name => this.owner.indexedNames(document.languageId, name),
}
}
/** 查库规则问的名字时问的:规则的工作区索引、这份文档的语言扩展。 */
private lookupSources(): LookupSources {
return {
find: (index, key) => this.owner.indexed(index, key),
definitions: offset => this.definitionStarts(offset),
}
}
/** 跳到高亮着的名字的下一处 / 上一处出现;页面不在就算了(页面重新加载时高亮已经没了)。 */
moveOccurrence(step: 1 | -1): void {
if (this.ready)
@@ -258,6 +283,7 @@ class Preview {
dispose(): void {
clearTimeout(this.timer)
this.names.dispose()
this.lookups.dispose()
for (const disposable of this.disposables)
disposable.dispose()
}