✨ feat(vscode): 代码阅读预览里工作区外的库文件也能问 AI,问答记进第一个工作区文件夹,重写注释不对库文件开放
This commit is contained in:
1 parent
cecae0e244
commit
39dfd9b084
24 files changed
+463
-79
No files matched your search
@@ -150,8 +150,10 @@ export interface QaThreadView {
|
||||
*/
|
||||
export interface QaMessage {
|
||||
type: 'qa'
|
||||
/** 不在工作区文件夹里时没地方存,问 AI、重写注释不可用 */
|
||||
/** 能问 AI:问答有地方存(工作区外的库文件记在第一个工作区文件夹);没打开工作区文件夹时不能 */
|
||||
available: boolean
|
||||
/** 能重写注释:只有工作区里的文件,工作区外的库文件不改 */
|
||||
rewritable: boolean
|
||||
/** 块下面的问答,按块的记号 */
|
||||
threads: QaThreadView[]
|
||||
/** 这一页顶部、按路径记的问答;不挂在块上 */
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { Uri } from 'vscode'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { pathQaLocation, topQaLocation } from './pathPreview'
|
||||
|
||||
const places = vi.hoisted(() => {
|
||||
const folder = { uri: { scheme: 'file', path: '/ws/app', fsPath: '/ws/app', toString: () => 'file:///ws/app' }, name: 'app', index: 0 }
|
||||
/** ⌘ 点进去的标准库源码,不在工作区里 */
|
||||
const library = { scheme: 'file', query: '', path: '/Users/me/.local/lib/python3.12/pathlib.py', fsPath: '/Users/me/.local/lib/python3.12/pathlib.py' }
|
||||
/** locateQa 给它的位置:第一个工作区文件夹,键是 ~/… */
|
||||
const outside = { folder, key: '~/.local/lib/python3.12/pathlib.py', promptPath: library.fsPath, outsideDir: '/Users/me/.local/lib/python3.12' }
|
||||
return { folder, library, outside }
|
||||
})
|
||||
|
||||
vi.mock('vscode', () => ({
|
||||
Uri: {},
|
||||
workspace: {
|
||||
workspaceFolders: [places.folder],
|
||||
getWorkspaceFolder: (uri: { path: string }) => uri.path === '/ws/app' || uri.path.startsWith('/ws/app/') ? places.folder : undefined,
|
||||
},
|
||||
}))
|
||||
vi.mock('../folderStore', () => ({ locateQa: (uri: { path: string }) => uri.path === places.library.path ? places.outside : undefined }))
|
||||
|
||||
describe('顶部问答记在哪', () => {
|
||||
it('工作区外的库文件也能在顶部问:和块下面的问答同一个定位(第一个工作区文件夹 + ~/… 键)', () => {
|
||||
const library = places.library as unknown as Uri
|
||||
expect(pathQaLocation(library)).toEqual(places.outside)
|
||||
expect(topQaLocation(library)).toEqual({ folder: places.folder, key: 'file:///ws/app\n~/.local/lib/python3.12/pathlib.py' })
|
||||
})
|
||||
|
||||
it('工作区根本身也能问:键是空字符串,发给模型也是空的(写成 .)', () => {
|
||||
const root = { scheme: 'file', query: '', path: '/ws/app', fsPath: '/ws/app' } as unknown as Uri
|
||||
expect(pathQaLocation(root)).toEqual({ folder: places.folder, key: '', promptPath: '' })
|
||||
expect(topQaLocation(root)).toEqual({ folder: places.folder, key: 'file:///ws/app\n' })
|
||||
})
|
||||
})
|
||||
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
import type { WorkspaceFolder } from 'vscode'
|
||||
import type { QaLocation } from '../folderStore'
|
||||
import * as path from 'node:path'
|
||||
import { Uri, workspace } from 'vscode'
|
||||
import { locateQa } from '../folderStore'
|
||||
import { featureQaKey, pathQaKey, relativeQaKey } from '../qa/pathQa'
|
||||
|
||||
export const PATH_PREVIEW_SCHEME = 'code-reader-path'
|
||||
@@ -56,20 +58,20 @@ export function topSubjectOf(uri: Uri): { uri: Uri, kind: TopSubject } {
|
||||
}
|
||||
|
||||
/**
|
||||
* 这条路径在哪个工作区文件夹、相对路径是什么。
|
||||
* 工作区根本身的相对路径是空字符串(locate 不认根,顶部问答要认)。
|
||||
* 这条路径的顶部问答记在哪、发给模型时怎么写它:同 [locateQa](工作区外的库文件也能问),
|
||||
* 另外认工作区根本身,键和发给模型的路径都是空字符串(locate 不认根,顶部问答要认)。
|
||||
*/
|
||||
export function pathQaLocation(uri: Uri): { folder: WorkspaceFolder, key: string } | undefined {
|
||||
export function pathQaLocation(uri: Uri): QaLocation | undefined {
|
||||
const folder = workspace.getWorkspaceFolder(uri)
|
||||
if (!folder)
|
||||
return undefined
|
||||
const key = relativeQaKey(path.posix.normalize(folder.uri.path), path.posix.normalize(uri.path))
|
||||
return key === undefined ? undefined : { folder, key }
|
||||
if (folder && relativeQaKey(path.posix.normalize(folder.uri.path), path.posix.normalize(uri.path)) === '')
|
||||
return { folder, key: '', promptPath: '' }
|
||||
return locateQa(uri)
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶部问答记在哪:在哪个工作区文件夹(claude 在它里面跑,带的图片存在它的 `.spx/code-qa/`)、记录表里的键(见 pathQa.ts)。
|
||||
* 文件、文件夹按「工作区文件夹 + 相对路径」;功能对话页按功能的 id,记在第一个工作区文件夹。不在工作区里、没打开文件夹时没有。
|
||||
* 文件、文件夹按「工作区文件夹 + 键」(见 [pathQaLocation],工作区外的库文件记在第一个工作区文件夹);功能对话页按功能的 id,记在第一个工作区文件夹。
|
||||
* 没打开文件夹时没有。
|
||||
*/
|
||||
export function topQaLocation(uri: Uri): { folder: WorkspaceFolder, key: string } | undefined {
|
||||
const feature = featureIdOf(uri)
|
||||
|
||||
@@ -29,10 +29,11 @@ import { commentFollowUp, commentJob, commentRewritePrompt, findBlock, writeComm
|
||||
import { DefinitionKinds } from '../definitionKinds'
|
||||
import { PreviewDiff } from '../diff/previewDiff'
|
||||
import { blockReplacement } from '../editText'
|
||||
import { locate } from '../folderStore'
|
||||
import { locate, locateQa } from '../folderStore'
|
||||
import { LanguageRequests } from '../languageRequests'
|
||||
import { definitionFor, symbolAt, symbolOccurrences } from '../localSymbols'
|
||||
import { DocumentNames, resolveNames } from '../nameResolution'
|
||||
import { NOT_REWRITABLE } from '../qa/codeQa'
|
||||
import { PATH_ASK_ANCHOR } from '../qa/pathQa'
|
||||
import { askPrompt, followUpPrompt } from '../qa/prompt'
|
||||
import { DocumentLookups } from '../ruleLookups'
|
||||
@@ -622,8 +623,8 @@ class Preview {
|
||||
this.postQa()
|
||||
return
|
||||
}
|
||||
const at = locate(subject.uri)
|
||||
const block = at && this.sent && { path: at.key, languageId: this.document.languageId, blocks: this.sent.blocks, source: this.sent.source, anchor }
|
||||
const at = locateQa(subject.uri)
|
||||
const block = at && this.sent && { path: at.promptPath, languageId: this.document.languageId, blocks: this.sent.blocks, source: this.sent.source, anchor }
|
||||
const prompt = block && askPrompt(block, question)
|
||||
const followUp = block && followUpPrompt(block, question)
|
||||
const refused = prompt === undefined || followUp === undefined
|
||||
@@ -1075,7 +1076,7 @@ class Preview {
|
||||
}
|
||||
const at = locate(document.uri)
|
||||
if (!at) {
|
||||
fail('这个文件不在工作区文件夹里,重写记录没地方存。')
|
||||
fail(NOT_REWRITABLE)
|
||||
return
|
||||
}
|
||||
if (document.isDirty) {
|
||||
|
||||
Reference in new issue
Block a user