Skip to content

Commit 4e7c81e

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 81c0a12 commit 4e7c81e

File tree

4 files changed

+77
-12
lines changed

4 files changed

+77
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
99
### Added
1010

1111
- Adds support for OpenAI's GPT-4 Turbo and latest Anthropic models for GitLens' experimental AI features — closes [#3005](https://github.com/gitkraken/vscode-gitlens/issues/3005)
12+
- Adds support for opening renamed/deleted files using the _Open File at Revision..._ commands by showing a quickpick 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))
1213

1314
### Changed
1415

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: 72 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, workspace } from 'vscode';
3+
import type { Disposable } from '../../api/gitlens';
34
import type { DiffWithCommandArgs } from '../../commands/diffWith';
45
import type { DiffWithPreviousCommandArgs } from '../../commands/diffWithPrevious';
56
import type { DiffWithWorkingCommandArgs } from '../../commands/diffWithWorking';
@@ -13,7 +14,7 @@ import { Commands } from '../../constants';
1314
import { Container } from '../../container';
1415
import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol';
1516
import { executeCommand, executeEditorCommand } from '../../system/command';
16-
import { findOrOpenEditor, findOrOpenEditors } from '../../system/utils';
17+
import { findOrOpenEditor, findOrOpenEditors, getQuickPickIgnoreFocusOut } from '../../system/utils';
1718
import { GitUri } from '../gitUri';
1819
import type { GitCommit } from '../models/commit';
1920
import { isCommit } from '../models/commit';
@@ -402,7 +403,7 @@ export async function openFileAtRevision(
402403
commitOrOptions?: GitCommit | TextDocumentShowOptions,
403404
options?: TextDocumentShowOptions & { annotationType?: FileAnnotationType; line?: number },
404405
): Promise<void> {
405-
let uri;
406+
let uri: Uri;
406407
if (fileOrRevisionUri instanceof Uri) {
407408
if (isCommit(commitOrOptions)) throw new Error('Invalid arguments');
408409

@@ -440,11 +441,74 @@ export async function openFileAtRevision(
440441
opts.selection = new Range(line, 0, line, 0);
441442
}
442443

443-
const editor = await findOrOpenEditor(uri, opts);
444-
if (annotationType != null && editor != null) {
445-
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
446-
selection: { line: line },
447-
}));
444+
const gitUri = await GitUri.fromUri(uri);
445+
446+
let editor: TextEditor | undefined;
447+
try {
448+
editor = await findOrOpenEditor(uri, { throwOnError: true, ...opts }).catch(error => {
449+
if (error?.message?.includes('Unable to resolve nonexistent file')) {
450+
return pickFileAtRevision(gitUri, {
451+
title: 'File not found in revision - pick another file to open instead',
452+
}).then(pickedUri => {
453+
return pickedUri ? findOrOpenEditor(pickedUri, opts) : undefined;
454+
});
455+
}
456+
throw error;
457+
});
458+
459+
if (annotationType != null && editor != null) {
460+
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
461+
selection: { line: line },
462+
}));
463+
}
464+
} catch (error) {
465+
await window.showErrorMessage(
466+
`Unable to open '${gitUri.relativePath}' - file doesn't exist in selected revision`,
467+
);
468+
}
469+
}
470+
471+
export async function pickFileAtRevision(
472+
uri: GitUri,
473+
options: {
474+
title: string;
475+
initialPath?: string;
476+
},
477+
): Promise<Uri | undefined> {
478+
const disposables: Disposable[] = [];
479+
try {
480+
const picker = window.createQuickPick();
481+
Object.assign(picker, {
482+
title: options.title,
483+
value: options.initialPath ?? uri.relativePath,
484+
placeholder: 'Enter path to file...',
485+
busy: true,
486+
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
487+
});
488+
picker.show();
489+
490+
const tree = await Container.instance.git.getTreeForRevision(uri.repoPath, uri.sha!);
491+
picker.items = tree.filter(file => file.type === 'blob').map((file): QuickPickItem => ({ label: file.path }));
492+
picker.busy = false;
493+
494+
const pick = await new Promise<string | undefined>(resolve => {
495+
disposables.push(
496+
picker,
497+
picker.onDidHide(() => resolve(undefined)),
498+
picker.onDidAccept(() => {
499+
if (picker.activeItems.length === 0) return;
500+
resolve(picker.activeItems[0].label);
501+
}),
502+
);
503+
});
504+
505+
return pick
506+
? Container.instance.git.getRevisionUri(uri.sha!, `${uri.repoPath}/${pick}`, uri.repoPath!)
507+
: undefined;
508+
} finally {
509+
disposables.forEach(d => {
510+
d.dispose();
511+
});
448512
}
449513
}
450514

src/system/utils.ts

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

126126
export async function openEditor(
127127
uri: Uri,
128-
options: TextDocumentShowOptions & { rethrow?: boolean } = {},
128+
options: TextDocumentShowOptions & { throwOnError?: boolean } = {},
129129
): Promise<TextEditor | undefined> {
130-
const { rethrow, ...opts } = options;
130+
const { throwOnError, ...opts } = options;
131131
try {
132132
if (isGitUri(uri)) {
133133
uri = uri.documentUri();
@@ -154,7 +154,7 @@ export async function openEditor(
154154
return undefined;
155155
}
156156

157-
if (rethrow) throw ex;
157+
if (throwOnError) throw ex;
158158

159159
Logger.error(ex, 'openEditor');
160160
return undefined;

0 commit comments

Comments
 (0)