Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up git menu #699

Merged
merged 5 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 9 additions & 55 deletions src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Dialog, showDialog } from '@jupyterlab/apputils';
import { PathExt } from '@jupyterlab/coreutils';
import {
caretDownIcon,
Expand All @@ -22,52 +21,9 @@ import {
toolbarNavClass
} from '../style/Toolbar';
import { IGitExtension } from '../tokens';
import { GitCredentialsForm } from '../widgets/CredentialsBox';
import { GitPullPushDialog, Operation } from '../widgets/gitPushPull';
import { ActionButton } from './ActionButton';
import { BranchMenu } from './BranchMenu';

/**
* Displays an error dialog when a Git operation fails.
*
* @private
* @param model - Git extension model
* @param operation - Git operation name
* @returns Promise for displaying a dialog
*/
async function showGitOperationDialog(
model: IGitExtension,
operation: Operation
): Promise<void> {
const title = `Git ${operation}`;
let result = await showDialog({
title: title,
body: new GitPullPushDialog(model, operation),
buttons: [Dialog.okButton({ label: 'DISMISS' })]
});
let retry = false;
while (!result.button.accept) {
const credentials = await showDialog({
title: 'Git credentials required',
body: new GitCredentialsForm(
'Enter credentials for remote repository',
retry ? 'Incorrect username or password.' : ''
),
buttons: [Dialog.cancelButton(), Dialog.okButton({ label: 'OK' })]
});

if (!credentials.button.accept) {
break;
}

result = await showDialog({
title: title,
body: new GitPullPushDialog(model, operation, credentials.value),
buttons: [Dialog.okButton({ label: 'DISMISS' })]
});
retry = true;
}
}
import { CommandIDs } from '../gitMenuCommands';

/**
* Interface describing component properties.
Expand Down Expand Up @@ -304,11 +260,10 @@ export class Toolbar extends React.Component<IToolbarProps, IToolbarState> {
* @param event - event object
*/
private _onPullClick = (): void => {
showGitOperationDialog(this.props.model, Operation.Pull).catch(reason => {
console.error(
`Encountered an error when pulling changes. Error: ${reason}`
);
});
const commands = this.props.model.commands;
if (commands) {
commands.execute(CommandIDs.gitPull);
}
};

/**
Expand All @@ -317,11 +272,10 @@ export class Toolbar extends React.Component<IToolbarProps, IToolbarState> {
* @param event - event object
*/
private _onPushClick = (): void => {
showGitOperationDialog(this.props.model, Operation.Push).catch(reason => {
console.error(
`Encountered an error when pushing changes. Error: ${reason}`
);
});
const commands = this.props.model.commands;
if (commands) {
commands.execute(CommandIDs.gitPush);
}
};

/**
Expand Down
96 changes: 90 additions & 6 deletions src/gitMenuCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ITerminal } from '@jupyterlab/terminal';
import { IGitExtension } from './tokens';
import { doGitClone } from './widgets/gitClone';
import { GitPullPushDialog, Operation } from './widgets/gitPushPull';
import { GitCredentialsForm } from './widgets/CredentialsBox';

/**
* The command IDs used by the git plugin.
Expand All @@ -24,6 +26,8 @@ export namespace CommandIDs {
export const gitToggleDoubleClickDiff = 'git:toggle-double-click-diff';
export const gitAddRemote = 'git:add-remote';
export const gitClone = 'git:clone';
export const gitPush = 'git:push';
export const gitPull = 'git:pull';
}

/**
Expand Down Expand Up @@ -63,7 +67,8 @@ export function addCommands(
console.error(e);
main.dispose();
}
}
},
isEnabled: () => model.pathRepository !== null
});

/** Add open/go to git interface command */
Expand All @@ -81,8 +86,8 @@ export function addCommands(

/** Add git init command */
commands.addCommand(CommandIDs.gitInit, {
label: 'Init',
caption: ' Create an empty Git repository or reinitialize an existing one',
label: 'Initialize a Repository',
caption: 'Create an empty Git repository or reinitialize an existing one',
execute: async () => {
const currentPath = fileBrowser.model.path;
const result = await showDialog({
Expand All @@ -95,7 +100,8 @@ export function addCommands(
await model.init(currentPath);
model.pathRepository = currentPath;
}
}
},
isEnabled: () => model.pathRepository === null
});

/** Open URL externally */
Expand Down Expand Up @@ -127,7 +133,7 @@ export function addCommands(

/** Command to add a remote Git repository */
commands.addCommand(CommandIDs.gitAddRemote, {
label: 'Add remote repository',
label: 'Add Remote Repository',
caption: 'Add a Git remote repository',
isEnabled: () => model.pathRepository !== null,
execute: async args => {
Expand Down Expand Up @@ -162,12 +168,90 @@ export function addCommands(

/** Add git clone command */
commands.addCommand(CommandIDs.gitClone, {
label: 'Clone',
label: 'Clone a Repository',
caption: 'Clone a repository from a URL',
isEnabled: () => model.pathRepository === null,
execute: async () => {
await doGitClone(model, fileBrowser.model.path);
fileBrowser.model.refresh();
}
});

/** Add git push command */
commands.addCommand(CommandIDs.gitPush, {
label: 'Push to Remote',
caption: 'Push code to remote repository',
isEnabled: () => model.pathRepository !== null,
execute: async () => {
await Private.showGitOperationDialog(model, Operation.Push).catch(
reason => {
console.error(
`Encountered an error when pushing changes. Error: ${reason}`
);
}
);
}
});

/** Add git pull command */
commands.addCommand(CommandIDs.gitPull, {
label: 'Pull from Remote',
caption: 'Pull latest code from remote repository',
isEnabled: () => model.pathRepository !== null,
execute: async () => {
await Private.showGitOperationDialog(model, Operation.Pull).catch(
reason => {
console.error(
`Encountered an error when pulling changes. Error: ${reason}`
);
}
);
}
});
}

/* eslint-disable no-inner-declarations */
namespace Private {
/**
* Displays an error dialog when a Git operation fails.
*
* @private
* @param model - Git extension model
* @param operation - Git operation name
* @returns Promise for displaying a dialog
*/
export async function showGitOperationDialog(
model: IGitExtension,
operation: Operation
): Promise<void> {
const title = `Git ${operation}`;
let result = await showDialog({
title: title,
body: new GitPullPushDialog(model, operation),
buttons: [Dialog.okButton({ label: 'DISMISS' })]
});
let retry = false;
while (!result.button.accept) {
const credentials = await showDialog({
title: 'Git credentials required',
body: new GitCredentialsForm(
'Enter credentials for remote repository',
retry ? 'Incorrect username or password.' : ''
),
buttons: [Dialog.cancelButton(), Dialog.okButton({ label: 'OK' })]
});

if (!credentials.button.accept) {
break;
}

result = await showDialog({
title: title,
body: new GitPullPushDialog(model, operation, credentials.value),
buttons: [Dialog.okButton({ label: 'DISMISS' })]
});
retry = true;
}
}
}
/* eslint-enable no-inner-declarations */
24 changes: 14 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,30 +143,34 @@ function createGitMenu(
const menu = new Menu({ commands });
menu.title.label = 'Git';
[
CommandIDs.gitUI,
CommandIDs.gitTerminalCommand,
CommandIDs.gitInit,
CommandIDs.gitClone,
CommandIDs.gitAddRemote
CommandIDs.gitPush,
CommandIDs.gitPull,
CommandIDs.gitAddRemote,
CommandIDs.gitTerminalCommand
].forEach(command => {
menu.addItem({ command });
});

menu.addItem({ type: 'separator' });

menu.addItem({ command: CommandIDs.gitToggleSimpleStaging });

menu.addItem({ command: CommandIDs.gitToggleDoubleClickDiff });

menu.addItem({ type: 'separator' });

const tutorial = new Menu({ commands });
tutorial.title.label = ' Tutorial ';
tutorial.title.label = ' Help ';
RESOURCES.map(args => {
tutorial.addItem({
args,
command: CommandIDs.gitOpenUrl
});
});
menu.addItem({ type: 'submenu', submenu: tutorial });

menu.addItem({ type: 'separator' });

menu.addItem({ command: CommandIDs.gitToggleSimpleStaging });

menu.addItem({ command: CommandIDs.gitToggleDoubleClickDiff });
menu.addItem({ type: 'submenu', submenu: tutorial });

return menu;
}
3 changes: 3 additions & 0 deletions src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DocumentRegistry } from '@jupyterlab/docregistry';
import { Token, JSONObject } from '@lumino/coreutils';
import { IDisposable } from '@lumino/disposable';
import { ISignal } from '@lumino/signaling';
import { CommandRegistry } from '@lumino/commands';

export const EXTENSION_ID = 'jupyter.extensions.git_plugin';

Expand Down Expand Up @@ -57,6 +58,8 @@ export interface IGitExtension extends IDisposable {
*/
readonly statusChanged: ISignal<IGitExtension, Git.IStatusFileResult[]>;

readonly commands: CommandRegistry | null;

/**
* Make request to add one or all files into
* the staging area in repository
Expand Down