Skip to content

Commit 0bad949

Browse files
authored
Merge pull request #890 from meeseeksmachine/auto-backport-of-pr-877-on-jlab-2
Backport PR #877 on branch jlab-2 (Implement Git context menu in the file browser)
2 parents 0f4baeb + abcebcc commit 0bad949

File tree

13 files changed

+676
-325
lines changed

13 files changed

+676
-325
lines changed

src/commandsAndMenu.tsx

Lines changed: 465 additions & 190 deletions
Large diffs are not rendered by default.

src/components/CommitBox.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
commitButtonClass
88
} from '../style/CommitBox';
99
import { CommandRegistry } from '@lumino/commands';
10-
import { SUBMIT_COMMIT_COMMAND } from '../commandsAndMenu';
10+
import { CommandIDs } from '../tokens';
1111

1212
/**
1313
* Interface describing component properties.
@@ -136,7 +136,7 @@ export class CommitBox extends React.Component<
136136
*/
137137
private _getSubmitKeystroke = (): string => {
138138
const binding = this.props.commands.keyBindings.find(
139-
binding => binding.command === SUBMIT_COMMIT_COMMAND
139+
binding => binding.command === CommandIDs.gitSubmitCommand
140140
);
141141
return binding.keys.join(' ');
142142
};
@@ -201,7 +201,7 @@ export class CommitBox extends React.Component<
201201
_: CommandRegistry,
202202
commandArgs: CommandRegistry.ICommandExecutedArgs
203203
): void => {
204-
if (commandArgs.id === SUBMIT_COMMIT_COMMAND && this._canCommit()) {
204+
if (commandArgs.id === CommandIDs.gitSubmitCommand && this._canCommit()) {
205205
this._onCommitSubmit();
206206
}
207207
};

src/components/FileList.tsx

Lines changed: 85 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Menu } from '@lumino/widgets';
55
import * as React from 'react';
66
import AutoSizer from 'react-virtualized-auto-sizer';
77
import { ListChildComponentProps } from 'react-window';
8-
import { CommandIDs } from '../commandsAndMenu';
8+
import { addMenuItems, CommandArguments } from '../commandsAndMenu';
99
import { GitExtension } from '../model';
1010
import { hiddenButtonStyle } from '../style/ActionButtonStyle';
1111
import { fileListWrapperClass } from '../style/FileListStyle';
@@ -16,7 +16,7 @@ import {
1616
openIcon,
1717
removeIcon
1818
} from '../style/icons';
19-
import { Git } from '../tokens';
19+
import { ContextCommandIDs, Git } from '../tokens';
2020
import { ActionButton } from './ActionButton';
2121
import { isDiffSupported } from './diff/Diff';
2222
import { FileItem } from './FileItem';
@@ -45,6 +45,58 @@ export interface IFileListProps {
4545
settings: ISettingRegistry.ISettings;
4646
}
4747

48+
export type ContextCommands = Record<Git.Status, ContextCommandIDs[]>;
49+
50+
export const CONTEXT_COMMANDS: ContextCommands = {
51+
'partially-staged': [
52+
ContextCommandIDs.gitFileOpen,
53+
ContextCommandIDs.gitFileUnstage,
54+
ContextCommandIDs.gitFileDiff
55+
],
56+
unstaged: [
57+
ContextCommandIDs.gitFileOpen,
58+
ContextCommandIDs.gitFileStage,
59+
ContextCommandIDs.gitFileDiscard,
60+
ContextCommandIDs.gitFileDiff
61+
],
62+
untracked: [
63+
ContextCommandIDs.gitFileOpen,
64+
ContextCommandIDs.gitFileTrack,
65+
ContextCommandIDs.gitIgnore,
66+
ContextCommandIDs.gitIgnoreExtension,
67+
ContextCommandIDs.gitFileDelete
68+
],
69+
staged: [
70+
ContextCommandIDs.gitFileOpen,
71+
ContextCommandIDs.gitFileUnstage,
72+
ContextCommandIDs.gitFileDiff
73+
]
74+
};
75+
76+
const SIMPLE_CONTEXT_COMMANDS: ContextCommands = {
77+
'partially-staged': [
78+
ContextCommandIDs.gitFileOpen,
79+
ContextCommandIDs.gitFileDiscard,
80+
ContextCommandIDs.gitFileDiff
81+
],
82+
staged: [
83+
ContextCommandIDs.gitFileOpen,
84+
ContextCommandIDs.gitFileDiscard,
85+
ContextCommandIDs.gitFileDiff
86+
],
87+
unstaged: [
88+
ContextCommandIDs.gitFileOpen,
89+
ContextCommandIDs.gitFileDiscard,
90+
ContextCommandIDs.gitFileDiff
91+
],
92+
untracked: [
93+
ContextCommandIDs.gitFileOpen,
94+
ContextCommandIDs.gitIgnore,
95+
ContextCommandIDs.gitIgnoreExtension,
96+
ContextCommandIDs.gitFileDelete
97+
]
98+
};
99+
48100
export class FileList extends React.Component<IFileListProps, IFileListState> {
49101
constructor(props: IFileListProps) {
50102
super(props);
@@ -71,42 +123,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
71123
});
72124

73125
const contextMenu = new Menu({ commands: this.props.commands });
74-
const commands = [CommandIDs.gitFileOpen];
75-
switch (selectedFile.status) {
76-
case 'unstaged':
77-
commands.push(
78-
CommandIDs.gitFileStage,
79-
CommandIDs.gitFileDiscard,
80-
CommandIDs.gitFileDiff
81-
);
82-
break;
83-
case 'untracked':
84-
commands.push(
85-
CommandIDs.gitFileTrack,
86-
CommandIDs.gitIgnore,
87-
CommandIDs.gitIgnoreExtension,
88-
CommandIDs.gitFileDelete
89-
);
90-
break;
91-
case 'staged':
92-
commands.push(CommandIDs.gitFileUnstage, CommandIDs.gitFileDiff);
93-
break;
94-
}
126+
const commands = CONTEXT_COMMANDS[selectedFile.status];
127+
addMenuItems(commands, contextMenu, [selectedFile]);
95128

96-
commands.forEach(command => {
97-
if (command === CommandIDs.gitFileDiff) {
98-
contextMenu.addItem({
99-
command,
100-
args: {
101-
filePath: selectedFile.to,
102-
isText: !selectedFile.is_binary,
103-
status: selectedFile.status
104-
}
105-
});
106-
} else {
107-
contextMenu.addItem({ command, args: selectedFile as any });
108-
}
109-
});
110129
contextMenu.open(event.clientX, event.clientY);
111130
};
112131

@@ -123,34 +142,8 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
123142
event.preventDefault();
124143

125144
const contextMenu = new Menu({ commands: this.props.commands });
126-
const commands = [CommandIDs.gitFileOpen];
127-
switch (selectedFile.status) {
128-
case 'untracked':
129-
commands.push(
130-
CommandIDs.gitIgnore,
131-
CommandIDs.gitIgnoreExtension,
132-
CommandIDs.gitFileDelete
133-
);
134-
break;
135-
default:
136-
commands.push(CommandIDs.gitFileDiscard, CommandIDs.gitFileDiff);
137-
break;
138-
}
139-
140-
commands.forEach(command => {
141-
if (command === CommandIDs.gitFileDiff) {
142-
contextMenu.addItem({
143-
command,
144-
args: {
145-
filePath: selectedFile.to,
146-
isText: !selectedFile.is_binary,
147-
status: selectedFile.status
148-
}
149-
});
150-
} else {
151-
contextMenu.addItem({ command, args: selectedFile as any });
152-
}
153-
});
145+
const commands = SIMPLE_CONTEXT_COMMANDS[selectedFile.status];
146+
addMenuItems(commands, contextMenu, [selectedFile]);
154147
contextMenu.open(event.clientX, event.clientY);
155148
};
156149

@@ -216,7 +209,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
216209

217210
/** Discard changes in a specific unstaged or staged file */
218211
discardChanges = async (file: Git.IStatusFile) => {
219-
await this.props.commands.execute(CommandIDs.gitFileDiscard, file as any);
212+
await this.props.commands.execute(ContextCommandIDs.gitFileDiscard, ({
213+
files: [file]
214+
} as CommandArguments.IGitContextAction) as any);
220215
};
221216

222217
/** Add all untracked files */
@@ -334,7 +329,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
334329
const { data, index, style } = rowProps;
335330
const file = data[index] as Git.IStatusFile;
336331
const openFile = () => {
337-
this.props.commands.execute(CommandIDs.gitFileOpen, file as any);
332+
this.props.commands.execute(ContextCommandIDs.gitFileOpen, ({
333+
files: [file]
334+
} as CommandArguments.IGitContextAction) as any);
338335
};
339336
const diffButton = this._createDiffButton(file);
340337
return (
@@ -418,7 +415,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
418415
const { data, index, style } = rowProps;
419416
const file = data[index] as Git.IStatusFile;
420417
const openFile = () => {
421-
this.props.commands.execute(CommandIDs.gitFileOpen, file as any);
418+
this.props.commands.execute(ContextCommandIDs.gitFileOpen, ({
419+
files: [file]
420+
} as CommandArguments.IGitContextAction) as any);
422421
};
423422
const diffButton = this._createDiffButton(file);
424423
return (
@@ -528,10 +527,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
528527
icon={openIcon}
529528
title={'Open this file'}
530529
onClick={() => {
531-
this.props.commands.execute(
532-
CommandIDs.gitFileOpen,
533-
file as any
534-
);
530+
this.props.commands.execute(ContextCommandIDs.gitFileOpen, ({
531+
files: [file]
532+
} as CommandArguments.IGitContextAction) as any);
535533
}}
536534
/>
537535
<ActionButton
@@ -549,7 +547,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
549547
model={this.props.model}
550548
onDoubleClick={() => {
551549
if (!doubleClickDiff) {
552-
this.props.commands.execute(CommandIDs.gitFileOpen, file as any);
550+
this.props.commands.execute(ContextCommandIDs.gitFileOpen, ({
551+
files: [file]
552+
} as CommandArguments.IGitContextAction) as any);
553553
}
554554
}}
555555
selected={this._isSelectedFile(file)}
@@ -601,7 +601,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
601601
.composite as boolean;
602602

603603
const openFile = () => {
604-
this.props.commands.execute(CommandIDs.gitFileOpen, file as any);
604+
this.props.commands.execute(ContextCommandIDs.gitFileOpen, ({
605+
files: [file]
606+
} as CommandArguments.IGitContextAction) as any);
605607
};
606608

607609
// Default value for actions and double click
@@ -737,11 +739,15 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
737739
*/
738740
private async _openDiffView(file: Git.IStatusFile): Promise<void> {
739741
try {
740-
await this.props.commands.execute(CommandIDs.gitFileDiff, {
741-
filePath: file.to,
742-
isText: !file.is_binary,
743-
status: file.status
744-
});
742+
await this.props.commands.execute(ContextCommandIDs.gitFileDiff, ({
743+
files: [
744+
{
745+
filePath: file.to,
746+
isText: !file.is_binary,
747+
status: file.status
748+
}
749+
]
750+
} as CommandArguments.IGitFileDiff) as any);
745751
} catch (reason) {
746752
console.error(`Failed to open diff view for ${file.to}.\n${reason}`);
747753
}

src/components/GitPanel.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { Signal } from '@lumino/signaling';
88
import Tab from '@material-ui/core/Tab';
99
import Tabs from '@material-ui/core/Tabs';
1010
import * as React from 'react';
11-
import { CommandIDs } from '../commandsAndMenu';
1211
import { Logger } from '../logger';
1312
import { GitExtension } from '../model';
1413
import {
@@ -20,7 +19,7 @@ import {
2019
tabsClass,
2120
warningTextClass
2221
} from '../style/GitPanel';
23-
import { Git, ILogMessage, Level } from '../tokens';
22+
import { CommandIDs, Git, ILogMessage, Level } from '../tokens';
2423
import { GitAuthorForm } from '../widgets/AuthorBox';
2524
import { CommitBox } from './CommitBox';
2625
import { FileList } from './FileList';

src/components/SinglePastCommitInfo.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CommandRegistry } from '@lumino/commands';
33
import * as React from 'react';
44
import { FixedSizeList, ListChildComponentProps } from 'react-window';
55
import { classes } from 'typestyle';
6-
import { CommandIDs } from '../commandsAndMenu';
6+
import { CommandArguments } from '../commandsAndMenu';
77
import { LoggerContext } from '../logger';
88
import { GitExtension } from '../model';
99
import {
@@ -25,7 +25,7 @@ import {
2525
iconClass,
2626
insertionsIconClass
2727
} from '../style/SinglePastCommitInfo';
28-
import { Git } from '../tokens';
28+
import { ContextCommandIDs, Git } from '../tokens';
2929
import { ActionButton } from './ActionButton';
3030
import { isDiffSupported } from './diff/Diff';
3131
import { FilePath } from './FilePath';
@@ -337,18 +337,22 @@ export class SinglePastCommitInfo extends React.Component<
337337
event.stopPropagation();
338338

339339
try {
340-
self.props.commands.execute(CommandIDs.gitFileDiff, {
341-
filePath: fpath,
342-
isText: bool,
343-
context: {
344-
previousRef: {
345-
gitRef: self.props.commit.pre_commit
346-
},
347-
currentRef: {
348-
gitRef: self.props.commit.commit
340+
self.props.commands.execute(ContextCommandIDs.gitFileDiff, ({
341+
files: [
342+
{
343+
filePath: fpath,
344+
isText: bool,
345+
context: {
346+
previousRef: {
347+
gitRef: self.props.commit.pre_commit
348+
},
349+
currentRef: {
350+
gitRef: self.props.commit.commit
351+
}
352+
}
349353
}
350-
}
351-
});
354+
]
355+
} as CommandArguments.IGitFileDiff) as any);
352356
} catch (err) {
353357
console.error(`Failed to open diff view for ${fpath}.\n${err}`);
354358
}

src/components/Toolbar.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { CommandRegistry } from '@lumino/commands';
88
import { Badge, Tab, Tabs } from '@material-ui/core';
99
import * as React from 'react';
1010
import { classes } from 'typestyle';
11-
import { CommandIDs } from '../commandsAndMenu';
1211
import { Logger } from '../logger';
1312
import {
1413
selectedTabClass,
@@ -31,7 +30,7 @@ import {
3130
toolbarMenuWrapperClass,
3231
toolbarNavClass
3332
} from '../style/Toolbar';
34-
import { Git, IGitExtension, Level } from '../tokens';
33+
import { CommandIDs, Git, IGitExtension, Level } from '../tokens';
3534
import { ActionButton } from './ActionButton';
3635
import { BranchMenu } from './BranchMenu';
3736
import { TagMenu } from './TagMenu';

src/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import { IMainMenu } from '@jupyterlab/mainmenu';
1111
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
1212
import { ISettingRegistry } from '@jupyterlab/settingregistry';
1313
import { IStatusBar } from '@jupyterlab/statusbar';
14-
import { addCommands, createGitMenu } from './commandsAndMenu';
14+
import {
15+
addCommands,
16+
addFileBrowserContextMenu,
17+
createGitMenu
18+
} from './commandsAndMenu';
1519
import { GitExtension } from './model';
1620
import { getServerSettings } from './server';
1721
import { gitIcon } from './style/icons';
@@ -169,6 +173,14 @@ async function activate(
169173

170174
// Add the status bar widget
171175
addStatusBarWidget(statusBar, gitExtension, settings);
176+
177+
// Add the context menu items for the default file browser
178+
addFileBrowserContextMenu(
179+
gitExtension,
180+
factory.tracker,
181+
app.commands,
182+
app.contextMenu
183+
);
172184
}
173185

174186
return gitExtension;

0 commit comments

Comments
 (0)