✨ feat(vscode): 代码阅读认出代码里起的中文名(Annotated[…, Comment("…")]、@Comment 装饰器、行尾 # @Comment、docstring / JSDoc 的参数说明),预览里定义和用到它的各处都写中文名,悬停看原名,定义处后面淡淡写原名

This commit is contained in:
cruldra committed 2026-09-27 00:56:39 +08:00
1 parent 1b1299dc1f
commit 44c221ea2b
45 files changed
+1685 -128

No files matched your search

+32 -4
View File
@@ -2,6 +2,7 @@ import type { CustomTextEditorProvider, Disposable, TextDocument, TextDocumentSh
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 { CodeQa } from '../qa/codeQa'
import type { CodeReaderRegistry } from '../registry'
import type { TreeSitterRuntime } from '../runtime'
@@ -13,6 +14,7 @@ import { logger } from '../../logging/logger'
import { webviewHtml, webviewUiRoot } from '../../webview/html'
import { blockReplacement } from '../editText'
import { locate } from '../folderStore'
import { DocumentNames, resolveNames } from '../nameResolution'
import { askPrompt, followUpPrompt } from '../qa/prompt'
import { definitionAction } from './definitionAction'
import { findDefinitions, languageDefinitions, pickDefinition, ruleDefinitions } from './definitions'
@@ -46,6 +48,8 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
readonly resolveReference: (reference: RuleReference) => Promise<RuleLocation[]>,
/** 问 AI:各预览共用,同一份文件开在几处时问答、正在答的问题都一样 */
readonly qa: CodeQa,
/** 工作区里别的文件定义的、原名叫 name 的中文名(languageId 是查的这份文档的语言) */
readonly indexedNames: (languageId: string, name: string) => Promise<IndexedName[]>,
) {}
resolveCustomTextEditor(document: TextDocument, panel: WebviewPanel): void {
@@ -118,6 +122,8 @@ class Preview {
visibleLine: number | undefined
/** 最近一次发出去的块树和它的源码:问 AI 时按块的记号在里面找那块代码 */
private sent: { blocks: BlockView[], source: string } | undefined
/** 这份文档各版本显示成中文名的名字 */
private readonly names: DocumentNames
constructor(
private readonly document: TextDocument,
@@ -136,6 +142,10 @@ class Preview {
rule: async reference => ruleDefinitions(await owner.resolveReference(reference)),
language: offset => languageDefinitions(document.uri, document.positionAt(offset)),
})
this.names = new DocumentNames((version, names) => {
if (this.ready && version === document.version)
this.post({ type: 'displayNames', version, names })
})
this.disposables = [
panel.webview.onDidReceiveMessage((msg: WebviewToExtension) => this.handleMessage(msg)),
panel.onDidChangeViewState(() => {
@@ -146,8 +156,10 @@ class Preview {
owner.navigation.update(this, false)
}),
workspace.onDidChangeTextDocument((e) => {
if (e.document === document && e.contentChanges.length > 0)
this.schedulePush()
if (e.document !== document || e.contentChanges.length === 0)
return
this.names.edited(document.version, e.contentChanges.map(change => ({ offset: change.rangeOffset, length: change.rangeLength, text: change.text })))
this.schedulePush()
}),
// 页面没 ready 时收不到,等它 ready 再随第一份文档之前补发
filter.subscribe((excluded) => {
@@ -202,7 +214,7 @@ class Preview {
const request = ++this.latest
const { languageId, version } = this.document
const source = this.document.getText()
const message = await documentMessage({ languageId, version, getText: () => source }, this.registry, this.runtime, (text) => {
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}` })
})
if (request !== this.latest || !this.ready)
@@ -211,7 +223,22 @@ class Preview {
this.sent = { blocks: message.blocks, source }
const reveal = this.pendingReveal
this.pendingReveal = undefined
this.post(reveal === undefined ? message : { ...message, reveal })
this.post({ ...message, names: this.names.known(version), ...(reveal && { reveal }) })
// 文档在解析期间又改了的,等下一次发文档再查
if (version === this.document.version)
this.names.resolve(version, report => resolveNames(displayNames, message.blocks, this.nameSources(), report))
}
/** 找中文名落在哪些名字上时问的:这份文档的语言扩展、工作区里的中文名索引。 */
private nameSources(): NameSources {
const { document } = this
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 })),
indexed: name => this.owner.indexedNames(document.languageId, name),
}
}
/** 跳到高亮着的名字的下一处 / 上一处出现;页面不在就算了(页面重新加载时高亮已经没了)。 */
@@ -230,6 +257,7 @@ class Preview {
dispose(): void {
clearTimeout(this.timer)
this.names.dispose()
for (const disposable of this.disposables)
disposable.dispose()
}