✨ 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

@@ -0,0 +1,59 @@
/**
* 「了解一块代码」的条目按符号名找位置:用语言插件把源码解析成代码块(同预览),在有名字的块里按 `类名.方法名` 一层层找。
* 每次画栏、点条目时按眼下的代码现找,代码改过也找得到。不碰 VS Code;语言没有插件时用 VS Code 的文档符号(见 featureViews.ts)。
*/
import type { BlockKind, CodeBlock, LanguageAdapter, Node, SourceRange } from '../contract'
import type { TreeSitterRuntime } from '../runtime'
import { ownName } from '../blockView'
import { findRepoSymbol } from '../editor/repoPath'
/** 块树上有名字的一块:名字、种类、名字在源码里的范围;子孙里有名字的挂在下面。 */
export interface NamedBlock {
name: string
kind: BlockKind
range: SourceRange
children: NamedBlock[]
}
/** 语言插件不给名字、按语法树里的声明找名字的块:类型(TypeScript 的 interface、type)、类型里的成员、别的声明语句(Rust 的 type)。 */
const DECLARATIONS = new Set<BlockKind>(['type', 'field', 'statement'])
/**
* 块的名字:函数、类、组件、变量(见 ownName),Rust 的 impl、trait(`impl Order` 里的方法写成 `Order.pay`);
* DECLARATIONS 那几种按语法树里声明的 name 字段找,export 语句先进到它导出的声明里。
*/
function nameOf(block: CodeBlock): Node | undefined {
if (block.kind === 'impl' || block.kind === 'trait')
return block.facts.name
if (!DECLARATIONS.has(block.kind))
return ownName(block)
const declaration = block.node.childForFieldName('declaration') ?? block.node
return declaration.childForFieldName('name') ?? undefined
}
/** 块树里有名字的那些;没名字的块(条件、调用语句)跳过,它里面有名字的往上提一层。 */
export function namedBlocks(blocks: readonly CodeBlock[]): NamedBlock[] {
return blocks.flatMap((block): NamedBlock[] => {
const children = namedBlocks(block.children)
const name = nameOf(block)
return name ? [{ name: name.text, kind: block.kind, range: { startIndex: name.startIndex, endIndex: name.endIndex }, children }] : children
})
}
/** 用语言插件解析 source,按 symbol(`login`、`AuthService.login`)找那一块;找不到没有。语法树用完即释放。 */
export async function locateInSource(
runtime: Pick<TreeSitterRuntime, 'parse'>,
adapter: LanguageAdapter,
languageId: string,
source: string,
symbol: string,
): Promise<NamedBlock | undefined> {
const tree = await runtime.parse(adapter, languageId, source)
try {
return findRepoSymbol(namedBlocks(adapter.toBlocks(tree.rootNode, source, languageId)), symbol)
}
finally {
tree.delete()
}
}