Skip to content

Commit 9ea07d3

Browse files
committed
Fixes #708 - Prompt for file path when missing from revision
Improves the `openFileAtRevision()` function by: - Asking user to enter another path when the requested file doesn't exist in the selected revision. - Showing an error message on subsequent failure (or if an error is thrown). The above function is used by a number of commands: - `gitlens.openFileRevision` - `gitlens.openFileRevisionFrom` - `gitlens.openRevisionFile` Also renames the `rethrow` argument of `utils.openEditor()` to `throwOnError` to align it with `utils.findOrOpenEditor()`.
1 parent 60b4983 commit 9ea07d3

File tree

4 files changed

+71
-12
lines changed

4 files changed

+71
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Adds support for opening renamed/deleted files using the _Open File at Revision..._ commands by showing a quickpicker prompt if the requested file doesn't exist in the selected revision — thanks to [PR #2825](https://github.com/gitkraken/vscode-gitlens/pull/2825) by Victor Hallberg ([@mogelbrod](https://github.com/mogelbrod))
12+
913
### Changed
1014

1115
- Changes _Compact Graph Column Layout_ context menu command to _Use Compact Graph Column_ for better clarity

src/commands/openFileFromRemote.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class OpenFileFromRemoteCommand extends Command {
5454
}
5555

5656
try {
57-
await openEditor(local.uri, { selection: selection, rethrow: true });
57+
await openEditor(local.uri, { selection: selection, throwOnError: true });
5858
} catch {
5959
const uris = await window.showOpenDialog({
6060
title: 'Open local file',

src/git/actions/commit.ts

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { TextDocumentShowOptions } from 'vscode';
1+
import type { QuickPickItem, TextDocumentShowOptions, TextEditor } from 'vscode';
22
import { env, Range, Uri, window } from 'vscode';
3+
import type { Disposable } from '../../api/gitlens';
34
import type {
45
DiffWithCommandArgs,
56
DiffWithPreviousCommandArgs,
@@ -14,7 +15,7 @@ import { Commands } from '../../constants';
1415
import { Container } from '../../container';
1516
import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol';
1617
import { executeCommand, executeEditorCommand } from '../../system/command';
17-
import { findOrOpenEditor, findOrOpenEditors } from '../../system/utils';
18+
import { findOrOpenEditor, findOrOpenEditors, getQuickPickIgnoreFocusOut } from '../../system/utils';
1819
import { GitUri } from '../gitUri';
1920
import type { GitCommit } from '../models/commit';
2021
import { isCommit } from '../models/commit';
@@ -389,7 +390,7 @@ export async function openFileAtRevision(
389390
commitOrOptions?: GitCommit | TextDocumentShowOptions,
390391
options?: TextDocumentShowOptions & { annotationType?: FileAnnotationType; line?: number },
391392
): Promise<void> {
392-
let uri;
393+
let uri: Uri;
393394
if (fileOrRevisionUri instanceof Uri) {
394395
if (isCommit(commitOrOptions)) throw new Error('Invalid arguments');
395396

@@ -427,11 +428,65 @@ export async function openFileAtRevision(
427428
opts.selection = new Range(line, 0, line, 0);
428429
}
429430

430-
const editor = await findOrOpenEditor(uri, opts);
431-
if (annotationType != null && editor != null) {
432-
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
433-
selection: { line: line },
434-
}));
431+
const gitUri = await GitUri.fromUri(uri);
432+
433+
let editor: TextEditor | undefined;
434+
try {
435+
editor = await findOrOpenEditor(uri, { throwOnError: true, ...opts }).catch(error => {
436+
if (error?.message?.includes('Unable to resolve nonexistent file')) {
437+
return pickFileAtRevision(gitUri, gitUri.relativePath).then(pickedUri => {
438+
return pickedUri ? findOrOpenEditor(pickedUri, opts) : undefined;
439+
});
440+
}
441+
throw error;
442+
});
443+
444+
if (annotationType != null && editor != null) {
445+
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
446+
selection: { line: line },
447+
}));
448+
}
449+
} catch (error) {
450+
await window.showErrorMessage(
451+
`Unable to open '${gitUri.relativePath}' - file doesn't exist in selected revision`,
452+
);
453+
}
454+
}
455+
456+
async function pickFileAtRevision(uri: GitUri, initialPath?: string): Promise<Uri | undefined> {
457+
const disposables: Disposable[] = [];
458+
try {
459+
const picker = window.createQuickPick();
460+
Object.assign(picker, {
461+
title: 'File not found in revision - pick another file to open instead',
462+
placeholder: 'Enter path to file...',
463+
value: initialPath,
464+
busy: true,
465+
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
466+
});
467+
picker.show();
468+
469+
const tree = await Container.instance.git.getTreeForRevision(uri.repoPath, uri.sha!);
470+
picker.items = tree.map((file): QuickPickItem => ({ label: file.path }));
471+
picker.busy = false;
472+
473+
const pick = await new Promise<string | undefined>(resolve => {
474+
disposables.push(
475+
picker,
476+
picker.onDidHide(() => resolve(undefined)),
477+
picker.onDidAccept(() => {
478+
if (picker.activeItems.length === 0) return;
479+
resolve(picker.activeItems[0].label);
480+
}),
481+
);
482+
});
483+
484+
const result = pick ? uri.with({ path: `${uri.repoPath!}/${pick}` }) : undefined;
485+
return result;
486+
} finally {
487+
disposables.forEach(d => {
488+
d.dispose();
489+
});
435490
}
436491
}
437492

src/system/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ export function isTextEditor(editor: TextEditor): boolean {
106106

107107
export async function openEditor(
108108
uri: Uri,
109-
options: TextDocumentShowOptions & { rethrow?: boolean } = {},
109+
options: TextDocumentShowOptions & { throwOnError?: boolean } = {},
110110
): Promise<TextEditor | undefined> {
111-
const { rethrow, ...opts } = options;
111+
const { throwOnError, ...opts } = options;
112112
try {
113113
if (isGitUri(uri)) {
114114
uri = uri.documentUri();
@@ -135,7 +135,7 @@ export async function openEditor(
135135
return undefined;
136136
}
137137

138-
if (rethrow) throw ex;
138+
if (throwOnError) throw ex;
139139

140140
Logger.error(ex, 'openEditor');
141141
return undefined;

0 commit comments

Comments
 (0)