Skip to content

Commit 8238ea3

Browse files
committed
#476 "Open File" action is now available in the Visual Studio Code Diff View Title Menu, when the Diff View is opened from Git Graph.
1 parent dbea5a1 commit 8238ea3

9 files changed

Lines changed: 340 additions & 95 deletions

File tree

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@
8989
"category": "Git Graph",
9090
"command": "git-graph.version",
9191
"title": "Get Version Information"
92+
},
93+
{
94+
"category": "Git Graph",
95+
"command": "git-graph.openFile",
96+
"title": "Open File",
97+
"icon": "$(go-to-file)",
98+
"enablement": "isInDiffEditor && resourceScheme == git-graph && git-graph:codiconsSupported"
9299
}
93100
],
94101
"configuration": {
@@ -1422,6 +1429,19 @@
14221429
}
14231430
},
14241431
"menus": {
1432+
"commandPalette": [
1433+
{
1434+
"command": "git-graph.openFile",
1435+
"when": "isInDiffEditor && resourceScheme == git-graph && git-graph:codiconsSupported"
1436+
}
1437+
],
1438+
"editor/title": [
1439+
{
1440+
"command": "git-graph.openFile",
1441+
"group": "navigation",
1442+
"when": "isInDiffEditor && resourceScheme == git-graph && git-graph:codiconsSupported"
1443+
}
1444+
],
14251445
"scm/title": [
14261446
{
14271447
"when": "scmProvider == git && config.git-graph.sourceCodeProviderIntegrationLocation == 'Inline'",

src/commands.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import * as vscode from 'vscode';
33
import { AvatarManager } from './avatarManager';
44
import { getConfig } from './config';
55
import { DataSource } from './dataSource';
6+
import { DiffDocProvider, decodeDiffDocUri } from './diffDocProvider';
67
import { CodeReviewData, CodeReviews, ExtensionState } from './extensionState';
78
import { GitGraphView } from './gitGraphView';
89
import { Logger } from './logger';
910
import { RepoManager } from './repoManager';
10-
import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, abbrevCommit, abbrevText, copyToClipboard, getExtensionVersion, getPathFromUri, getRelativeTimeDiff, getRepoName, isPathInWorkspace, resolveToSymbolicPath, showErrorMessage, showInformationMessage } from './utils';
11+
import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, abbrevCommit, abbrevText, copyToClipboard, doesVersionMeetRequirement, getExtensionVersion, getPathFromUri, getRelativeTimeDiff, getRepoName, isPathInWorkspace, openFile, resolveToSymbolicPath, showErrorMessage, showInformationMessage } from './utils';
1112
import { Disposable } from './utils/disposable';
1213
import { Event } from './utils/event';
1314

@@ -44,6 +45,7 @@ export class CommandManager extends Disposable {
4445
this.repoManager = repoManager;
4546
this.gitExecutable = gitExecutable;
4647

48+
// Register Extension Commands
4749
this.registerCommand('git-graph.view', (arg) => this.view(arg));
4850
this.registerCommand('git-graph.addGitRepository', () => this.addGitRepository());
4951
this.registerCommand('git-graph.removeGitRepository', () => this.removeGitRepository());
@@ -53,12 +55,20 @@ export class CommandManager extends Disposable {
5355
this.registerCommand('git-graph.endSpecificWorkspaceCodeReview', () => this.endSpecificWorkspaceCodeReview());
5456
this.registerCommand('git-graph.resumeWorkspaceCodeReview', () => this.resumeWorkspaceCodeReview());
5557
this.registerCommand('git-graph.version', () => this.version());
58+
this.registerCommand('git-graph.openFile', (arg) => this.openFile(arg));
5659

5760
this.registerDisposable(
5861
onDidChangeGitExecutable((gitExecutable) => {
5962
this.gitExecutable = gitExecutable;
6063
})
6164
);
65+
66+
// Register Extension Contexts
67+
try {
68+
this.registerContext('git-graph:codiconsSupported', doesVersionMeetRequirement(vscode.version, '1.42.0'));
69+
} catch (_) {
70+
this.logger.logError('Unable to set Visual Studio Code Context "git-graph:codiconsSupported"');
71+
}
6272
}
6373

6474
/**
@@ -72,6 +82,18 @@ export class CommandManager extends Disposable {
7282
);
7383
}
7484

85+
/**
86+
* Register a context with Visual Studio Code.
87+
* @param key The Context Key.
88+
* @param value The Context Value.
89+
*/
90+
private registerContext(key: string, value: any) {
91+
return vscode.commands.executeCommand('setContext', key, value).then(
92+
() => this.logger.log('Successfully set Visual Studio Code Context "' + key + '" to "' + JSON.stringify(value) + '"'),
93+
() => this.logger.logError('Failed to set Visual Studio Code Context "' + key + '" to "' + JSON.stringify(value) + '"')
94+
);
95+
}
96+
7597

7698
/* Commands */
7799

@@ -292,6 +314,26 @@ export class CommandManager extends Disposable {
292314
}
293315
}
294316

317+
/**
318+
* Opens a file in Visual Studio Code, based on a Git Graph URI (from the Diff View).
319+
* The method run when the `git-graph.openFile` command is invoked.
320+
* @param arg The Git Graph URI.
321+
*/
322+
private openFile(arg?: vscode.Uri) {
323+
const uri = arg || vscode.window.activeTextEditor?.document.uri;
324+
if (typeof uri === 'object' && uri.scheme === DiffDocProvider.scheme) {
325+
// A Git Graph URI has been provided
326+
const request = decodeDiffDocUri(uri);
327+
return openFile(request.repo, request.filePath, vscode.ViewColumn.Active).then((errorInfo) => {
328+
if (errorInfo !== null) {
329+
return showErrorMessage('Unable to Open File: ' + errorInfo);
330+
}
331+
});
332+
} else {
333+
return showErrorMessage('Unable to Open File: The command was not called with the required arguments.');
334+
}
335+
}
336+
295337

296338
/* Helper Methods */
297339

src/dataSource.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { AskpassEnvironment, AskpassManager } from './askpass/askpassManager';
77
import { getConfig } from './config';
88
import { Logger } from './logger';
99
import { CommitOrdering, DateType, DeepWriteable, ErrorInfo, GitCommit, GitCommitDetails, GitCommitStash, GitConfigLocation, GitFileChange, GitFileStatus, GitPushBranchMode, GitRepoConfig, GitRepoConfigBranches, GitResetMode, GitSignatureStatus, GitStash, MergeActionOn, RebaseActionOn, SquashMessageFormat, TagType, Writeable } from './types';
10-
import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, UNCOMMITTED, abbrevCommit, constructIncompatibleGitVersionMessage, getPathFromStr, getPathFromUri, isGitAtLeastVersion, openGitTerminal, pathWithTrailingSlash, realpath, resolveSpawnOutput, showErrorMessage } from './utils';
10+
import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, UNCOMMITTED, abbrevCommit, constructIncompatibleGitVersionMessage, doesVersionMeetRequirement, getPathFromStr, getPathFromUri, openGitTerminal, pathWithTrailingSlash, realpath, resolveSpawnOutput, showErrorMessage } from './utils';
1111
import { Disposable } from './utils/disposable';
1212
import { Event } from './utils/event';
1313

@@ -88,7 +88,7 @@ export class DataSource extends Disposable {
8888
*/
8989
public setGitExecutable(gitExecutable: GitExecutable | null) {
9090
this.gitExecutable = gitExecutable;
91-
this.gitExecutableSupportsGpgInfo = gitExecutable !== null ? isGitAtLeastVersion(gitExecutable, '2.4.0') : false;
91+
this.gitExecutableSupportsGpgInfo = gitExecutable !== null ? doesVersionMeetRequirement(gitExecutable.version, '2.4.0') : false;
9292
this.generateGitCommandFormats();
9393
}
9494

@@ -719,7 +719,7 @@ export class DataSource extends Disposable {
719719
if (pruneTags) {
720720
if (!prune) {
721721
return Promise.resolve('In order to Prune Tags, pruning must also be enabled when fetching from ' + (remote !== null ? 'a remote' : 'remote(s)') + '.');
722-
} else if (this.gitExecutable !== null && !isGitAtLeastVersion(this.gitExecutable, '2.17.0')) {
722+
} else if (this.gitExecutable !== null && !doesVersionMeetRequirement(this.gitExecutable.version, '2.17.0')) {
723723
return Promise.resolve(constructIncompatibleGitVersionMessage(this.gitExecutable, '2.17.0', 'pruning tags when fetching'));
724724
}
725725
args.push('--prune-tags');
@@ -1197,8 +1197,7 @@ export class DataSource extends Disposable {
11971197
public pushStash(repo: string, message: string, includeUntracked: boolean): Promise<ErrorInfo> {
11981198
if (this.gitExecutable === null) {
11991199
return Promise.resolve(UNABLE_TO_FIND_GIT_MSG);
1200-
}
1201-
if (!isGitAtLeastVersion(this.gitExecutable, '2.13.2')) {
1200+
} else if (!doesVersionMeetRequirement(this.gitExecutable.version, '2.13.2')) {
12021201
return Promise.resolve(constructIncompatibleGitVersionMessage(this.gitExecutable, '2.13.2'));
12031202
}
12041203

src/diffDocProvider.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,20 @@ export class DiffDocProvider extends Disposable implements vscode.TextDocumentCo
4747
* @returns The content of the text document.
4848
*/
4949
public provideTextDocumentContent(uri: vscode.Uri): string | Thenable<string> {
50-
let document = this.docs.get(uri.toString());
51-
if (document) return document.value;
50+
const document = this.docs.get(uri.toString());
51+
if (document) {
52+
return document.value;
53+
}
5254

53-
let request = decodeDiffDocUri(uri);
54-
if (request === null) return ''; // Return empty file (used for one side of added / deleted file diff)
55+
const request = decodeDiffDocUri(uri);
56+
if (!request.exists) {
57+
// Return empty file (used for one side of added / deleted file diff)
58+
return '';
59+
}
5560

5661
return this.dataSource.getCommitFile(request.repo, request.commit, request.filePath).then(
5762
(contents) => {
58-
let document = new DiffDocument(contents);
63+
const document = new DiffDocument(contents);
5964
this.docs.set(uri.toString(), document);
6065
return document.value;
6166
},
@@ -99,7 +104,8 @@ type DiffDocUriData = {
99104
filePath: string;
100105
commit: string;
101106
repo: string;
102-
} | null;
107+
exists: boolean;
108+
};
103109

104110
/**
105111
* Produce the URI of a file to be used in the Visual Studio Diff View.
@@ -115,17 +121,19 @@ export function encodeDiffDocUri(repo: string, filePath: string, commit: string,
115121
return vscode.Uri.file(path.join(repo, filePath));
116122
}
117123

118-
let data: DiffDocUriData, extension: string;
119-
if ((diffSide === DiffSide.Old && type === GitFileStatus.Added) || (diffSide === DiffSide.New && type === GitFileStatus.Deleted)) {
120-
data = null;
124+
const fileDoesNotExist = (diffSide === DiffSide.Old && type === GitFileStatus.Added) || (diffSide === DiffSide.New && type === GitFileStatus.Deleted);
125+
const data: DiffDocUriData = {
126+
filePath: getPathFromStr(filePath),
127+
commit: commit,
128+
repo: repo,
129+
exists: !fileDoesNotExist
130+
};
131+
132+
let extension: string;
133+
if (fileDoesNotExist) {
121134
extension = '';
122135
} else {
123-
data = {
124-
filePath: getPathFromStr(filePath),
125-
commit: commit,
126-
repo: repo
127-
};
128-
let extIndex = data.filePath.indexOf('.', data.filePath.lastIndexOf('/') + 1);
136+
const extIndex = data.filePath.indexOf('.', data.filePath.lastIndexOf('/') + 1);
129137
extension = extIndex > -1 ? data.filePath.substring(extIndex) : '';
130138
}
131139

src/utils.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -334,16 +334,17 @@ export function openExternalUrl(url: string, type: string = 'External URL'): The
334334
* Open a file within a repository in Visual Studio Code.
335335
* @param repo The repository the file is contained in.
336336
* @param filePath The relative path of the file within the repository.
337+
* @param viewColumn An optional ViewColumn that the file should be opened in.
337338
* @returns A promise resolving to the ErrorInfo of the executed command.
338339
*/
339-
export function openFile(repo: string, filePath: string) {
340+
export function openFile(repo: string, filePath: string, viewColumn: vscode.ViewColumn | null = null) {
340341
return new Promise<ErrorInfo>(resolve => {
341342
const p = path.join(repo, filePath);
342343
fs.access(p, fs.constants.R_OK, (err) => {
343344
if (err === null) {
344345
vscode.commands.executeCommand('vscode.open', vscode.Uri.file(p), {
345346
preview: true,
346-
viewColumn: getConfig().openNewTabEditorGroup
347+
viewColumn: viewColumn === null ? getConfig().openNewTabEditorGroup : viewColumn
347348
}).then(
348349
() => resolve(null),
349350
() => resolve('Visual Studio Code was unable to open ' + filePath + '.')
@@ -710,17 +711,17 @@ export async function getGitExecutableFromPaths(paths: string[]): Promise<GitExe
710711
}
711712

712713

713-
/* Git Version Handling */
714+
/* Version Handling */
714715

715716
/**
716-
* Checks whether a Git executable is at least the specified version.
717-
* @param executable The Git executable to check.
718-
* @param version The minimum required version.
719-
* @returns TRUE => `executable` is at least `version`, FALSE => `executable` is older than `version`.
717+
* Checks whether a version is at least a required version.
718+
* @param version The version to check.
719+
* @param requiredVersion The minimum required version.
720+
* @returns TRUE => `version` is at least `requiredVersion`, FALSE => `version` is older than `requiredVersion`.
720721
*/
721-
export function isGitAtLeastVersion(executable: GitExecutable, version: string) {
722-
const v1 = parseVersion(executable.version);
723-
const v2 = parseVersion(version);
722+
export function doesVersionMeetRequirement(version: string, requiredVersion: string) {
723+
const v1 = parseVersion(version);
724+
const v2 = parseVersion(requiredVersion);
724725

725726
if (v1 === null || v2 === null) {
726727
// Unable to parse a version number

0 commit comments

Comments
 (0)