✨ feat(vscode): 代码阅读预览问 AI 的回答里代码位置写成链接、点了能跳:提示词要求写 [名字](路径#符号) 或 [文件名:起-止](路径#L起-L止) 并给出仓库根,仓库链接认 #L 行号(注释里同一套),旧回答里整段是「路径:行」的行内代码也当链接,带行号的在预览里展开、闪包住那几行的块,不支持预览的用文本编辑器选中
This commit is contained in:
1 parent
ceeaad91a7
commit
e401e4aec3
22 files changed
+516
-90
No files matched your search
@@ -30,7 +30,10 @@ vi.mock('vscode', () => ({
|
||||
},
|
||||
Range: class {
|
||||
constructor(readonly startLine: number, readonly startCharacter: number, readonly endLine: number, readonly endCharacter: number) {}
|
||||
get start() { return { line: this.startLine, character: this.startCharacter } }
|
||||
get end() { return { line: this.endLine, character: this.endCharacter } }
|
||||
},
|
||||
Uri: { file: (fsPath: string) => ({ fsPath, scheme: 'file', toString: () => `file://${fsPath}` }) },
|
||||
EndOfLine: { LF: 1, CRLF: 2 },
|
||||
}))
|
||||
vi.mock('../../logging/logger', () => ({ logger: { add: vi.fn() } }))
|
||||
@@ -52,6 +55,12 @@ vi.mock('./documentMessage', async importOriginal => ({
|
||||
}))
|
||||
/** 文档符号由各个测试给 */
|
||||
vi.mock('./documentSymbols', () => ({ documentSymbolNodes: vi.fn(async () => []) }))
|
||||
/** 仓库在 /workspace,里面只有这几个文件 */
|
||||
vi.mock('./repoPath', async importOriginal => ({
|
||||
...await importOriginal<typeof import('./repoPath')>(),
|
||||
repoRootOf: async () => '/workspace',
|
||||
resolveRepoEntry: async (_root: string, rel: string) => ['web/a.ts', 'web/b.ts', 'docs/notes.txt'].includes(rel) ? { fsPath: `/workspace/${rel}`, isDirectory: false } : undefined,
|
||||
}))
|
||||
|
||||
const SOURCE = ['// 旧说明', 'export function load() {}']
|
||||
|
||||
@@ -189,7 +198,7 @@ function themeColors(colors: CodeColors) {
|
||||
}
|
||||
|
||||
/** 对比基准只认从「未提交的更改」点开的:从那儿点开的对比上一次提交,别处打开的不比 */
|
||||
function opening(profiles = { ask: profileSet(''), rewrite: profileSet('') }, colors = themeColors(COLORS)) {
|
||||
function opening(profiles = { ask: profileSet(''), rewrite: profileSet('') }, colors = themeColors(COLORS), languageFor: (languageId: string) => unknown = () => undefined) {
|
||||
const subscribe = () => ({ dispose: () => {} })
|
||||
const diffBase = vi.fn((_uri: unknown, from: OpenedFrom): DiffBase | undefined => from === 'uncommitted' ? HEAD_BASE : undefined)
|
||||
const qa = {
|
||||
@@ -202,7 +211,7 @@ function opening(profiles = { ask: profileSet(''), rewrite: profileSet('') }, co
|
||||
}
|
||||
const provider = new PreviewEditorProvider(...[
|
||||
{},
|
||||
{ selectedTranslator: () => undefined },
|
||||
{ selectedTranslator: () => undefined, languageFor },
|
||||
{},
|
||||
{ subscribe, current: { excluded: {}, onlyChanges: false } },
|
||||
colors,
|
||||
@@ -565,6 +574,60 @@ describe('注释里不带路径的引用按它写在哪块的注释里找', () =
|
||||
})
|
||||
})
|
||||
|
||||
describe('点了带几行的仓库链接', () => {
|
||||
/** 文件按名字认语言,第 n 行(0 起)有 n 个字,一共 20 行 */
|
||||
function textDocument(fsPath: string) {
|
||||
const languageId = fsPath.endsWith('.ts') ? 'typescript' : 'plaintext'
|
||||
return { uri: { fsPath, scheme: 'file', toString: () => `file://${fsPath}` }, languageId, lineCount: 20, lineAt: (line: number) => ({ text: 'x'.repeat(line) }) }
|
||||
}
|
||||
|
||||
async function linked() {
|
||||
vi.mocked(workspace.openTextDocument).mockImplementation((async (uri: { fsPath: string }) => textDocument(uri.fsPath)) as never)
|
||||
vi.mocked(commands.executeCommand).mockClear()
|
||||
const { provider, document } = opening(undefined, undefined, languageId => languageId === 'typescript' ? {} : undefined)
|
||||
const page = tab()
|
||||
provider.resolveCustomTextEditor(document as never, page.panel)
|
||||
await page.send({ type: 'ready' })
|
||||
await vi.waitFor(() => expect(page.posted()).toContainEqual(expect.objectContaining({ type: 'document' })))
|
||||
page.clear()
|
||||
return { provider, page }
|
||||
}
|
||||
|
||||
it('指到这份文件:就在本预览里滚到包住这几行的块', async () => {
|
||||
const { page } = await linked()
|
||||
await page.send({ type: 'openRepoLink', path: 'web/a.ts', lines: { start: 2, end: 3 } })
|
||||
expect(page.posted()).toEqual([{ type: 'reveal', line: 1, endLine: 2 }, { type: 'repoLinkOpened', path: 'web/a.ts' }])
|
||||
})
|
||||
|
||||
it('指到别的有代码阅读的文件:用预览打开,滚到包住这几行的块', async () => {
|
||||
const { provider, page } = await linked()
|
||||
const open = vi.spyOn(provider, 'open').mockResolvedValue()
|
||||
await page.send({ type: 'openRepoLink', path: 'web/b.ts', lines: { start: 5, end: 9 } })
|
||||
expect(open).toHaveBeenCalledWith(expect.objectContaining({ fsPath: '/workspace/web/b.ts' }), { reveal: { line: 4, endLine: 8 } }, expect.any(Function))
|
||||
expect(page.posted()).toEqual([{ type: 'repoLinkOpened', path: 'web/b.ts' }])
|
||||
})
|
||||
|
||||
it('指到没有代码阅读的文件:文本编辑器打开,选中这几行', async () => {
|
||||
const { page } = await linked()
|
||||
await page.send({ type: 'openRepoLink', path: 'docs/notes.txt', lines: { start: 3, end: 4 } })
|
||||
expect(commands.executeCommand).toHaveBeenCalledWith('vscode.open', expect.objectContaining({ fsPath: '/workspace/docs/notes.txt' }), expect.objectContaining({ selection: expect.objectContaining({ startLine: 2, startCharacter: 0, endLine: 3, endCharacter: 3 }) }))
|
||||
expect(page.posted()).toEqual([{ type: 'repoLinkOpened', path: 'docs/notes.txt' }])
|
||||
})
|
||||
|
||||
it('文件没有:标坏,不打开', async () => {
|
||||
const { page } = await linked()
|
||||
await page.send({ type: 'openRepoLink', path: 'web/gone.ts', lines: { start: 1, end: 1 } })
|
||||
expect(page.posted()).toEqual([{ type: 'repoLinkBroken', path: 'web/gone.ts' }])
|
||||
expect(commands.executeCommand).not.toHaveBeenCalledWith('vscode.open', expect.anything(), expect.anything())
|
||||
})
|
||||
|
||||
it('问哪些路径没有时,带几行的链接按文件问', async () => {
|
||||
const { page } = await linked()
|
||||
await page.send({ type: 'checkRepoLinks', id: 3, paths: ['web/a.ts', 'web/gone.ts'], references: [] })
|
||||
expect(page.posted()).toEqual([{ type: 'repoLinksChecked', id: 3, missing: ['web/gone.ts'], unresolved: [] }])
|
||||
})
|
||||
})
|
||||
|
||||
describe('预览里的查找栏', () => {
|
||||
const sent = (page: ReturnType<typeof tab>): ExtensionToWebview[] => page.posted().filter(msg => msg.type === 'openFind' || msg.type === 'moveFind')
|
||||
|
||||
|
||||
Reference in new issue
Block a user