Skip to content

Commit

Permalink
Fixes gitkraken#708 - Prompt for file path when missing from revision
Browse files Browse the repository at this point in the history
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()`.
  • Loading branch information
mogelbrod committed Jul 20, 2023
1 parent 60b4983 commit 55b39b9
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 12 deletions.
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 16
2 changes: 1 addition & 1 deletion src/commands/openFileFromRemote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class OpenFileFromRemoteCommand extends Command {
}

try {
await openEditor(local.uri, { selection: selection, rethrow: true });
await openEditor(local.uri, { selection: selection, throwOnError: true });
} catch {
const uris = await window.showOpenDialog({
title: 'Open local file',
Expand Down
71 changes: 63 additions & 8 deletions src/git/actions/commit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { TextDocumentShowOptions } from 'vscode';
import type { QuickPickItem, TextDocumentShowOptions, TextEditor } from 'vscode';
import { env, Range, Uri, window } from 'vscode';
import type { Disposable } from '../../api/gitlens';
import type {
DiffWithCommandArgs,
DiffWithPreviousCommandArgs,
Expand All @@ -14,7 +15,7 @@ import { Commands } from '../../constants';
import { Container } from '../../container';
import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol';
import { executeCommand, executeEditorCommand } from '../../system/command';
import { findOrOpenEditor, findOrOpenEditors } from '../../system/utils';
import { findOrOpenEditor, findOrOpenEditors, getQuickPickIgnoreFocusOut } from '../../system/utils';
import { GitUri } from '../gitUri';
import type { GitCommit } from '../models/commit';
import { isCommit } from '../models/commit';
Expand Down Expand Up @@ -389,7 +390,7 @@ export async function openFileAtRevision(
commitOrOptions?: GitCommit | TextDocumentShowOptions,
options?: TextDocumentShowOptions & { annotationType?: FileAnnotationType; line?: number },
): Promise<void> {
let uri;
let uri: Uri;
if (fileOrRevisionUri instanceof Uri) {
if (isCommit(commitOrOptions)) throw new Error('Invalid arguments');

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

const editor = await findOrOpenEditor(uri, opts);
if (annotationType != null && editor != null) {
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
selection: { line: line },
}));
const gitUri = await GitUri.fromUri(uri);

let editor: TextEditor | undefined;
try {
editor = await findOrOpenEditor(uri, { throwOnError: true, ...opts }).catch(error => {
if (error?.message?.includes('Unable to resolve nonexistent file')) {
return pickFileAtRevision(gitUri, gitUri.relativePath).then(pickedUri => {
return pickedUri ? findOrOpenEditor(pickedUri, opts) : undefined;
});
}
throw error;
});

if (annotationType != null && editor != null) {
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
selection: { line: line },
}));
}
} catch (error) {
await window.showErrorMessage(
`Unable to open '${gitUri.relativePath}' - file doesn't exist in selected revision`,
);
}
}

async function pickFileAtRevision(uri: GitUri, initialPath?: string): Promise<Uri | undefined> {
const disposables: Disposable[] = [];
try {
const picker = window.createQuickPick();
Object.assign(picker, {
title: 'File not found in revision - pick another file to open instead',
placeholder: 'Enter path to file...',
value: initialPath,
busy: true,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
});
picker.show();

const tree = await Container.instance.git.getTreeForRevision(uri.repoPath, uri.sha!);
picker.items = tree.map((file): QuickPickItem => ({ label: file.path }));
picker.busy = false;

const pick = await new Promise<string | undefined>(resolve => {
disposables.push(
picker,
picker.onDidHide(() => resolve(undefined)),
picker.onDidAccept(() => {
if (picker.activeItems.length === 0) return;
resolve(picker.activeItems[0].label);
}),
);
});

const result = pick ? uri.with({ path: `${uri.repoPath!}/${pick}` }) : undefined;
return result;
} finally {
disposables.forEach(d => {
d.dispose();
});
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/system/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ export function isTextEditor(editor: TextEditor): boolean {

export async function openEditor(
uri: Uri,
options: TextDocumentShowOptions & { rethrow?: boolean } = {},
options: TextDocumentShowOptions & { throwOnError?: boolean } = {},
): Promise<TextEditor | undefined> {
const { rethrow, ...opts } = options;
const { throwOnError, ...opts } = options;
try {
if (isGitUri(uri)) {
uri = uri.documentUri();
Expand All @@ -135,7 +135,7 @@ export async function openEditor(
return undefined;
}

if (rethrow) throw ex;
if (throwOnError) throw ex;

Logger.error(ex, 'openEditor');
return undefined;
Expand Down

0 comments on commit 55b39b9

Please sign in to comment.