✨ feat(vscode): 新增「了解一块代码」:AI 找功能代码占一栏,对话里接着改

主栏标题栏的放大镜问要了解哪一块,占一个栏位建一栏(和收藏共用 30 个栏位,视图改名 slot1..30,按 slotKindN 分收藏 / 功能挂标题栏按钮),同时开对话页让 Claude 只读地在全部工作区文件夹里找,按 --json-schema 交回环节 → 文件 + 符号 + 一句话。
对话复用预览顶部的聊天(CodeQa.askTop:会话续接、进行中的一轮、记录存 workspaceState);结构不合格算这一轮没答成、留着能再画出来。
条目每次画、点时按眼下的代码找:有语言插件的解析成代码块按「类名.方法名」找,没有的问 VS Code 文档符号;找不到画成灰的。
This commit is contained in:
cruldra committed 2026-10-12 15:48:54 +11:00
1 parent 16d27004d8
commit 880bf7fc57
38 files changed
+3386 -378

No files matched your search

+47 -3
View File
@@ -1,17 +1,39 @@
/**
* 文件夹、以及代码阅读不认识的文件,用这个协议打开代码阅读预览:
* 文档是空的,页面上没有代码块,只在顶部聊天。源码文件直接开它自己的预览。
* 「了解一块代码」找的功能的对话页也用这个协议,只是对的不是路径,是功能。
*/
import type { Uri, WorkspaceFolder } from 'vscode'
import type { WorkspaceFolder } from 'vscode'
import * as path from 'node:path'
import { workspace } from 'vscode'
import { relativeQaKey } from '../qa/pathQa'
import { Uri, workspace } from 'vscode'
import { featureQaKey, pathQaKey, relativeQaKey } from '../qa/pathQa'
export const PATH_PREVIEW_SCHEME = 'code-reader-path'
export type PathSubject = 'file' | 'folder'
/** 预览顶部的聊天问的是什么:文件、文件夹,或「了解一块代码」找的一个功能 */
export type TopSubject = PathSubject | 'feature'
/** 功能对话页的查询串:后面跟着功能的 id */
const FEATURE_QUERY = 'feature='
/** 「了解一块代码」一个功能的对话页:路径是问的那句话(标签页上显示它,`/` 换成全角的免得被当成目录),查询串带着功能的 id。 */
export function featurePageUri(id: string, question: string): Uri {
return Uri.from({ scheme: PATH_PREVIEW_SCHEME, path: `/${question.replaceAll('/', '/')}`, query: `${FEATURE_QUERY}${id}` })
}
/** 一个功能的问答按这个 Uri 记、按它对事件:对话页标签上叫什么都是它。 */
export function featureSubjectUri(id: string): Uri {
return Uri.from({ scheme: PATH_PREVIEW_SCHEME, path: '/', query: `${FEATURE_QUERY}${id}` })
}
/** 功能对话页(或 featureSubjectUri)对的功能 id;别的 Uri 没有。 */
export function featureIdOf(uri: Uri): string | undefined {
return uri.scheme === PATH_PREVIEW_SCHEME && uri.query.startsWith(FEATURE_QUERY) ? uri.query.slice(FEATURE_QUERY.length) : undefined
}
export function pathPreviewUri(target: Uri, kind: PathSubject): Uri {
return target.with({ scheme: PATH_PREVIEW_SCHEME, query: kind, fragment: '' })
}
@@ -25,6 +47,14 @@ export function pathPreviewOf(uri: Uri): { uri: Uri, kind: PathSubject } | undef
return { uri: uri.with({ scheme: 'file', query: '', fragment: '' }), kind: uri.query }
}
/** 预览这一页顶部问的是什么:功能对话页是那个功能(featureSubjectUri),路径页还原成真实文件或文件夹,源码预览就是文档自己。 */
export function topSubjectOf(uri: Uri): { uri: Uri, kind: TopSubject } {
const feature = featureIdOf(uri)
if (feature !== undefined)
return { uri: featureSubjectUri(feature), kind: 'feature' }
return pathPreviewOf(uri) ?? { uri, kind: 'file' }
}
/**
* 这条路径在哪个工作区文件夹、相对路径是什么。
* 工作区根本身的相对路径是空字符串(locate 不认根,顶部问答要认)。
@@ -36,3 +66,17 @@ export function pathQaLocation(uri: Uri): { folder: WorkspaceFolder, key: string
const key = relativeQaKey(path.posix.normalize(folder.uri.path), path.posix.normalize(uri.path))
return key === undefined ? undefined : { folder, key }
}
/**
* 顶部问答记在哪:在哪个工作区文件夹(claude 在它里面跑,带的图片存在它的 `.spx/code-qa/`)、记录表里的键(见 pathQa.ts)。
* 文件、文件夹按「工作区文件夹 + 相对路径」;功能对话页按功能的 id,记在第一个工作区文件夹。不在工作区里、没打开文件夹时没有。
*/
export function topQaLocation(uri: Uri): { folder: WorkspaceFolder, key: string } | undefined {
const feature = featureIdOf(uri)
if (feature !== undefined) {
const folder = workspace.workspaceFolders?.[0]
return folder && { folder, key: featureQaKey(feature) }
}
const at = pathQaLocation(uri)
return at && { folder: at.folder, key: pathQaKey(at.folder.uri.toString(), at.key) }
}