Files
superwork/vscode/src/codeReader/editor/pathPreview.ts
T

111 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 文件夹、以及代码阅读不认识的文件,用这个协议打开代码阅读预览:
* 文档是空的,页面上没有代码块,只在顶部聊天。源码文件直接开它自己的预览。
* 「了解一块代码」找的功能的对话页也用这个协议,只是对的不是路径,是功能。
*/
import type { WorkspaceFolder } from 'vscode'
import type { QaLocation } from '../folderStore'
import type { NamedFolder, RepoPlace } from './repoPath'
import * as path from 'node:path'
import { Uri, workspace } from 'vscode'
import { locateQa } from '../folderStore'
import { featureQaKey, pathQaKey, relativeQaKey } from '../qa/pathQa'
import { folderLinkPlace, repoRootOf } from './repoPath'
export const PATH_PREVIEW_SCHEME = 'code-reader-path'
export type PathSubject = 'file' | 'folder'
/** 预览顶部的聊天问的是什么:文件、文件夹,或「了解一块代码」找的一个功能 */
export type TopSubject = PathSubject | 'feature'
/** 功能对话页的查询串:后面跟着功能的 id */
const FEATURE_QUERY = 'feature='
/** 「了解一块代码」一个功能的对话页:路径是问的那句话(标签页上显示它,`/` 换成全角的免得被当成目录),查询串带着功能的 id。 */
export function featurePageUri(id: string, question: string): Uri {
return Uri.from({ scheme: PATH_PREVIEW_SCHEME, path: `/${question.replaceAll('/', '/')}`, query: `${FEATURE_QUERY}${id}` })
}
/** 一个功能的问答按这个 Uri 记、按它对事件:对话页标签上叫什么都是它。 */
export function featureSubjectUri(id: string): Uri {
return Uri.from({ scheme: PATH_PREVIEW_SCHEME, path: '/', query: `${FEATURE_QUERY}${id}` })
}
/** 功能对话页(或 featureSubjectUri)对的功能 id;别的 Uri 没有。 */
export function featureIdOf(uri: Uri): string | undefined {
return uri.scheme === PATH_PREVIEW_SCHEME && uri.query.startsWith(FEATURE_QUERY) ? uri.query.slice(FEATURE_QUERY.length) : undefined
}
/** 「了解一块代码」找代码用的工作区文件夹:眼下打开的全部,claude 在第一个里面跑,别的加进来读。 */
export function featureFolders(): NamedFolder[] {
return (workspace.workspaceFolders ?? []).map(folder => ({ name: folder.name, path: folder.uri.fsPath }))
}
/** 一页上的仓库链接从哪儿算:`place` 把一条链接的路径换成磁盘上的位置,算不出来的没有;算出来的位置都在 `roots` 这几个根下面。 */
export interface LinkBase {
place: (rel: string) => RepoPlace | undefined
roots: string[]
}
/**
* 预览这一页上仓库链接的路径从哪儿算:功能对话页从这个功能找代码用的工作区文件夹算(见 [featureFolders]、[folderLinkPlace]),
* 别的页从文档所在的 git 仓库根算,不在 git 仓库里时一条也算不出来。
*/
export async function linkBaseOf(uri: Uri): Promise<LinkBase> {
if (featureIdOf(uri) !== undefined) {
const folders = featureFolders()
return { place: rel => folderLinkPlace(folders, rel), roots: folders.map(folder => folder.path) }
}
const root = await repoRootOf(uri.fsPath)
return root === undefined ? { place: () => undefined, roots: [] } : { place: rel => ({ root, rel }), roots: [root] }
}
export function pathPreviewUri(target: Uri, kind: PathSubject): Uri {
return target.with({ scheme: PATH_PREVIEW_SCHEME, query: kind, fragment: '' })
}
/** 预览文档是路径页时,还原成真实文件或文件夹;普通源码预览不是。 */
export function pathPreviewOf(uri: Uri): { uri: Uri, kind: PathSubject } | undefined {
if (uri.scheme !== PATH_PREVIEW_SCHEME)
return undefined
if (uri.query !== 'file' && uri.query !== 'folder')
return undefined
return { uri: uri.with({ scheme: 'file', query: '', fragment: '' }), kind: uri.query }
}
/** 预览这一页顶部问的是什么:功能对话页是那个功能(featureSubjectUri),路径页还原成真实文件或文件夹,源码预览就是文档自己。 */
export function topSubjectOf(uri: Uri): { uri: Uri, kind: TopSubject } {
const feature = featureIdOf(uri)
if (feature !== undefined)
return { uri: featureSubjectUri(feature), kind: 'feature' }
return pathPreviewOf(uri) ?? { uri, kind: 'file' }
}
/**
* 这条路径的顶部问答记在哪、发给模型时怎么写它:同 [locateQa](工作区外的库文件也能问),
* 另外认工作区根本身,键和发给模型的路径都是空字符串(locate 不认根,顶部问答要认)。
*/
export function pathQaLocation(uri: Uri): QaLocation | undefined {
const folder = workspace.getWorkspaceFolder(uri)
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)。
* 文件、文件夹按「工作区文件夹 + 键」(见 [pathQaLocation],工作区外的库文件记在第一个工作区文件夹);功能对话页按功能的 id,记在第一个工作区文件夹。
* 没打开文件夹时没有。
*/
export function topQaLocation(uri: Uri): { folder: WorkspaceFolder, key: string } | undefined {
const feature = featureIdOf(uri)
if (feature !== undefined) {
const folder = workspace.workspaceFolders?.[0]
return folder && { folder, key: featureQaKey(feature) }
}
const at = pathQaLocation(uri)
return at && { folder: at.folder, key: pathQaKey(at.folder.uri.toString(), at.key) }
}