✨ feat(vscode): 代码阅读「了解一块代码」改成按路径列调用树:结果是几条路径(用户的一种操作一条),每条从入口一路调到后端服务、数据表,跨进程的写明怎么连过去(via),结构用扁平的 nodes 加 parent 连成树、不用递归;侧栏路径下面一层层展开节点,回答里用嵌套列表画树;回答和找的过程里的代码位置写成仓库链接,功能对话页上的链接按找代码用的工作区文件夹检查、打开、悬停;旧格式的结果当没有,栏上方提示打开对话重新找;删功能前先弹模态框确认,说清连带删掉调用树和问答记录,点「删除」才删

This commit is contained in:
cruldra committed 2026-10-14 21:28:40 +11:00
1 parent cc445e7123
commit c6de46ad37
17 files changed
+978 -253

No files matched your search

+19 -18
View File
@@ -17,7 +17,8 @@ import type { BlockFilter } from './blockFilter'
import type { FoundLocation } from './definitions'
import type { ChatKind, DefinitionPeek, ExtensionToWebview, LineRange, Occurrence, Reveal, SymbolReference, WebviewToExtension } from './messages'
import type { TargetSpot } from './openTarget'
import type { TopSubject } from './pathPreview'
import type { LinkBase, TopSubject } from './pathPreview'
import type { RepoEntry } from './repoPath'
import { realpath } from 'node:fs/promises'
import { commands, EndOfLine, Position, Range, Uri, window, workspace, WorkspaceEdit } from 'vscode'
import { logger } from '../../logging/logger'
@@ -49,7 +50,7 @@ import { FIND_CONTEXT } from './findCommands'
import { NavigationContext, OCCURRENCES_CONTEXT } from './occurrenceNavigation'
import { occurrencesOf } from './occurrences'
import { languageOf, linesSpot, openingFor, revealOf } from './openTarget'
import { PATH_PREVIEW_SCHEME, topSubjectOf } from './pathPreview'
import { linkBaseOf, PATH_PREVIEW_SCHEME, topSubjectOf } from './pathPreview'
import { ReferenceTargets } from './referenceTargets'
import { findRepoSymbol, isRepoImagePath, repoRootOf, resolveRepoEntry } from './repoPath'
@@ -950,15 +951,11 @@ class Preview {
}
}
/** 预览里这批仓库路径,哪些文件或目录不在;这批不带路径的引用,各按写在哪块找,哪些找不到、哪些是语言自带的名字(页面当普通文字)。 */
/** 预览里这批仓库路径(从哪儿算见 [linkBaseOf]),哪些文件或目录不在;这批不带路径的引用,各按写在哪块找,哪些找不到、哪些是语言自带的名字(页面当普通文字)。 */
private async checkRepoLinks(id: number, paths: string[], references: SymbolReference[]): Promise<void> {
const root = await repoRootOf(this.document.uri.fsPath)
const base = await linkBaseOf(this.document.uri)
const [missing, resolved] = await Promise.all([
Promise.all(paths.map(async (rel) => {
if (root === undefined || await resolveRepoEntry(root, rel) === undefined)
return rel
return undefined
})),
Promise.all(paths.map(async rel => await linkEntry(base, rel) === undefined ? rel : undefined)),
Promise.all(references.map(async reference => ({ reference, target: await this.resolveSymbolLink(reference) }))),
])
this.post({
@@ -993,13 +990,13 @@ class Preview {
/** 这批仓库图片哪些在。在的带回预览里能直接用的地址;不在、不是图片、跑出仓库的没有地址。 */
private async checkRepoImages(id: number, paths: string[]): Promise<void> {
const root = await repoRootOf(this.document.uri.fsPath)
if (root)
await this.allowRepoRoot(root)
const base = await linkBaseOf(this.document.uri)
const images = await Promise.all(paths.map(async (rel) => {
if (root === undefined || !isRepoImagePath(rel))
const place = isRepoImagePath(rel) ? base(rel) : undefined
if (!place)
return { path: rel }
const entry = await resolveRepoEntry(root, rel)
await this.allowRepoRoot(place.root)
const entry = await resolveRepoEntry(place.root, place.rel)
if (!entry || entry.isDirectory)
return { path: rel }
const src = this.panel.webview.asWebviewUri(Uri.file(entry.fsPath)).toString()
@@ -1032,8 +1029,7 @@ class Preview {
}
if (rel === undefined)
return
const root = await repoRootOf(this.document.uri.fsPath)
const entry = root === undefined ? undefined : await resolveRepoEntry(root, rel)
const entry = await linkEntry(await linkBaseOf(this.document.uri), rel)
if (!entry) {
this.markRepoLinkBroken(rel)
return
@@ -1064,8 +1060,7 @@ class Preview {
private async symbolTarget(rel: string | undefined, symbol: string, block: string | undefined): Promise<ReferenceTarget> {
if (rel === undefined)
return this.resolveSymbolLink({ symbol, block })
const root = await repoRootOf(this.document.uri.fsPath)
const entry = root === undefined ? undefined : await resolveRepoEntry(root, rel)
const entry = await linkEntry(await linkBaseOf(this.document.uri), rel)
return entry && !entry.isDirectory ? this.locateRepoSymbol(Uri.file(entry.fsPath), symbol) : undefined
}
@@ -1167,6 +1162,12 @@ class Preview {
}
}
/** 一条仓库链接指到磁盘上的哪个文件或目录,路径按这一页的算法算(见 [linkBaseOf]);算不出来、不在、跑出根的没有。 */
async function linkEntry(base: LinkBase, rel: string): Promise<RepoEntry | undefined> {
const place = base(rel)
return place && resolveRepoEntry(place.root, place.rel)
}
/** 写回重写好的注释:换掉原来那几行(同块编辑),或在块上面插进新写的几行。 */
async function writeCommentEdit(document: TextDocument, edit: CommentEdit): Promise<WriteResult> {
if (edit.kind === 'replace')