✨ feat(vscode): 新增「了解一块代码」:AI 找功能代码占一栏,对话里接着改
主栏标题栏的放大镜问要了解哪一块,占一个栏位建一栏(和收藏共用 30 个栏位,视图改名 slot1..30,按 slotKindN 分收藏 / 功能挂标题栏按钮),同时开对话页让 Claude 只读地在全部工作区文件夹里找,按 --json-schema 交回环节 → 文件 + 符号 + 一句话。 对话复用预览顶部的聊天(CodeQa.askTop:会话续接、进行中的一轮、记录存 workspaceState);结构不合格算这一轮没答成、留着能再画出来。 条目每次画、点时按眼下的代码找:有语言插件的解析成代码块按「类名.方法名」找,没有的问 VS Code 文档符号;找不到画成灰的。
This commit is contained in:
1 parent
16d27004d8
commit
880bf7fc57
38 files changed
+3386
-378
No files matched your search
@@ -1,7 +1,9 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import * as path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { FAVORITE_SLOTS, favoriteCommand, favoriteContext, favoriteViewId } from '../favorites/favoriteSlots'
|
||||
import { favoriteCommand } from '../favorites/favoriteSlots'
|
||||
import { FEATURE_MARKER, featureCommand } from '../features/features'
|
||||
import { slotContext, SLOTS, slotViewId } from '../slots/slots'
|
||||
import { UNSTAGED_MARKER } from '../uncommitted/uncommittedFiles'
|
||||
import { COPY_COMMANDS } from './copyText'
|
||||
import { entryContext, GROUP_CONTEXT } from './fileEntries'
|
||||
@@ -95,8 +97,9 @@ function evaluate(when: string, context: Context): boolean {
|
||||
|
||||
const FILES = 'superpowers.codeReader.files'
|
||||
const UNCOMMITTED = 'superpowers.codeReader.uncommitted'
|
||||
const FAVORITE = favoriteViewId(7)
|
||||
const READER_VIEWS = [FILES, UNCOMMITTED, ...FAVORITE_SLOTS.map(favoriteViewId)]
|
||||
const FAVORITE = slotViewId(7)
|
||||
const FEATURE = slotViewId(8)
|
||||
const READER_VIEWS = [FILES, UNCOMMITTED, ...SLOTS.map(slotViewId)]
|
||||
const REVIEWING = 'superpowers.codeReader.reviewing'
|
||||
|
||||
const COPY_MENU = 'superpowers.codeReader.copy'
|
||||
@@ -277,6 +280,7 @@ describe('右键菜单的「在系统文件管理器中显示」', () => {
|
||||
})
|
||||
|
||||
describe('主栏的审查工单', () => {
|
||||
const FIND = 'superpowers.codeReader.findFeature'
|
||||
const PICK = 'superpowers.codeReader.pickReviewIssue'
|
||||
const HIDE_TESTS = 'superpowers.codeReader.hideReviewTests'
|
||||
const SHOW_TESTS = 'superpowers.codeReader.showReviewTests'
|
||||
@@ -284,10 +288,10 @@ describe('主栏的审查工单', () => {
|
||||
const REFRESH = 'superpowers.codeReader.refresh'
|
||||
const TESTS_HIDDEN = 'superpowers.codeReader.reviewTestsHidden'
|
||||
|
||||
it('标题栏:平时是选工单、刷新;审查时多出隐藏 / 显示测试文件(按开关换着显示)和「显示全部」', () => {
|
||||
expect(titleBar(FILES)).toEqual([PICK, REFRESH])
|
||||
expect(titleBar(FILES, { [REVIEWING]: true })).toEqual([PICK, HIDE_TESTS, SHOW_ALL, REFRESH])
|
||||
expect(titleBar(FILES, { [REVIEWING]: true, [TESTS_HIDDEN]: true })).toEqual([PICK, SHOW_TESTS, SHOW_ALL, REFRESH])
|
||||
it('标题栏:平时是了解一块代码、选工单、刷新;审查时多出隐藏 / 显示测试文件(按开关换着显示)和「显示全部」', () => {
|
||||
expect(titleBar(FILES)).toEqual([FIND, PICK, REFRESH])
|
||||
expect(titleBar(FILES, { [REVIEWING]: true })).toEqual([FIND, PICK, HIDE_TESTS, SHOW_ALL, REFRESH])
|
||||
expect(titleBar(FILES, { [REVIEWING]: true, [TESTS_HIDDEN]: true })).toEqual([FIND, PICK, SHOW_TESTS, SHOW_ALL, REFRESH])
|
||||
expect(menus['view/title'].filter(item => item.when.includes(FILES)).every(item => item.group?.startsWith('navigation'))).toBe(true)
|
||||
})
|
||||
|
||||
@@ -347,22 +351,34 @@ describe('「未提交的更改」一栏', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('收藏栏', () => {
|
||||
it(`预先声明 ${FAVORITE_SLOTS.length} 个栏位,排在「未提交的更改」后面,各自有收藏占着时才显示`, () => {
|
||||
expect(views['superpowers-reader'].slice(2)).toEqual(FAVORITE_SLOTS.map(slot => ({ id: favoriteViewId(slot), name: `收藏 ${slot}`, when: favoriteContext(slot) })))
|
||||
describe('收藏、「了解一块代码」共用的栏位', () => {
|
||||
it(`预先声明 ${SLOTS.length} 个栏位,排在「未提交的更改」后面,各自有收藏或功能占着时才显示`, () => {
|
||||
expect(views['superpowers-reader'].slice(2)).toEqual(SLOTS.map(slot => ({ id: slotViewId(slot), name: `栏位 ${slot}`, when: slotContext(slot) })))
|
||||
})
|
||||
|
||||
it('每一栏的标题栏:重命名、取消收藏,只对这一栏', () => {
|
||||
for (const slot of FAVORITE_SLOTS)
|
||||
expect(titleBar(favoriteViewId(slot))).toEqual([favoriteCommand('rename', slot), favoriteCommand('remove', slot)])
|
||||
it('收藏占着的栏:标题栏是重命名、取消收藏,只对这一栏', () => {
|
||||
for (const slot of SLOTS)
|
||||
expect(titleBar(slotViewId(slot), { [slotContext(slot)]: 'favorite' })).toEqual([favoriteCommand('rename', slot), favoriteCommand('remove', slot)])
|
||||
})
|
||||
|
||||
it('每个栏位的命令都登记了、字和图标一样,命令面板里藏起来', () => {
|
||||
for (const slot of FAVORITE_SLOTS) {
|
||||
it('功能占着的栏:标题栏是打开对话、重命名、删除,只对这一栏', () => {
|
||||
for (const slot of SLOTS)
|
||||
expect(titleBar(slotViewId(slot), { [slotContext(slot)]: 'feature' })).toEqual([featureCommand('openChat', slot), featureCommand('rename', slot), featureCommand('remove', slot)])
|
||||
})
|
||||
|
||||
it('别的栏被占着不影响这一栏的按钮', () => {
|
||||
expect(titleBar(slotViewId(3), { [slotContext(4)]: 'feature' })).toEqual([])
|
||||
})
|
||||
|
||||
it('每个栏位的命令都登记了、字和图标,命令面板里藏起来', () => {
|
||||
for (const slot of SLOTS) {
|
||||
expect(titleOf(favoriteCommand('rename', slot))).toMatchObject({ title: '重命名', icon: '$(edit)' })
|
||||
expect(titleOf(favoriteCommand('remove', slot))).toMatchObject({ title: '取消收藏', icon: '$(star-full)' })
|
||||
expect(palette.get(favoriteCommand('rename', slot))).toBe('false')
|
||||
expect(palette.get(favoriteCommand('remove', slot))).toBe('false')
|
||||
expect(titleOf(featureCommand('openChat', slot))).toMatchObject({ title: '打开对话', icon: '$(comment-discussion)' })
|
||||
expect(titleOf(featureCommand('rename', slot))).toMatchObject({ title: '重命名', icon: '$(edit)' })
|
||||
expect(titleOf(featureCommand('remove', slot))).toMatchObject({ title: '删除', icon: '$(trash)' })
|
||||
for (const id of [favoriteCommand('rename', slot), favoriteCommand('remove', slot), featureCommand('openChat', slot), featureCommand('rename', slot), featureCommand('remove', slot)])
|
||||
expect(palette.get(id)).toBe('false')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -378,6 +394,43 @@ describe('收藏栏', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('了解一块代码', () => {
|
||||
const FIND = 'superpowers.codeReader.findFeature'
|
||||
const OPEN_ITEM = 'superpowers.codeReader.openFeatureItem'
|
||||
/** 功能栏里找得到的条目行(符号、只有文件的都是它) */
|
||||
const item = entryContext({ isDirectory: false, isRoot: false }, FEATURE_MARKER)
|
||||
|
||||
it('入口只在主栏标题栏:放大镜「了解一块代码」,排在最前;别的栏没有', () => {
|
||||
expect(titleBar(FILES)[0]).toBe(FIND)
|
||||
expect(titleBar(FILES, { [REVIEWING]: true })[0]).toBe(FIND)
|
||||
for (const view of [UNCOMMITTED, FEATURE])
|
||||
expect(titleBar(view, { [slotContext(8)]: 'feature' })).not.toContain(FIND)
|
||||
expect(titleOf(FIND)).toMatchObject({ title: '了解一块代码', icon: '$(search)' })
|
||||
})
|
||||
|
||||
it('条目行:「问 AI」「复制」、在系统文件管理器中显示照常有;注释、收藏、暂存不挂', () => {
|
||||
expect(itemsFor('view/item/context', 'inline', item, FEATURE)).toEqual([ASK, COPY_MENU])
|
||||
expect(itemsFor('view/item/context', 'copy', item, FEATURE)).toEqual(FILE_COPIES)
|
||||
expect(itemsFor(COPY_MENU, 'copy', item, FEATURE)).toEqual(FILE_COPIES)
|
||||
expect(itemsFor('view/item/context', 'fileManager', item, FEATURE)).toEqual(['superpowers.codeReader.revealInFinder'])
|
||||
for (const group of ['notes', 'star'])
|
||||
expect(itemsFor('view/item/context', group, item, FEATURE)).toEqual([])
|
||||
})
|
||||
|
||||
it('分组行、找不到的条目行什么按钮、菜单都没有', () => {
|
||||
for (const context of [GROUP_CONTEXT, `${FEATURE_MARKER} missing`]) {
|
||||
for (const group of ['inline', 'copy', 'fileManager', 'notes', 'star'])
|
||||
expect(itemsFor('view/item/context', group, context, FEATURE)).toEqual([])
|
||||
}
|
||||
})
|
||||
|
||||
it('点条目的命令只给侧栏行用,命令面板里藏起来;「了解一块代码」命令面板里能用', () => {
|
||||
expect(titleOf(OPEN_ITEM)).toBeDefined()
|
||||
expect(palette.get(OPEN_ITEM)).toBe('false')
|
||||
expect(palette.has(FIND)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('菜单里用到的', () => {
|
||||
it('命令都登记了;旧的筛选按钮、上下文键不再出现', () => {
|
||||
const ids = new Set(commands.map(item => item.command))
|
||||
|
||||
Reference in new issue
Block a user