Skip to content

Commit 985d163

Browse files
BoscoCHWfcollonval
andauthored
Add 'open file' command to context menu on diff tab (#1135)
* attempting to add a command to context menu but not successful * implement opening on file from diff tab context menu * Add className to diff widget title and implement more specific context menu selector * Add caption to command `openFileFromDiff` Co-authored-by: Frédéric Collonval <[email protected]> * change contextMenuHitTest to test for nodeId 'git-diff...' * Don't rely on dom node title and open file even if it is not currently modified Co-authored-by: Frédéric Collonval <[email protected]> Co-authored-by: Frédéric Collonval <[email protected]>
1 parent 0a50d7e commit 985d163

File tree

5 files changed

+85
-10
lines changed

5 files changed

+85
-10
lines changed

schema/plugin.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@
172172
"label": "Git",
173173
"icon": "git"
174174
}
175+
},
176+
{
177+
"type": "command",
178+
"command": "git:open-file-from-diff",
179+
"selector": ".jp-git-diff-title",
180+
"rank": 1
175181
}
176182
]
177183
}

src/commandsAndMenu.tsx

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ export function addCommands(
496496
getDiffProvider(fullPath) ?? (isText && createPlainTextDiff);
497497

498498
if (buildDiffWidget) {
499-
const id = `diff-${fullPath}-${model.reference.label}-${model.challenger.label}`;
499+
const id = `git-diff-${fullPath}-${model.reference.label}-${model.challenger.label}`;
500500
const mainAreaItems = shell.widgets('main');
501501
let mainAreaItem = mainAreaItems.next();
502502
while (mainAreaItem) {
@@ -519,6 +519,7 @@ export function addCommands(
519519
diffWidget.title.caption = fullPath;
520520
diffWidget.title.icon = diffIcon;
521521
diffWidget.title.closable = true;
522+
diffWidget.title.className = 'jp-git-diff-title';
522523
diffWidget.addClass('jp-git-diff-parent-widget');
523524

524525
shell.add(diffWidget, 'main');
@@ -750,6 +751,55 @@ export function addCommands(
750751
icon: openIcon.bindprops({ stylesheet: 'menuItem' })
751752
});
752753

754+
commands.addCommand(ContextCommandIDs.openFileFromDiff, {
755+
label: trans.__('Open File'),
756+
caption: trans.__('Open file from its diff view'),
757+
execute: async _ => {
758+
const domNode = app.contextMenuHitTest((node: HTMLElement) => {
759+
const nodeId = node.dataset.id;
760+
return nodeId && nodeId.substring(0, 8) === 'git-diff';
761+
});
762+
if (!domNode) {
763+
return;
764+
}
765+
766+
const matches = toArray(shell.widgets('main')).filter(
767+
widget => widget.id === domNode.dataset.id
768+
);
769+
770+
if (matches.length === 0) {
771+
return;
772+
}
773+
774+
const diffModel = (
775+
((matches[0] as MainAreaWidget).content as Panel)
776+
.widgets[0] as Git.Diff.IDiffWidget
777+
).model;
778+
779+
const filename = diffModel.filename;
780+
781+
if (
782+
diffModel.reference.source === Git.Diff.SpecialRef.INDEX ||
783+
diffModel.reference.source === Git.Diff.SpecialRef.WORKING ||
784+
diffModel.challenger.source === Git.Diff.SpecialRef.INDEX ||
785+
diffModel.challenger.source === Git.Diff.SpecialRef.WORKING
786+
) {
787+
const file = gitModel.status.files.find(
788+
fileStatus => fileStatus.from === filename
789+
);
790+
if (file) {
791+
commands.execute(ContextCommandIDs.gitFileOpen, {
792+
files: [file]
793+
} as any);
794+
}
795+
} else {
796+
commands.execute('docmanager:open', {
797+
path: gitModel.getRelativeFilePath(filename)
798+
});
799+
}
800+
}
801+
});
802+
753803
commands.addCommand(ContextCommandIDs.gitFileDiff, {
754804
label: trans.__('Diff'),
755805
caption: pluralizedContextLabel(

src/components/diff/NotebookDiff.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,13 @@ export class NotebookDiff extends Panel implements Git.Diff.IDiffWidget {
175175
return this._model.hasConflict;
176176
}
177177

178+
/**
179+
* Diff model
180+
*/
181+
get model(): Git.Diff.IModel {
182+
return this._model;
183+
}
184+
178185
/**
179186
* Nbdime notebook widget.
180187
*/

src/components/diff/PlainTextDiff.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ export class PlainTextDiff extends Widget implements Git.Diff.IDiffWidget {
8080
return true;
8181
}
8282

83+
/**
84+
* Diff model
85+
*/
86+
get model(): Git.Diff.IModel {
87+
return this._model;
88+
}
89+
8390
/**
8491
* Promise which fulfills when the widget is ready.
8592
*/

src/tokens.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -568,20 +568,24 @@ export namespace Git {
568568
*/
569569
export interface IDiffWidget extends Widget {
570570
/**
571-
* Refresh the diff widget
572-
*
573-
* Note: Update the content and recompute the diff
574-
*/
575-
refresh(): Promise<void>;
576-
/**
577-
* Checks if the conflicted file has been resolved.
571+
* Diff model
578572
*/
579-
isFileResolved: boolean;
573+
readonly model: Git.Diff.IModel;
580574
/**
581575
* Gets the file model of a resolved merge conflict,
582576
* and rejects if unable to retrieve
583577
*/
584578
getResolvedFile(): Promise<Partial<Contents.IModel>>;
579+
/**
580+
* Checks if the conflicted file has been resolved.
581+
*/
582+
readonly isFileResolved: boolean;
583+
/**
584+
* Refresh the diff widget
585+
*
586+
* Note: Update the content and recompute the diff
587+
*/
588+
refresh(): Promise<void>;
585589
}
586590

587591
/**
@@ -1149,7 +1153,8 @@ export enum ContextCommandIDs {
11491153
gitFileHistory = 'git:context-history',
11501154
gitIgnore = 'git:context-ignore',
11511155
gitIgnoreExtension = 'git:context-ignoreExtension',
1152-
gitNoAction = 'git:no-action'
1156+
gitNoAction = 'git:no-action',
1157+
openFileFromDiff = 'git:open-file-from-diff'
11531158
}
11541159

11551160
/**

0 commit comments

Comments
 (0)