diff --git a/src/commands/common.ts b/src/commands/common.ts index 1affbd8..b920472 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -512,12 +512,13 @@ export abstract class EditorCommand implements Disposable { abstract execute(editor: TextEditor, edit: TextEditorEdit, ...args: any[]): any; } -export function findEditor(uri: Uri, options?: { includeDiffs?: boolean }): TextEditor | undefined { +export function findEditor(uri: Uri): TextEditor | undefined { const active = window.activeTextEditor; - const normalizedUri = uri.toString(false); + const normalizedUri = uri.toString(); for (const e of [...(active != null ? [active] : []), ...window.visibleTextEditors]) { - if (e.document.uri.toString(false) === normalizedUri && (options?.includeDiffs || e?.viewColumn != null)) { + // Don't include diff editors + if (e.document.uri.toString() === normalizedUri && e?.viewColumn != null) { return e; } } @@ -527,19 +528,33 @@ export function findEditor(uri: Uri, options?: { includeDiffs?: boolean }): Text export async function findOrOpenEditor( uri: Uri, - openOptions?: TextDocumentShowOptions & { throwOnError?: boolean }, - findOptions?: { includeDiffs?: boolean }, + options?: TextDocumentShowOptions & { throwOnError?: boolean }, ): Promise { - const e = findEditor(uri, findOptions); + const e = findEditor(uri); if (e != null) { - if (!openOptions?.preserveFocus) { - await window.showTextDocument(e.document, { ...openOptions, viewColumn: e.viewColumn }); + if (!options?.preserveFocus) { + await window.showTextDocument(e.document, { ...options, viewColumn: e.viewColumn }); } return e; } - return openEditor(uri, { viewColumn: window.activeTextEditor?.viewColumn, ...openOptions }); + return openEditor(uri, { viewColumn: window.activeTextEditor?.viewColumn, ...options }); +} + +export function findOrOpenEditors(uris: Uri[]): void { + const normalizedUris = new Map(uris.map(uri => [uri.toString(), uri])); + + for (const e of window.visibleTextEditors) { + // Don't include diff editors + if (e?.viewColumn != null) { + normalizedUris.delete(e.document.uri.toString()); + } + } + + for (const uri of normalizedUris.values()) { + void commands.executeCommand(BuiltInCommands.Open, uri, { background: true, preview: false }); + } } export async function openEditor( diff --git a/src/commands/gitCommands.actions.ts b/src/commands/gitCommands.actions.ts index 0e6990b..157562a 100644 --- a/src/commands/gitCommands.actions.ts +++ b/src/commands/gitCommands.actions.ts @@ -7,9 +7,11 @@ import { executeCommand, executeEditorCommand, findOrOpenEditor, + findOrOpenEditors, GitCommandsCommandArgs, OpenWorkingFileCommandArgs, } from '../commands'; +import { FileAnnotationType } from '../configuration'; import { Container } from '../container'; import { GitBranchReference, @@ -24,7 +26,6 @@ import { Repository, } from '../git/git'; import { GitUri } from '../git/gitUri'; -import { FileAnnotationType } from '../configuration'; export async function executeGitCommand(args: GitCommandsCommandArgs): Promise { void (await executeCommand(Commands.GitCommands, args)); @@ -1025,15 +1026,30 @@ export namespace GitActions { } } + export async function openFile(uri: Uri, options?: TextDocumentShowOptions): Promise; export async function openFile( file: string | GitFile, ref: GitRevisionReference, options?: TextDocumentShowOptions, + ): Promise; + export async function openFile( + fileOrUri: string | GitFile | Uri, + refOrOptions?: GitRevisionReference | TextDocumentShowOptions, + options?: TextDocumentShowOptions, ) { + let uri; + if (fileOrUri instanceof Uri) { + uri = fileOrUri; + options = refOrOptions as TextDocumentShowOptions; + } else { + const ref = refOrOptions as GitRevisionReference; + uri = GitUri.fromFile(fileOrUri, ref.repoPath, ref.ref); + } + options = { preserveFocus: true, preview: false, ...options }; void (await executeEditorCommand(Commands.OpenWorkingFile, undefined, { - uri: GitUri.fromFile(file, ref.repoPath, ref.ref), + uri: uri, showOptions: options, })); } @@ -1097,38 +1113,74 @@ export namespace GitActions { } } - export async function openFiles(commit: GitLogCommit, options?: TextDocumentShowOptions) { - if (commit.files.length > 20) { + export async function openFiles(commit: GitLogCommit): Promise; + export async function openFiles(files: GitFile[], repoPath: string, ref: string): Promise; + export async function openFiles( + commitOrFiles: GitLogCommit | GitFile[], + repoPath?: string, + ref?: string, + ): Promise { + let files; + if (GitLogCommit.is(commitOrFiles)) { + files = commitOrFiles.files; + repoPath = commitOrFiles.repoPath; + ref = commitOrFiles.sha; + } else { + files = commitOrFiles; + } + + if (files.length > 20) { const result = await window.showWarningMessage( - `Are your sure you want to open all ${commit.files.length} files?`, + `Are your sure you want to open all ${files.length} files?`, { title: 'Yes' }, { title: 'No', isCloseAffordance: true }, ); if (result == null || result.title === 'No') return; } - options = { preserveFocus: true, preview: false, ...options }; + const uris: Uri[] = ( + await Promise.all( + files.map(file => Container.git.getWorkingUri(repoPath!, GitUri.fromFile(file, repoPath!, ref))), + ) + ).filter(Boolean) as Uri[]; + findOrOpenEditors(uris); + } - for (const file of commit.files) { - void (await openFile(file, commit, options)); + export async function openFilesAtRevision(commit: GitLogCommit): Promise; + export async function openFilesAtRevision( + files: GitFile[], + repoPath: string, + ref1: string, + ref2: string, + ): Promise; + export async function openFilesAtRevision( + commitOrFiles: GitLogCommit | GitFile[], + repoPath?: string, + ref1?: string, + ref2?: string, + ): Promise { + let files; + if (GitLogCommit.is(commitOrFiles)) { + files = commitOrFiles.files; + repoPath = commitOrFiles.repoPath; + ref1 = commitOrFiles.sha; + ref2 = commitOrFiles.previousFileSha; + } else { + files = commitOrFiles; } - } - export async function openFilesAtRevision(commit: GitLogCommit, options?: TextDocumentShowOptions) { - if (commit.files.length > 20) { + if (files.length > 20) { const result = await window.showWarningMessage( - `Are your sure you want to open all ${commit.files.length} revisions?`, + `Are your sure you want to open all ${files.length} revisions?`, { title: 'Yes' }, { title: 'No', isCloseAffordance: true }, ); if (result == null || result.title === 'No') return; } - options = { preserveFocus: true, preview: false, ...options }; - - for (const file of commit.files) { - void (await openFileAtRevision(file, commit, options)); - } + findOrOpenEditors( + files.map(file => GitUri.toRevisionUri(file.status === 'D' ? ref2! : ref1!, file, repoPath!)), + ); } export async function restoreFile(file: string | GitFile, ref: GitRevisionReference) { diff --git a/src/commands/openChangedFiles.ts b/src/commands/openChangedFiles.ts index a4f3a10..2fa69bf 100644 --- a/src/commands/openChangedFiles.ts +++ b/src/commands/openChangedFiles.ts @@ -1,6 +1,6 @@ 'use strict'; import { Uri, window } from 'vscode'; -import { command, Command, Commands, findOrOpenEditor, getRepoPathOrPrompt } from './common'; +import { Command, command, Commands, findOrOpenEditors, getRepoPathOrPrompt } from './common'; import { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; @@ -34,9 +34,7 @@ export class OpenChangedFilesCommand extends Command { args.uris = Arrays.filterMap(status.files, f => (f.status !== 'D' ? f.uri : undefined)); } - for (const uri of args.uris) { - void (await findOrOpenEditor(uri, { preserveFocus: true, preview: false })); - } + findOrOpenEditors(args.uris); } catch (ex) { Logger.error(ex, 'OpenChangedFilesCommand'); void Messages.showGenericErrorMessage('Unable to open all changed files'); diff --git a/src/quickpicks/commitQuickPickItems.ts b/src/quickpicks/commitQuickPickItems.ts index e7ed591..4ffa291 100644 --- a/src/quickpicks/commitQuickPickItems.ts +++ b/src/quickpicks/commitQuickPickItems.ts @@ -182,8 +182,8 @@ export class CommitOpenFilesCommandQuickPickItem extends CommandQuickPickItem { super(item ?? '$(files) Open Files'); } - execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openFiles(this.commit, options); + execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openFiles(this.commit); } } @@ -202,8 +202,8 @@ export class CommitOpenRevisionsCommandQuickPickItem extends CommandQuickPickIte super(item ?? '$(files) Open Files at Revision'); } - execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openFilesAtRevision(this.commit, options); + execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openFilesAtRevision(this.commit); } } diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts index a75b97f..d5fe992 100644 --- a/src/views/viewCommands.ts +++ b/src/views/viewCommands.ts @@ -7,11 +7,9 @@ import { DiffWithWorkingCommandArgs, executeCommand, executeEditorCommand, - findOrOpenEditor, GitActions, OpenFileAtRevisionCommandArgs, OpenFileOnRemoteCommandArgs, - OpenWorkingFileCommandArgs, } from '../commands'; import { FileAnnotationType } from '../config'; import { BuiltInCommands, CommandContext, setCommandContext } from '../constants'; @@ -748,61 +746,30 @@ export class ViewCommands { return; } - void (await executeEditorCommand(Commands.OpenWorkingFile, undefined, { - uri: node.uri, - showOptions: { - preserveFocus: true, - preview: false, - }, - })); + await GitActions.Commit.openFile(node.uri, { + preserveFocus: true, + preview: false, + }); } @debug() - private async openFiles(node: CommitNode | StashNode | ResultsFilesNode, options?: TextDocumentShowOptions) { + private async openFiles(node: CommitNode | StashNode | ResultsFilesNode) { if (!(node instanceof CommitNode) && !(node instanceof StashNode) && !(node instanceof ResultsFilesNode)) { return; } - options = { preserveFocus: false, preview: false, ...options }; - - let repoPath: string; - let files; - let ref: string; - if (node instanceof ResultsFilesNode) { const { diff } = await node.getFilesQueryResults(); if (diff == null || diff.length === 0) return; - repoPath = node.repoPath; - files = diff; - ref = node.ref1 || node.ref2; + await GitActions.Commit.openFiles(diff, node.repoPath, node.ref1 || node.ref2); } else { - repoPath = node.commit.repoPath; - files = node.commit.files; - ref = node.commit.sha; - } - - if (files.length > 20) { - const result = await window.showWarningMessage( - `Are your sure you want to open all ${files.length} files?`, - { title: 'Yes' }, - { title: 'No', isCloseAffordance: true }, - ); - if (result === undefined || result.title === 'No') return; - } - - for (const file of files) { - const uri = GitUri.fromFile(file, repoPath, ref); - - await executeEditorCommand(Commands.OpenWorkingFile, undefined, { - uri: uri, - showOptions: options, - }); + await GitActions.Commit.openFiles(node.commit); } } @debug() - private openRevision( + private async openRevision( node: CommitFileNode | ResultsFileNode | StashFileNode | StatusFileNode, options?: OpenFileAtRevisionCommandArgs, ) { @@ -812,7 +779,7 @@ export class ViewCommands { !(node instanceof ResultsFileNode) && !(node instanceof StatusFileNode) ) { - return undefined; + return; } options = { showOptions: { preserveFocus: true, preview: false }, ...options }; @@ -833,50 +800,22 @@ export class ViewCommands { } } - return findOrOpenEditor(uri, options.showOptions ?? { preserveFocus: true, preview: false }); + await GitActions.Commit.openFileAtRevision(uri, options.showOptions ?? { preserveFocus: true, preview: false }); } @debug() - private async openRevisions(node: CommitNode | StashNode | ResultsFilesNode, options?: TextDocumentShowOptions) { + private async openRevisions(node: CommitNode | StashNode | ResultsFilesNode, _options?: TextDocumentShowOptions) { if (!(node instanceof CommitNode) && !(node instanceof StashNode) && !(node instanceof ResultsFilesNode)) { return; } - options = { preserveFocus: false, preview: false, ...options }; - - let repoPath: string; - let files; - let ref1: string; - let ref2: string; - if (node instanceof ResultsFilesNode) { const { diff } = await node.getFilesQueryResults(); if (diff == null || diff.length === 0) return; - repoPath = node.repoPath; - files = diff; - ref1 = node.ref1; - ref2 = node.ref2; + await GitActions.Commit.openFilesAtRevision(diff, node.repoPath, node.ref1, node.ref2); } else { - repoPath = node.commit.repoPath; - files = node.commit.files; - ref1 = node.commit.sha; - ref2 = node.commit.previousFileSha; - } - - if (files.length > 20) { - const result = await window.showWarningMessage( - `Are your sure you want to open all ${files.length} files?`, - { title: 'Yes' }, - { title: 'No', isCloseAffordance: true }, - ); - if (result === undefined || result.title === 'No') return; - } - - for (const file of files) { - const uri = GitUri.toRevisionUri(file.status === 'D' ? ref2 : ref1, file, repoPath); - - await findOrOpenEditor(uri, options); + await GitActions.Commit.openFilesAtRevision(node.commit); } }