✨ feat(vscode): 代码阅读侧栏加「未提交的更改」筛选
- 标题栏新按钮「未提交的更改」:树里只剩工作区各 git 仓库里没提交的文件(暂存、没暂存、未跟踪、合并冲突),每个有改动的仓库一个根,跟着 git 状态自动重摆;上色和徽标交给 VS Code 自带的 git 装饰
- 两种筛选互斥,由 TreeFilters 统一管,上下文键合并成 superpowers.codeReader.filter(review / uncommitted)
- 改动文件树改成多根,buildChangedTree 挪到 explorer/changedTree 并改吃 {path, change}
- 内置 git 扩展 API 的类型和加载抽到 src/git/gitApi、gitExtension,提交按钮的监听也改用它
- 文件注释按文件所在的工作区文件夹记,不看树从哪个根摆下来
This commit is contained in:
1 parent
61c4728dce
commit
7f460d1257
18 files changed
+847
-240
No files matched your search
@@ -3,12 +3,14 @@ import type { PrFileDto } from '../../gitea/api'
|
||||
import type { RepoContext } from '../../gitea/repoContext'
|
||||
import type { Issue } from '../../gitea/types'
|
||||
import type { FileNode, FileTreeProvider } from '../explorer/fileTree'
|
||||
import type { TreeFilter, TreeFilters } from '../explorer/treeFilters'
|
||||
import type { FileCount } from './pickList'
|
||||
import * as path from 'node:path'
|
||||
import { commands, QuickPickItemKind, Uri, window, workspace } from 'vscode'
|
||||
import { QuickPickItemKind, Uri, window, workspace } from 'vscode'
|
||||
import { logger } from '../../logging/logger'
|
||||
import { buildChangedTree } from '../explorer/changedTree'
|
||||
import { ChangeDecorations } from './changeDecorations'
|
||||
import { buildChangedTree, changeOf } from './changedFiles'
|
||||
import { changeOf } from './changedFiles'
|
||||
import { countingFiles, pickEntries, pickItemText, pickRows, withFileCount } from './pickList'
|
||||
import { columnLabel, REVIEW_COLUMNS_TEXT, reviewableIssue, reviewablePullRequest, reviewDescription, reviewMessage } from './reviewRules'
|
||||
import { loadIssue, loadPullRequest, loadPullRequestFiles, requireRepo, reviewCandidateIssues } from './reviewSource'
|
||||
@@ -16,8 +18,6 @@ import { localWorktree, prepareWorktree } from './reviewWorktree'
|
||||
|
||||
/** workspaceState:正在审查的工单号,重开窗口接着审查。 */
|
||||
const REVIEW_ISSUE_KEY = 'superpowers.codeReader.reviewIssue'
|
||||
/** 上下文键:正在审查工单。标题栏的「显示全部」、行上的注释按钮按它显示 / 藏起来。 */
|
||||
const REVIEWING_CONTEXT = 'superpowers.codeReader.reviewing'
|
||||
|
||||
/**
|
||||
* 怎么进的审查,决定不能审查、出错时怎么收场:
|
||||
@@ -45,8 +45,9 @@ function errorText(err: unknown): string {
|
||||
/**
|
||||
* 侧栏按审查工单筛文件:「选工单」列出看板「审查」「进行中」列里能审查的工单,选中后树里只剩这张工单的 PR 改过的文件(按改动着色),
|
||||
* 点开的是本机这张工单的工作副本里 PR 最新的代码;「显示全部」回到工作区文件树。选的工单按工作区记住。
|
||||
* 和别的筛选(未提交的更改)互斥,见 TreeFilters。
|
||||
*/
|
||||
export class ReviewMode implements Disposable {
|
||||
export class ReviewMode implements TreeFilter, Disposable {
|
||||
private readonly decorations = new ChangeDecorations()
|
||||
private readonly disposables: Disposable[]
|
||||
/** 正在审查的工单号 */
|
||||
@@ -60,25 +61,14 @@ export class ReviewMode implements Disposable {
|
||||
private readonly viewId: string,
|
||||
private readonly view: TreeView<FileNode>,
|
||||
private readonly files: FileTreeProvider,
|
||||
private readonly filters: TreeFilters,
|
||||
) {
|
||||
this.disposables = [this.decorations, window.registerFileDecorationProvider(this.decorations)]
|
||||
// 视图第一次露出来时才接着上次的审查:没打开侧栏就不去拉 PR、动工作副本
|
||||
if (view.visible) {
|
||||
void this.restore()
|
||||
}
|
||||
else {
|
||||
const shown = view.onDidChangeVisibility(({ visible }) => {
|
||||
if (!visible)
|
||||
return
|
||||
shown.dispose()
|
||||
void this.restore()
|
||||
})
|
||||
this.disposables.push(shown)
|
||||
}
|
||||
}
|
||||
|
||||
get active(): boolean {
|
||||
return this.reviewing !== undefined
|
||||
filters.add(this)
|
||||
this.disposables = [
|
||||
this.decorations,
|
||||
window.registerFileDecorationProvider(this.decorations),
|
||||
filters.whenShown(() => void this.restore()),
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,16 +146,13 @@ export class ReviewMode implements Disposable {
|
||||
: []))
|
||||
}
|
||||
|
||||
/** 「显示全部」:回到工作区文件树,不再记着这张工单。 */
|
||||
/** 「显示全部」或换成别的筛选:不再审查、不再记着这张工单,正在进的审查作废;正审查着的回到工作区文件树。 */
|
||||
async exit(): Promise<void> {
|
||||
this.generation++
|
||||
this.reviewing = undefined
|
||||
this.files.showChanges(undefined)
|
||||
this.decorations.show([])
|
||||
this.view.description = undefined
|
||||
this.view.message = undefined
|
||||
await Promise.all([
|
||||
commands.executeCommand('setContext', REVIEWING_CONTEXT, false),
|
||||
this.filters.leave(this),
|
||||
this.context.workspaceState.update(REVIEW_ISSUE_KEY, undefined),
|
||||
])
|
||||
}
|
||||
@@ -261,13 +248,15 @@ export class ReviewMode implements Disposable {
|
||||
const root = Uri.file(worktree)
|
||||
// 工作副本当根,名字照写工作区文件夹的:看的还是这个仓库,只是 PR 最新的代码
|
||||
const folder: WorkspaceFolder = { uri: root, name: workspace.workspaceFolders?.[0]?.name ?? path.basename(worktree), index: 0 }
|
||||
const changes = files.map(file => ({ path: file.path, change: changeOf(file.status) }))
|
||||
const entering = this.filters.enter(this, 'review')
|
||||
this.reviewing = issue.number
|
||||
this.files.showChanges({ folder, tree: buildChangedTree(files) })
|
||||
this.decorations.show(files.map(file => ({ uri: Uri.joinPath(root, file.path), change: changeOf(file.status) })))
|
||||
this.files.showChanges([{ folder, tree: buildChangedTree(changes) }])
|
||||
this.decorations.show(changes.map(file => ({ uri: Uri.joinPath(root, file.path), change: file.change })))
|
||||
this.view.description = reviewDescription(issue.number)
|
||||
this.view.message = reviewMessage(issue, pr, files.length)
|
||||
await Promise.all([
|
||||
commands.executeCommand('setContext', REVIEWING_CONTEXT, true),
|
||||
entering,
|
||||
this.context.workspaceState.update(REVIEW_ISSUE_KEY, issue.number),
|
||||
])
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user