Skip to content

Commit

Permalink
#466 New context menu action to copy the relative path of a file in t…
Browse files Browse the repository at this point in the history
…he Commit Details View to the clipboard.
  • Loading branch information
mhutchie committed Feb 28, 2021
1 parent 67b1f39 commit ad1ba5c
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/gitGraphView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export class GitGraphView extends Disposable {
case 'copyFilePath':
this.sendMessage({
command: 'copyFilePath',
error: await copyFilePathToClipboard(msg.repo, msg.filePath)
error: await copyFilePathToClipboard(msg.repo, msg.filePath, msg.absolute)
});
break;
case 'copyToClipboard':
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ export interface ResponseCompareCommits extends ResponseWithErrorInfo {
export interface RequestCopyFilePath extends RepoRequest {
readonly command: 'copyFilePath';
readonly filePath: string;
readonly absolute: boolean;
}
export interface ResponseCopyFilePath extends ResponseWithErrorInfo {
readonly command: 'copyFilePath';
Expand Down
5 changes: 3 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,11 @@ export function archive(repo: string, ref: string, dataSource: DataSource): Then
* Copy the path of a file in a repository to the clipboard.
* @param repo The repository the file is contained in.
* @param filePath The relative path of the file within the repository.
* @param absolute TRUE => Absolute path, FALSE => Relative path.
* @returns A promise resolving to the ErrorInfo of the executed command.
*/
export function copyFilePathToClipboard(repo: string, filePath: string) {
return copyToClipboard(path.join(repo, filePath));
export function copyFilePathToClipboard(repo: string, filePath: string, absolute: boolean) {
return copyToClipboard(absolute ? path.join(repo, filePath) : filePath);
}

/**
Expand Down
19 changes: 16 additions & 3 deletions tests/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,25 +642,38 @@ describe('archive', () => {
});

describe('copyFilePathToClipboard', () => {
it('Appends the file path to the repository path, and copies the result to the clipboard', async () => {
it('Appends the relative file path to the repository path, and copies the result to the clipboard', async () => {
// Setup
vscode.env.clipboard.writeText.mockResolvedValueOnce(null);

// Run
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt');
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt', true);

// Assert
const receivedArgs: any[] = vscode.env.clipboard.writeText.mock.calls[0];
expect(result).toBe(null);
expect(getPathFromStr(receivedArgs[0])).toBe('/a/b/c/d.txt');
});

it('Copies the relative file path to the clipboard', async () => {
// Setup
vscode.env.clipboard.writeText.mockResolvedValueOnce(null);

// Run
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt', false);

// Assert
const receivedArgs: any[] = vscode.env.clipboard.writeText.mock.calls[0];
expect(result).toBe(null);
expect(getPathFromStr(receivedArgs[0])).toBe('c/d.txt');
});

it('Returns an error message when writeText fails', async () => {
// Setup
vscode.env.clipboard.writeText.mockRejectedValueOnce(null);

// Run
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt');
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt', true);

// Assert
expect(result).toBe('Visual Studio Code was unable to write to the Clipboard.');
Expand Down
19 changes: 12 additions & 7 deletions web/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2823,8 +2823,8 @@ class GitGraphView {
});
};

const triggerCopyFilePath = (file: GG.GitFileChange) => {
sendMessage({ command: 'copyFilePath', repo: this.currentRepo, filePath: file.newFilePath });
const triggerCopyFilePath = (file: GG.GitFileChange, absolute: boolean) => {
sendMessage({ command: 'copyFilePath', repo: this.currentRepo, filePath: file.newFilePath, absolute: absolute });
};

const triggerViewFileAtRevision = (file: GG.GitFileChange, fileElem: HTMLElement) => {
Expand Down Expand Up @@ -2884,7 +2884,7 @@ class GitGraphView {
if (expandedCommit === null || expandedCommit.fileChanges === null || e.target === null) return;

const fileElem = getFileElemOfEventTarget(e.target);
triggerCopyFilePath(getFileOfFileElem(expandedCommit.fileChanges, fileElem));
triggerCopyFilePath(getFileOfFileElem(expandedCommit.fileChanges, fileElem), true);
});

addListenerToClass('viewGitFileAtRevision', 'click', (e) => {
Expand Down Expand Up @@ -2943,9 +2943,14 @@ class GitGraphView {
],
[
{
title: 'Copy File Path to the Clipboard',
title: 'Copy Absolute File Path to Clipboard',
visible: true,
onClick: () => triggerCopyFilePath(file)
onClick: () => triggerCopyFilePath(file, true)
},
{
title: 'Copy Relative File Path to Clipboard',
visible: true,
onClick: () => triggerCopyFilePath(file, false)
}
]
], false, target, <MouseEvent>e, this.isCdvDocked() ? document.body : this.viewElem, () => {
Expand Down Expand Up @@ -3084,7 +3089,7 @@ window.addEventListener('load', () => {
}
break;
case 'copyFilePath':
finishOrDisplayError(msg.error, 'Unable to Copy File Path to the Clipboard');
finishOrDisplayError(msg.error, 'Unable to Copy File Path to Clipboard');
break;
case 'copyToClipboard':
finishOrDisplayError(msg.error, 'Unable to Copy ' + msg.type + ' to Clipboard');
Expand Down Expand Up @@ -3377,7 +3382,7 @@ function generateFileTreeLeafHtml(name: string, leaf: FileTreeLeaf, gitFiles: Re
(initialState.config.enhancedAccessibility ? '<span class="fileTreeFileType" title="' + changeTypeMessage + '">' + fileTreeFile.type + '</span>' : '') +
(fileTreeFile.type !== GG.GitFileStatus.Added && fileTreeFile.type !== GG.GitFileStatus.Untracked && fileTreeFile.type !== GG.GitFileStatus.Deleted && textFile ? '<span class="fileTreeFileAddDel">(<span class="fileTreeFileAdd" title="' + fileTreeFile.additions + ' addition' + (fileTreeFile.additions !== 1 ? 's' : '') + '">+' + fileTreeFile.additions + '</span>|<span class="fileTreeFileDel" title="' + fileTreeFile.deletions + ' deletion' + (fileTreeFile.deletions !== 1 ? 's' : '') + '">-' + fileTreeFile.deletions + '</span>)</span>' : '') +
(fileTreeFile.newFilePath === lastViewedFile ? '<span id="cdvLastFileViewed" title="Last File Viewed">' + SVG_ICONS.eyeOpen + '</span>' : '') +
'<span class="copyGitFile fileTreeFileAction" title="Copy File Path to the Clipboard">' + SVG_ICONS.copy + '</span>' +
'<span class="copyGitFile fileTreeFileAction" title="Copy Absolute File Path to Clipboard">' + SVG_ICONS.copy + '</span>' +
(fileTreeFile.type !== GG.GitFileStatus.Deleted
? (diffPossible && !isUncommitted ? '<span class="viewGitFileAtRevision fileTreeFileAction" title="View File at this Revision">' + SVG_ICONS.commit + '</span>' : '') +
'<span class="openGitFile fileTreeFileAction" title="Open File">' + SVG_ICONS.openFile + '</span>'
Expand Down

0 comments on commit ad1ba5c

Please sign in to comment.