diff --git a/vscode/package.json b/vscode/package.json index a044667..9200365 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -295,12 +295,12 @@ }, { "command": "superpowers.codeReader.hideTests", - "when": "view == superpowers.codeReader.files && superpowers.codeReader.filter == uncommitted && !superpowers.codeReader.testsHidden", + "when": "view == superpowers.codeReader.files && (superpowers.codeReader.filter == uncommitted || superpowers.codeReader.filter == review) && !superpowers.codeReader.testsHidden", "group": "navigation@3" }, { "command": "superpowers.codeReader.showTests", - "when": "view == superpowers.codeReader.files && superpowers.codeReader.filter == uncommitted && superpowers.codeReader.testsHidden", + "when": "view == superpowers.codeReader.files && (superpowers.codeReader.filter == uncommitted || superpowers.codeReader.filter == review) && superpowers.codeReader.testsHidden", "group": "navigation@3" }, { @@ -399,11 +399,11 @@ }, { "command": "superpowers.codeReader.hideTests", - "when": "superpowers.codeReader.filter == uncommitted && !superpowers.codeReader.testsHidden" + "when": "(superpowers.codeReader.filter == uncommitted || superpowers.codeReader.filter == review) && !superpowers.codeReader.testsHidden" }, { "command": "superpowers.codeReader.showTests", - "when": "superpowers.codeReader.filter == uncommitted && superpowers.codeReader.testsHidden" + "when": "(superpowers.codeReader.filter == uncommitted || superpowers.codeReader.filter == review) && superpowers.codeReader.testsHidden" }, { "command": "superpowers.codeReader.stage", diff --git a/vscode/src/codeReader/explorer/rowMenus.test.ts b/vscode/src/codeReader/explorer/rowMenus.test.ts index 483c8e9..4c1ff74 100644 --- a/vscode/src/codeReader/explorer/rowMenus.test.ts +++ b/vscode/src/codeReader/explorer/rowMenus.test.ts @@ -157,12 +157,13 @@ describe('筛选(审查工单、未提交的更改)', () => { expect(whenOf(command)).not.toContain(FILTER) }) - it('隐藏 / 显示测试文件只在未提交的更改时有,按是否隐藏着换着显示', () => { - expect(whenOf(HIDE_TESTS)).toBe(`view == superpowers.codeReader.files && ${FILTER} == uncommitted && !${TESTS_HIDDEN}`) - expect(whenOf(SHOW_TESTS)).toBe(`view == superpowers.codeReader.files && ${FILTER} == uncommitted && ${TESTS_HIDDEN}`) + it('隐藏 / 显示测试文件在未提交的更改和审查时都有(同一个开关),按是否隐藏着换着显示,整棵项目树上没有', () => { + const listing = `(${FILTER} == uncommitted || ${FILTER} == review)` + expect(whenOf(HIDE_TESTS)).toBe(`view == superpowers.codeReader.files && ${listing} && !${TESTS_HIDDEN}`) + expect(whenOf(SHOW_TESTS)).toBe(`view == superpowers.codeReader.files && ${listing} && ${TESTS_HIDDEN}`) const palette = new Map(menus.commandPalette.map(item => [item.command, item.when])) - expect(palette.get(HIDE_TESTS)).toBe(`${FILTER} == uncommitted && !${TESTS_HIDDEN}`) - expect(palette.get(SHOW_TESTS)).toBe(`${FILTER} == uncommitted && ${TESTS_HIDDEN}`) + expect(palette.get(HIDE_TESTS)).toBe(`${listing} && !${TESTS_HIDDEN}`) + expect(palette.get(SHOW_TESTS)).toBe(`${listing} && ${TESTS_HIDDEN}`) }) it('「暂存」只在未提交的更改时挂在「更改」组的文件行上,命令面板里藏起来', () => { diff --git a/vscode/src/codeReader/explorer/treeFilters.ts b/vscode/src/codeReader/explorer/treeFilters.ts index b42db05..308353a 100644 --- a/vscode/src/codeReader/explorer/treeFilters.ts +++ b/vscode/src/codeReader/explorer/treeFilters.ts @@ -1,10 +1,14 @@ -import type { Event, TreeView, Uri } from 'vscode' +import type { Event, Memento, TreeView, Uri } from 'vscode' import type { DiffBase } from '../diff/diffBase' import type { FileTreeProvider, TreeNode } from './fileTree' import { commands, Disposable, EventEmitter } from 'vscode' /** 上下文键:侧栏树开着哪种筛选,没设是全部文件。标题栏的「显示全部」、行上的注释按钮按它显示 / 藏起来。 */ const FILTER_CONTEXT = 'superpowers.codeReader.filter' +/** workspaceState:隐藏测试文件。几种筛选共用这一个开关。 */ +const HIDE_TESTS_KEY = 'superpowers.codeReader.hideTests' +/** 上下文键:隐藏着测试文件。标题栏的「隐藏测试文件 / 显示测试文件」按它换着显示。 */ +const TESTS_HIDDEN_CONTEXT = 'superpowers.codeReader.testsHidden' /** 侧栏树的筛选:review 审查工单(只摆 PR 改过的文件),uncommitted 未提交的更改(只摆各仓库里没提交的文件)。 */ export type TreeFilterKind = 'review' | 'uncommitted' @@ -17,12 +21,15 @@ export interface TreeFilter { exit: () => Promise /** 开着这种筛选时 uri 这个文件的预览对比什么(见 DiffBase);不在这种筛选管的范围里(仓库外、工作副本外)时没有 */ diffBase: (uri: Uri) => DiffBase | undefined + /** 开着这种筛选时隐藏测试文件的开关换了:按 TreeFilters.testsHidden 马上重摆树 */ + testsToggled: () => void } /** * 侧栏树上的几种筛选,同时只开一种:开一种时别的都退出;上下文键跟着开着的那种。 * 筛选开起来时自己换上树、标题后面的字(description)、标题下面的一行(message);退出时由这里回到工作区文件树。 * 开着的筛选也定打开着的预览对比什么(diffBase),换了筛选、基准变了都通知预览重新比。 + * 隐藏测试文件的开关也在这里:几种筛选共用一个、按工作区记住,换了只叫开着的那种重摆。 */ export class TreeFilters { private readonly filters: TreeFilter[] = [] @@ -30,8 +37,28 @@ export class TreeFilters { private readonly baseChange = new EventEmitter() /** 开着的筛选换了(开了、换了、关了),或它的对比基准变了(git 状态变了、审查重读了 PR):打开着的预览照 diffBase 重新比 */ readonly onDidChangeDiffBase: Event = this.baseChange.event + private hideTests: boolean - constructor(private readonly files: FileTreeProvider, private readonly view: TreeView) {} + /** state 是 workspaceState:隐藏测试文件的开关记在这里。 */ + constructor(private readonly files: FileTreeProvider, private readonly view: TreeView, private readonly state: Memento) { + this.hideTests = state.get(HIDE_TESTS_KEY) ?? false + void commands.executeCommand('setContext', TESTS_HIDDEN_CONTEXT, this.hideTests) + } + + /** 隐藏着测试文件:筛选摆树时把测试文件剔掉 */ + get testsHidden(): boolean { + return this.hideTests + } + + /** 标题栏的「隐藏 / 显示测试文件」:换开关、按工作区记住,开着的筛选马上按新开关重摆。 */ + async setTestsHidden(hidden: boolean): Promise { + this.hideTests = hidden + this.current?.testsToggled() + await Promise.all([ + commands.executeCommand('setContext', TESTS_HIDDEN_CONTEXT, hidden), + this.state.update(HIDE_TESTS_KEY, hidden || undefined), + ]) + } /** 开着的筛选下 uri 这个文件的预览对比什么;没开筛选(全部文件)时没有,预览不标差异。 */ diffBase(uri: Uri): DiffBase | undefined { diff --git a/vscode/src/codeReader/index.ts b/vscode/src/codeReader/index.ts index e52203e..9aeb044 100644 --- a/vscode/src/codeReader/index.ts +++ b/vscode/src/codeReader/index.ts @@ -99,7 +99,7 @@ export function registerCodeReader(context: ExtensionContext): CodeReaderApi { const notes = new FileNotes() const files = new FileTreeProvider(OPEN_COMMAND, notes) const view = window.createTreeView(FILES_VIEW, { treeDataProvider: files, showCollapseAll: true }) - const filters = new TreeFilters(files, view) + const filters = new TreeFilters(files, view, context.workspaceState) const review = new ReviewMode(context, FILES_VIEW, view, files, filters) const uncommitted = new UncommittedMode(context, FILES_VIEW, view, files, filters) // 侧栏开着的筛选定预览对比什么:未提交的更改对比上一次提交,审查工单对比 PR 的目标分支 @@ -135,8 +135,8 @@ export function registerCodeReader(context: ExtensionContext): CodeReaderApi { commands.registerCommand('superpowers.codeReader.refresh', () => filters.active ? filters.active.reload() : files.refresh()), commands.registerCommand('superpowers.codeReader.pickReviewIssue', () => review.pick()), commands.registerCommand('superpowers.codeReader.showUncommitted', () => uncommitted.enter()), - commands.registerCommand('superpowers.codeReader.hideTests', () => uncommitted.setTestsHidden(true)), - commands.registerCommand('superpowers.codeReader.showTests', () => uncommitted.setTestsHidden(false)), + commands.registerCommand('superpowers.codeReader.hideTests', () => filters.setTestsHidden(true)), + commands.registerCommand('superpowers.codeReader.showTests', () => filters.setTestsHidden(false)), // 「更改」组文件行上的按钮传来的是这一行的节点 commands.registerCommand('superpowers.codeReader.stage', (node?: FileNode) => uncommitted.stage(node)), commands.registerCommand('superpowers.codeReader.showAllFiles', () => filters.active?.exit()), diff --git a/vscode/src/codeReader/review/changedFiles.test.ts b/vscode/src/codeReader/review/changedFiles.test.ts index 43c335e..ee523e8 100644 --- a/vscode/src/codeReader/review/changedFiles.test.ts +++ b/vscode/src/codeReader/review/changedFiles.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { CHANGE_STYLES, changeOf, reviewBaseFile } from './changedFiles' +import { CHANGE_STYLES, changeOf, reviewBaseFile, reviewTree } from './changedFiles' describe('改动状态', () => { it('新增 A、删除 D,别的(改名、复制、改了、没改)都算修改 M', () => { @@ -31,3 +31,39 @@ describe('审查时在 PR 目标分支上取哪个文件', () => { expect(reviewBaseFile(files, 'src/untouched.ts')).toEqual({ path: 'src/untouched.ts', added: false }) }) }) + +describe('审查时树上摆哪些文件', () => { + const files = [ + { path: 'backend/app.py', status: 'changed' }, + { path: 'backend/test_app.py', status: 'added' }, + { path: 'backend/tests/test_avatar.py', status: 'added' }, + { path: 'backend/tests/fixtures/avatar.png', status: 'added' }, + { path: 'web/src/__tests__/Avatar.tsx', status: 'changed' }, + { path: 'web/src/Avatar.tsx', status: 'changed' }, + ] + const names = (tree: ReturnType['tree'], dir: string): string[] => (tree.get(dir) ?? []).map(entry => entry.name) + + it('隐藏测试文件时剔掉 tests/、__tests__/ 下的和 test_*.py,只装着测试文件的目录不摆,数出剔了几个', () => { + const { tree, hidden } = reviewTree(files, true) + expect(hidden).toBe(4) + expect(names(tree, '')).toEqual(['backend', 'web']) + expect(names(tree, 'backend')).toEqual(['app.py']) + expect(names(tree, 'web/src')).toEqual(['Avatar.tsx']) + expect(tree.has('backend/tests')).toBe(false) + expect(tree.has('backend/tests/fixtures')).toBe(false) + expect(tree.has('web/src/__tests__')).toBe(false) + }) + + it('不隐藏时 PR 改过的都在,剔了 0 个', () => { + const { tree, hidden } = reviewTree(files, false) + expect(hidden).toBe(0) + expect(names(tree, 'backend')).toEqual(['tests', 'app.py', 'test_app.py']) + expect(names(tree, 'backend/tests')).toEqual(['fixtures', 'test_avatar.py']) + expect(names(tree, 'web/src')).toEqual(['__tests__', 'Avatar.tsx']) + }) + + it('改动状态照 Gitea 的', () => { + const { tree } = reviewTree(files, true) + expect(tree.get('backend')?.[0]).toMatchObject({ path: 'backend/app.py', change: 'M' }) + }) +}) diff --git a/vscode/src/codeReader/review/changedFiles.ts b/vscode/src/codeReader/review/changedFiles.ts index 6911b91..c73db35 100644 --- a/vscode/src/codeReader/review/changedFiles.ts +++ b/vscode/src/codeReader/review/changedFiles.ts @@ -1,4 +1,6 @@ -import type { FileChange } from '../explorer/changedTree' +import type { ChangedTree, FileChange } from '../explorer/changedTree' +import { buildChangedTree } from '../explorer/changedTree' +import { isTestFile } from '../uncommitted/testFiles' /** * Gitea 给的改动状态 → M / A / D。Gitea 的状态有 added、deleted、changed、renamed、copied、unchanged: @@ -8,6 +10,16 @@ export function changeOf(status: string): FileChange { return status === 'added' ? 'A' : status === 'deleted' ? 'D' : 'M' } +/** + * 审查时树上摆的:PR 改过的文件和通往它们的目录。隐藏测试文件时先剔掉测试文件再摆,只装着测试文件的目录跟着不摆; + * hidden 是剔了几个。 + */ +export function reviewTree(files: readonly { path: string, status: string }[], hideTests: boolean): { tree: ChangedTree, hidden: number } { + const changes = files.map(file => ({ path: file.path, change: changeOf(file.status) })) + const shown = hideTests ? changes.filter(file => !isTestFile(file.path)) : changes + return { tree: buildChangedTree(shown), hidden: changes.length - shown.length } +} + /** * 审查时工作副本里的一个文件(path 相对工作副本的根)在 PR 目标分支上叫什么、有没有: * 改名的取改名前的路径,PR 里新加的(Gitea 的 added)目标分支上没有。PR 没改过的文件照原路径取。 diff --git a/vscode/src/codeReader/review/reviewMode.ts b/vscode/src/codeReader/review/reviewMode.ts index 2d45540..13a7d2d 100644 --- a/vscode/src/codeReader/review/reviewMode.ts +++ b/vscode/src/codeReader/review/reviewMode.ts @@ -10,10 +10,9 @@ import * as path from 'node:path' import { QuickPickItemKind, Uri, window, workspace } from 'vscode' import { fileAtRef } from '../../git/fileAt' import { logger } from '../../logging/logger' -import { buildChangedTree } from '../explorer/changedTree' import { toPathKey } from '../pathMap' import { ChangeDecorations } from './changeDecorations' -import { changeOf, reviewBaseFile } from './changedFiles' +import { changeOf, reviewBaseFile, reviewTree } from './changedFiles' import { countingFiles, pickEntries, pickItemText, pickRows, withFileCount } from './pickList' import { columnLabel, REVIEW_COLUMNS_TEXT, reviewableIssue, reviewablePullRequest, reviewBaseLabel, reviewBaseMissing, reviewDescription, reviewMessage } from './reviewRules' import { loadIssue, loadPullRequest, loadPullRequestFiles, requireRepo, reviewCandidateIssues } from './reviewSource' @@ -189,6 +188,11 @@ export class ReviewMode implements TreeFilter, Disposable { } } + /** 隐藏测试文件的开关换了:正审查着的文件按新开关重摆。 */ + testsToggled(): void { + this.place() + } + dispose(): void { for (const disposable of this.disposables) disposable.dispose() @@ -272,21 +276,29 @@ export class ReviewMode implements TreeFilter, Disposable { } private async show(loaded: Loaded): Promise { - const { issue, pr, files, worktree } = loaded - 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 root = Uri.file(loaded.worktree) // 先记下再进:进的时候打开着的预览就来问对比基准 this.loaded = loaded const entering = this.filters.enter(this, 'review') - this.files.showChanges([{ roots: [{ 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) + this.place() + this.decorations.show(loaded.files.map(file => ({ uri: Uri.joinPath(root, file.path), change: changeOf(file.status) }))) await Promise.all([ entering, - this.context.workspaceState.update(REVIEW_ISSUE_KEY, issue.number), + this.context.workspaceState.update(REVIEW_ISSUE_KEY, loaded.issue.number), ]) } + + /** 把正审查着的 PR 改过的文件摆上树(隐藏测试文件时剔掉),标题后面写上藏了几个。 */ + private place(): void { + const loaded = this.loaded + if (!loaded) + return + const { issue, pr, files, worktree } = loaded + // 工作副本当根,名字照写工作区文件夹的:看的还是这个仓库,只是 PR 最新的代码 + const folder: WorkspaceFolder = { uri: Uri.file(worktree), name: workspace.workspaceFolders?.[0]?.name ?? path.basename(worktree), index: 0 } + const { tree, hidden } = reviewTree(files, this.filters.testsHidden) + this.files.showChanges([{ roots: [{ folder, tree }] }]) + this.view.description = reviewDescription(issue.number, hidden) + this.view.message = reviewMessage(issue, pr, files.length) + } } diff --git a/vscode/src/codeReader/review/reviewRules.test.ts b/vscode/src/codeReader/review/reviewRules.test.ts index 38c7590..011568c 100644 --- a/vscode/src/codeReader/review/reviewRules.test.ts +++ b/vscode/src/codeReader/review/reviewRules.test.ts @@ -5,9 +5,13 @@ const issue = { number: 446, title: '用户资料:上传头像,收紧非管 describe('审查时的视图标题', () => { it('标题后面「审查 #446」,下面一行工单标题、PR、改了几个文件', () => { - expect(reviewDescription(446)).toBe('审查 #446') + expect(reviewDescription(446, 0)).toBe('审查 #446') expect(reviewMessage(issue, 447, 6)).toBe('#446 用户资料:上传头像,收紧非管理员改资料的权限 · PR #447 · 改了 6 个文件') }) + + it('隐藏了测试文件时标题后面接上藏了几个,和「未提交的更改」一个说法', () => { + expect(reviewDescription(446, 3)).toBe('审查 #446 · 已隐藏 3 个测试文件') + }) }) describe('选工单弹框列哪些工单', () => { diff --git a/vscode/src/codeReader/review/reviewRules.ts b/vscode/src/codeReader/review/reviewRules.ts index c402cf0..8cec88d 100644 --- a/vscode/src/codeReader/review/reviewRules.ts +++ b/vscode/src/codeReader/review/reviewRules.ts @@ -1,4 +1,5 @@ import type { Issue, IssueColumn } from '../../gitea/types' +import { withHiddenTests } from '../uncommitted/testFiles' /** 工单关联的 PR 号;state JSON 里没有、或不是正整数时没有。 */ export function prNumberOf(issue: Pick): number | undefined { @@ -67,9 +68,9 @@ export function reviewablePullRequest TEST_DIRS.has(dir.toLowerCase())) || TEST_FILE_NAMES.some(pattern => pattern.test(name)) } + +/** 视图标题后面淡色的字(TreeView.description):藏了测试文件时在后面注明藏了几个。「未提交的更改」和审查用同一个说法。 */ +export function withHiddenTests(description: string, hiddenTests: number): string { + return hiddenTests > 0 ? `${description} · 已隐藏 ${hiddenTests} 个测试文件` : description +} diff --git a/vscode/src/codeReader/uncommitted/uncommittedFiles.ts b/vscode/src/codeReader/uncommitted/uncommittedFiles.ts index 0b6041d..67487ca 100644 --- a/vscode/src/codeReader/uncommitted/uncommittedFiles.ts +++ b/vscode/src/codeReader/uncommitted/uncommittedFiles.ts @@ -3,7 +3,7 @@ import type { ChangedFile, FileChange } from '../explorer/changedTree' import { posix } from 'node:path' import { GIT_STATUS } from '../../git/gitApi' import { toPathKey } from '../pathMap' -import { isTestFile } from './testFiles' +import { isTestFile, withHiddenTests } from './testFiles' /** 一组改动里的一项:文件在哪(Uri.path)、git 状态。 */ interface StatusEntry { @@ -149,7 +149,7 @@ export const UNSTAGED_MARKER = 'unstaged' /** 开着时视图标题后面淡色的字(TreeView.description):藏了测试文件时注明藏了几个。 */ export function uncommittedDescription(hiddenTests: number): string { - return hiddenTests > 0 ? `未提交的更改 · 已隐藏 ${hiddenTests} 个测试文件` : '未提交的更改' + return withHiddenTests('未提交的更改', hiddenTests) } /** diff --git a/vscode/src/codeReader/uncommitted/uncommittedMode.ts b/vscode/src/codeReader/uncommitted/uncommittedMode.ts index 5107fae..29041a1 100644 --- a/vscode/src/codeReader/uncommitted/uncommittedMode.ts +++ b/vscode/src/codeReader/uncommitted/uncommittedMode.ts @@ -6,7 +6,7 @@ import type { ChangedSection, FileNode, FileTreeProvider, TreeNode } from '../ex import type { TreeFilter, TreeFilters } from '../explorer/treeFilters' import type { UncommittedRoot } from './uncommittedFiles' import * as path from 'node:path' -import { commands, Disposable, window, workspace } from 'vscode' +import { Disposable, window, workspace } from 'vscode' import { fileAtRef } from '../../git/fileAt' import { loadGitApi } from '../../git/gitExtension' import { logger } from '../../logging/logger' @@ -16,10 +16,6 @@ import { HEAD_BASE_LABEL, headMissingText, isUntracked, uncommittedDescription, /** workspaceState:开着「未提交的更改」,重开窗口接着显示。 */ const UNCOMMITTED_KEY = 'superpowers.codeReader.uncommitted' -/** workspaceState:隐藏测试文件。 */ -const HIDE_TESTS_KEY = 'superpowers.codeReader.hideTests' -/** 上下文键:隐藏着测试文件。标题栏的「隐藏测试文件 / 显示测试文件」按它换着显示。 */ -const TESTS_HIDDEN_CONTEXT = 'superpowers.codeReader.testsHidden' /** 两个分组的名字,照 VS Code 源代码管理视图的中文界面。 */ const STAGED_LABEL = '暂存的更改' const CHANGES_LABEL = '更改' @@ -36,8 +32,8 @@ function errorText(err: unknown): string { /** * 侧栏只看未提交的更改:照源代码管理视图分「暂存的更改」「更改」两组(空的不摆),每组下面每个有改动的仓库一个根。 * 文件就是工作区里的文件,颜色和 M / A / D 徽标由 VS Code 自带的 git 装饰加上;「更改」组里的文件行上有「暂存」按钮。 - * 可以隐藏测试文件。开着时盯着 git 状态自动重摆,打开的预览对比上一次提交(diffBase);「显示全部」回到工作区文件树。 - * 开着没开、隐不隐藏测试文件都按工作区记住;和审查工单互斥,见 TreeFilters。 + * 可以隐藏测试文件(和审查共用一个开关,见 TreeFilters.testsHidden)。开着时盯着 git 状态自动重摆,打开的预览对比上一次提交(diffBase); + * 「显示全部」回到工作区文件树。开着没开按工作区记住;和审查工单互斥,见 TreeFilters。 */ export class UncommittedMode implements TreeFilter, Disposable { private readonly disposables: Disposable[] @@ -50,7 +46,6 @@ export class UncommittedMode implements TreeFilter, Disposable { private shown: string | undefined /** 每进一次、退一次都加一:等 git 扩展回来时已经退出或又进了一次的,结果丢掉。 */ private generation = 0 - private hideTests: boolean /** viewId 是侧栏视图的 id:刷新时在这个视图上转进度。 */ constructor( @@ -60,8 +55,6 @@ export class UncommittedMode implements TreeFilter, Disposable { private readonly files: FileTreeProvider, private readonly filters: TreeFilters, ) { - this.hideTests = context.workspaceState.get(HIDE_TESTS_KEY) ?? false - void commands.executeCommand('setContext', TESTS_HIDDEN_CONTEXT, this.hideTests) filters.add(this) this.disposables = [filters.whenShown(() => void this.restore())] } @@ -95,14 +88,9 @@ export class UncommittedMode implements TreeFilter, Disposable { this.rebuild() } - /** 「隐藏测试文件 / 显示测试文件」:两组都剔除 / 放回测试文件,按工作区记住。 */ - async setTestsHidden(hidden: boolean): Promise { - this.hideTests = hidden + /** 隐藏测试文件的开关换了:两组都剔除 / 放回测试文件。 */ + testsToggled(): void { this.rebuild() - await Promise.all([ - commands.executeCommand('setContext', TESTS_HIDDEN_CONTEXT, hidden), - this.context.workspaceState.update(HIDE_TESTS_KEY, hidden || undefined), - ]) } /** 「更改」组里文件行上的「暂存」:把这个文件放进暂存区(新文件、删掉的、冲突的都行),树跟着 git 状态变。 */ @@ -244,7 +232,7 @@ export class UncommittedMode implements TreeFilter, Disposable { return const folders = workspace.workspaceFolders ?? [] const all = uncommittedRoots(api.repositories, folders) - const { roots, hidden } = this.hideTests ? withoutTestFiles(all) : { roots: all, hidden: 0 } + const { roots, hidden } = this.filters.testsHidden ? withoutTestFiles(all) : { roots: all, hidden: 0 } this.view.description = uncommittedDescription(hidden) this.view.message = uncommittedMessage({ hasFiles: roots.length > 0, hiddenTests: hidden, loading: api.state !== 'initialized' }) const shown = JSON.stringify(roots.map(root => [root.repository.rootUri.toString(), root.name, root.staged, root.changes]))