Skip to content

Commit ad1ba5c

Browse files
committed
#466 New context menu action to copy the relative path of a file in the Commit Details View to the clipboard.
1 parent 67b1f39 commit ad1ba5c

5 files changed

Lines changed: 33 additions & 13 deletions

File tree

src/gitGraphView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ export class GitGraphView extends Disposable {
259259
case 'copyFilePath':
260260
this.sendMessage({
261261
command: 'copyFilePath',
262-
error: await copyFilePathToClipboard(msg.repo, msg.filePath)
262+
error: await copyFilePathToClipboard(msg.repo, msg.filePath, msg.absolute)
263263
});
264264
break;
265265
case 'copyToClipboard':

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ export interface ResponseCompareCommits extends ResponseWithErrorInfo {
681681
export interface RequestCopyFilePath extends RepoRequest {
682682
readonly command: 'copyFilePath';
683683
readonly filePath: string;
684+
readonly absolute: boolean;
684685
}
685686
export interface ResponseCopyFilePath extends ResponseWithErrorInfo {
686687
readonly command: 'copyFilePath';

src/utils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,11 @@ export function archive(repo: string, ref: string, dataSource: DataSource): Then
245245
* Copy the path of a file in a repository to the clipboard.
246246
* @param repo The repository the file is contained in.
247247
* @param filePath The relative path of the file within the repository.
248+
* @param absolute TRUE => Absolute path, FALSE => Relative path.
248249
* @returns A promise resolving to the ErrorInfo of the executed command.
249250
*/
250-
export function copyFilePathToClipboard(repo: string, filePath: string) {
251-
return copyToClipboard(path.join(repo, filePath));
251+
export function copyFilePathToClipboard(repo: string, filePath: string, absolute: boolean) {
252+
return copyToClipboard(absolute ? path.join(repo, filePath) : filePath);
252253
}
253254

254255
/**

tests/utils.test.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -642,25 +642,38 @@ describe('archive', () => {
642642
});
643643

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

649649
// Run
650-
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt');
650+
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt', true);
651651

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

658+
it('Copies the relative file path to the clipboard', async () => {
659+
// Setup
660+
vscode.env.clipboard.writeText.mockResolvedValueOnce(null);
661+
662+
// Run
663+
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt', false);
664+
665+
// Assert
666+
const receivedArgs: any[] = vscode.env.clipboard.writeText.mock.calls[0];
667+
expect(result).toBe(null);
668+
expect(getPathFromStr(receivedArgs[0])).toBe('c/d.txt');
669+
});
670+
658671
it('Returns an error message when writeText fails', async () => {
659672
// Setup
660673
vscode.env.clipboard.writeText.mockRejectedValueOnce(null);
661674

662675
// Run
663-
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt');
676+
const result = await copyFilePathToClipboard('/a/b', 'c/d.txt', true);
664677

665678
// Assert
666679
expect(result).toBe('Visual Studio Code was unable to write to the Clipboard.');

web/main.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,8 +2823,8 @@ class GitGraphView {
28232823
});
28242824
};
28252825

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

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

28862886
const fileElem = getFileElemOfEventTarget(e.target);
2887-
triggerCopyFilePath(getFileOfFileElem(expandedCommit.fileChanges, fileElem));
2887+
triggerCopyFilePath(getFileOfFileElem(expandedCommit.fileChanges, fileElem), true);
28882888
});
28892889

28902890
addListenerToClass('viewGitFileAtRevision', 'click', (e) => {
@@ -2943,9 +2943,14 @@ class GitGraphView {
29432943
],
29442944
[
29452945
{
2946-
title: 'Copy File Path to the Clipboard',
2946+
title: 'Copy Absolute File Path to Clipboard',
29472947
visible: true,
2948-
onClick: () => triggerCopyFilePath(file)
2948+
onClick: () => triggerCopyFilePath(file, true)
2949+
},
2950+
{
2951+
title: 'Copy Relative File Path to Clipboard',
2952+
visible: true,
2953+
onClick: () => triggerCopyFilePath(file, false)
29492954
}
29502955
]
29512956
], false, target, <MouseEvent>e, this.isCdvDocked() ? document.body : this.viewElem, () => {
@@ -3084,7 +3089,7 @@ window.addEventListener('load', () => {
30843089
}
30853090
break;
30863091
case 'copyFilePath':
3087-
finishOrDisplayError(msg.error, 'Unable to Copy File Path to the Clipboard');
3092+
finishOrDisplayError(msg.error, 'Unable to Copy File Path to Clipboard');
30883093
break;
30893094
case 'copyToClipboard':
30903095
finishOrDisplayError(msg.error, 'Unable to Copy ' + msg.type + ' to Clipboard');
@@ -3377,7 +3382,7 @@ function generateFileTreeLeafHtml(name: string, leaf: FileTreeLeaf, gitFiles: Re
33773382
(initialState.config.enhancedAccessibility ? '<span class="fileTreeFileType" title="' + changeTypeMessage + '">' + fileTreeFile.type + '</span>' : '') +
33783383
(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>' : '') +
33793384
(fileTreeFile.newFilePath === lastViewedFile ? '<span id="cdvLastFileViewed" title="Last File Viewed">' + SVG_ICONS.eyeOpen + '</span>' : '') +
3380-
'<span class="copyGitFile fileTreeFileAction" title="Copy File Path to the Clipboard">' + SVG_ICONS.copy + '</span>' +
3385+
'<span class="copyGitFile fileTreeFileAction" title="Copy Absolute File Path to Clipboard">' + SVG_ICONS.copy + '</span>' +
33813386
(fileTreeFile.type !== GG.GitFileStatus.Deleted
33823387
? (diffPossible && !isUncommitted ? '<span class="viewGitFileAtRevision fileTreeFileAction" title="View File at this Revision">' + SVG_ICONS.commit + '</span>' : '') +
33833388
'<span class="openGitFile fileTreeFileAction" title="Open File">' + SVG_ICONS.openFile + '</span>'

0 commit comments

Comments
 (0)