✨ 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

+123
View File
@@ -0,0 +1,123 @@
import type { IndexEntry, RuleContext, WorkspaceIndex } from './contract'
import type { DefinitionStart } from './nameResolution'
import type { IndexedEntry } from './workspaceIndex'
import { RETRY_DELAYS } from './nameResolution'
/**
* 库规则认领时查的名字(RuleContext.lookup):名字的定义在规则的工作区索引里是哪一项。
* 同「中文名」查别的文件里的定义:索引里有同名的项才问语言扩展定义在哪,定义处正是索引里那一项才算。这里不碰 VS Code。
*/
/** 规则问的一个名字:哪个索引、名字原文,和名字在源码里的下标(问语言扩展定义用)。 */
export interface LookupRequest { index: WorkspaceIndex, key: string, offset: number }
/** 查规则问的名字要问的:工作区索引里认出的各项,这份文档里 offset 处名字的定义(语言扩展)。 */
export interface LookupSources {
find: (index: WorkspaceIndex, key: string) => Promise<IndexedEntry[]>
definitions: (offset: number) => Promise<DefinitionStart[]>
}
/** 排一遍视图数据用的:交给规则的 lookup,和这一遍规则问了哪些名字。 */
export interface LookupSession {
lookup: RuleContext['lookup']
requests: () => LookupRequest[]
}
function idOf(index: WorkspaceIndex, key: string): string {
return `${index.id}\n${key}`
}
/** 查到的项对规则来说一样不一样:规则只看键和值。 */
function same(a: IndexEntry | null | undefined, b: IndexEntry | null | undefined): boolean {
return a?.key === b?.key && a?.value === b?.value
}
/**
* 一份打开着的文档里库规则问的名字查到了什么:按「索引 id + 名字原文」记,同一份文档里同名的名字按一处查。
* 排视图数据是同步的:先交出已经查到的(文档改了也照旧用上一版查到的),记下这一遍问了什么;发出预览后再查,
* 每个版本同一个名字查一次(索引里有同名的项、语言扩展却没答时照 RETRY_DELAYS 隔一会儿再查),查到的和已知的不一样就 changed,
* 宿主按新结果重排、重发。
*/
export class DocumentLookups {
/** 查到的:没有这个定义是 null */
private readonly known = new Map<string, IndexEntry | null>()
private version: number | undefined
/** 眼下这个版本查过(或正在查)的名字 */
private readonly checked = new Set<string>()
private timer: ReturnType<typeof setTimeout> | undefined
constructor(
private readonly changed: () => void,
private readonly delays: readonly number[] = RETRY_DELAYS,
private readonly schedule: (run: () => void, ms: number) => ReturnType<typeof setTimeout> = setTimeout,
) {}
session(): LookupSession {
const requests = new Map<string, LookupRequest>()
return {
lookup: (index, name) => {
const id = idOf(index, name.text)
if (!requests.has(id))
requests.set(id, { index, key: name.text, offset: name.startIndex })
return this.known.get(id) ?? undefined
},
requests: () => [...requests.values()],
}
}
/** 查 version 这一版规则问的名字:这个版本查过的不再查。 */
resolve(version: number, requests: readonly LookupRequest[], sources: LookupSources): void {
if (this.version !== version) {
clearTimeout(this.timer)
this.version = version
this.checked.clear()
}
const fresh = requests.filter(request => !this.checked.has(idOf(request.index, request.key)))
for (const request of fresh)
this.checked.add(idOf(request.index, request.key))
if (fresh.length === 0)
return
const attempt = (tries: number): void => {
void this.check(fresh, sources).then(({ settled, changed }) => {
if (this.version !== version)
return
if (changed)
this.changed()
if (!settled && tries < this.delays.length)
this.timer = this.schedule(() => attempt(tries + 1), this.delays[tries])
})
}
attempt(0)
}
dispose(): void {
clearTimeout(this.timer)
this.version = undefined
}
/**
* 查一遍:索引里没有同名的项就是没有这个定义;有就问语言扩展,定义处正是其中一项才算。
* 语言扩展没答(还没起来)的名字留着已知的不动,算没查完(settled 为 false)。
*/
private async check(requests: readonly LookupRequest[], sources: LookupSources): Promise<{ settled: boolean, changed: boolean }> {
let settled = true
let changed = false
await Promise.all(requests.map(async ({ index, key, offset }) => {
const id = idOf(index, key)
const candidates = await sources.find(index, key)
let found: IndexEntry | null = null
if (candidates.length > 0) {
const definitions = await sources.definitions(offset)
if (definitions.length === 0) {
settled = false
return
}
found = candidates.find(({ location }) => definitions.some(at => at.uri === location.uri && at.line === location.start.line && at.character === location.start.character))?.entry ?? null
}
if (!same(this.known.get(id), found))
changed = true
this.known.set(id, found)
}))
return { settled, changed }
}
}