Skip to content

Commit c639273

Browse files
Add git to command palette (#1243)
* Add git to command palette * Add git to command palette * Add git to command palette * Add git to command palette * Finish up the PR * Deduplicate yjs * Give another shot at fixing yjs * Fix tests * Lint the code * Use debug mode * Turn off debug * Set up git identity for integration tests --------- Co-authored-by: Frédéric Collonval <fcollonval@gmail.com>
1 parent 37d922b commit c639273

21 files changed

+1283
-3584
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ jobs:
141141
run: jlpm playwright install chromium
142142
working-directory: ui-tests
143143

144+
- name: Set up git
145+
run: |
146+
git config --global user.name 'Test Bot'
147+
git config --global user.email 'test-bot@example.com'
148+
144149
- name: Execute integration tests
145150
working-directory: ui-tests
146151
run: |

jupyterlab_git/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818

1919
def _jupyter_labextension_paths():
20-
return [{"src": "labextension", "dest": "@jupyerlab/git"}]
20+
return [{"src": "labextension", "dest": "@jupyterlab/git"}]
2121

2222

2323
class JupyterLabGit(Configurable):

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@
134134
"stylelint-config-recommended": "^13.0.0",
135135
"stylelint-config-standard": "~34.0.0",
136136
"ts-jest": "^26.0.0",
137-
"typescript": "~4.3.5"
137+
"typescript": "~4.3.5",
138+
"yarn-dedupe": "^0.2.3",
139+
"yjs": "^13.5.40"
138140
},
139141
"peerDependencies": {
140142
"codemirror": "^5.0.0"

src/__tests__/commands.spec.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ describe('git-commands', () => {
8080
iconLabel: '',
8181
label: ''
8282
},
83+
isChecked: null,
8384
value: undefined
8485
});
8586
const spyReset = jest.spyOn(model, 'reset');
@@ -146,6 +147,7 @@ describe('git-commands', () => {
146147
iconLabel: '',
147148
label: ''
148149
},
150+
isChecked: null,
149151
value: {
150152
checked
151153
} as Git.ICheckboxFormValue

src/__tests__/plugin.spec.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { ISettingRegistry, SettingRegistry } from '@jupyterlab/settingregistry';
66
import { JupyterLab } from '@jupyterlab/application';
77
import { showErrorMessage } from '@jupyterlab/apputils';
88
import { URLExt } from '@jupyterlab/coreutils';
9+
import { Signal } from '@lumino/signaling';
910
import {
1011
defaultMockedResponses,
1112
IMockedResponses,
1213
mockedRequestAPI
1314
} from './utils';
15+
import { IFileBrowserFactory } from '@jupyterlab/filebrowser';
1416

1517
jest.mock('../git');
1618
jest.mock('@jupyterlab/application');
@@ -22,11 +24,17 @@ const plugin = plugins[0];
2224
describe('plugin', () => {
2325
const mockGit = git as jest.Mocked<typeof git>;
2426
let app: jest.Mocked<JupyterLab>;
27+
let browserFactory: jest.Mocked<IFileBrowserFactory>;
2528
let mockResponses: IMockedResponses = {};
2629
let settingRegistry: jest.Mocked<ISettingRegistry>;
2730

2831
beforeAll(() => {
2932
app = new JupyterLab() as jest.Mocked<JupyterLab>;
33+
browserFactory = {
34+
defaultBrowser: {
35+
model: { pathChanged: new Signal(null), restored: Promise.resolve() }
36+
}
37+
} as unknown as jest.Mocked<IFileBrowserFactory>;
3038
settingRegistry = new SettingRegistry({
3139
connector: null
3240
}) as jest.Mocked<SettingRegistry>;
@@ -59,7 +67,7 @@ describe('plugin', () => {
5967
const extension = await plugin.activate(
6068
app,
6169
null,
62-
null,
70+
browserFactory,
6371
{ tracker: { currentWidget: null } },
6472
null,
6573
settingRegistry
@@ -94,7 +102,7 @@ describe('plugin', () => {
94102
const extension = await plugin.activate(
95103
app,
96104
null,
97-
null,
105+
browserFactory,
98106
{ tracker: { currentWidget: null } },
99107
null,
100108
settingRegistry
@@ -128,7 +136,7 @@ describe('plugin', () => {
128136
const extension = await plugin.activate(
129137
app,
130138
null,
131-
null,
139+
browserFactory,
132140
{ tracker: { currentWidget: null } },
133141
null,
134142
settingRegistry
@@ -159,7 +167,7 @@ describe('plugin', () => {
159167
const extension = await plugin.activate(
160168
app,
161169
null,
162-
null,
170+
browserFactory,
163171
{ tracker: { currentWidget: null } },
164172
null,
165173
settingRegistry

src/__tests__/test-components/BranchMenu.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ describe('BranchMenu', () => {
246246
iconLabel: '',
247247
label: ''
248248
},
249+
isChecked: null,
249250
value: undefined
250251
});
251252
});

src/__tests__/test-components/GitPanel.spec.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ describe('GitPanel', () => {
121121
iconLabel: '',
122122
label: ''
123123
},
124+
isChecked: null,
124125
value: {
125126
name: commitUser['user.name'],
126127
email: commitUser['user.email']
@@ -207,6 +208,7 @@ describe('GitPanel', () => {
207208
...dialogValue.button,
208209
accept: false
209210
},
211+
isChecked: null,
210212
value: null
211213
});
212214

src/cloneCommand.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
66
import { CommandIDs, IGitExtension, Level } from './tokens';
77
import { IFileBrowserFactory } from '@jupyterlab/filebrowser';
8-
import { Dialog, showDialog } from '@jupyterlab/apputils';
8+
import { Dialog, ICommandPalette, showDialog } from '@jupyterlab/apputils';
99
import { GitCloneForm } from './widgets/GitCloneForm';
1010
import { logger } from './logger';
1111
import {
@@ -19,14 +19,16 @@ import { addCloneButton } from './widgets/gitClone';
1919

2020
export const gitCloneCommandPlugin: JupyterFrontEndPlugin<void> = {
2121
id: '@jupyterlab/git:clone',
22-
requires: [ITranslator, IGitExtension, IFileBrowserFactory],
22+
requires: [IGitExtension, IFileBrowserFactory],
23+
optional: [ICommandPalette, ITranslator],
2324
activate: (
2425
app: JupyterFrontEnd,
25-
translator: ITranslator,
2626
gitModel: IGitExtension,
27-
fileBrowserFactory: IFileBrowserFactory
27+
fileBrowserFactory: IFileBrowserFactory,
28+
palette: ICommandPalette | null,
29+
translator: ITranslator | null
2830
) => {
29-
translator = translator || nullTranslator;
31+
translator = translator ?? nullTranslator;
3032
const trans = translator.load('jupyterlab_git');
3133
const fileBrowser = fileBrowserFactory.defaultBrowser;
3234
const fileBrowserModel = fileBrowser.model;
@@ -89,6 +91,12 @@ export const gitCloneCommandPlugin: JupyterFrontEndPlugin<void> = {
8991

9092
// Add the context menu items for the default file browser
9193
addFileBrowserContextMenu(gitModel, fileBrowser, app.contextMenu, trans);
94+
95+
if (palette) {
96+
// Add the commands to the command palette
97+
const category = 'Git Operations';
98+
palette.addItem({ command: CommandIDs.gitClone, category });
99+
}
92100
},
93101
autoStart: true
94102
};

src/index.ts

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import {
33
JupyterFrontEnd,
44
JupyterFrontEndPlugin
55
} from '@jupyterlab/application';
6-
import { Dialog, showErrorMessage, Toolbar } from '@jupyterlab/apputils';
6+
import {
7+
Dialog,
8+
ICommandPalette,
9+
showErrorMessage,
10+
Toolbar
11+
} from '@jupyterlab/apputils';
712
import { IChangedArgs } from '@jupyterlab/coreutils';
813
import { IDocumentManager } from '@jupyterlab/docmanager';
914
import { FileBrowserModel, IFileBrowserFactory } from '@jupyterlab/filebrowser';
@@ -12,43 +17,41 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
1217
import { ISettingRegistry } from '@jupyterlab/settingregistry';
1318
import { IStatusBar } from '@jupyterlab/statusbar';
1419
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
20+
import { gitCloneCommandPlugin } from './cloneCommand';
1521
import {
1622
addCommands,
1723
addFileBrowserContextMenu,
1824
createGitMenu
1925
} from './commandsAndMenu';
26+
import { createImageDiff } from './components/diff/ImageDiff';
2027
import { createNotebookDiff } from './components/diff/NotebookDiff';
2128
import { addStatusBarWidget } from './components/StatusWidget';
2229
import { GitExtension } from './model';
2330
import { getServerSettings } from './server';
2431
import { gitIcon } from './style/icons';
25-
import { Git, IGitExtension } from './tokens';
32+
import { CommandIDs, Git, IGitExtension } from './tokens';
2633
import { addCloneButton } from './widgets/gitClone';
2734
import { GitWidget } from './widgets/GitWidget';
28-
import { gitCloneCommandPlugin } from './cloneCommand';
29-
import { createImageDiff } from './components/diff/ImageDiff';
3035

3136
export { DiffModel } from './components/diff/model';
3237
export { NotebookDiff } from './components/diff/NotebookDiff';
3338
export { PlainTextDiff } from './components/diff/PlainTextDiff';
34-
export { Git, IGitExtension } from './tokens';
3539
export { logger, LoggerContext } from './logger';
40+
export { Git, IGitExtension } from './tokens';
3641

3742
/**
3843
* The default running sessions extension.
3944
*/
4045
const plugin: JupyterFrontEndPlugin<IGitExtension> = {
4146
id: '@jupyterlab/git:plugin',
4247
requires: [
43-
IMainMenu,
4448
ILayoutRestorer,
4549
IFileBrowserFactory,
4650
IRenderMimeRegistry,
4751
ISettingRegistry,
48-
IDocumentManager,
49-
IStatusBar,
50-
ITranslator
52+
IDocumentManager
5153
],
54+
optional: [IMainMenu, IStatusBar, ICommandPalette, ITranslator],
5255
provides: IGitExtension,
5356
activate,
5457
autoStart: true
@@ -64,14 +67,15 @@ export default [plugin, gitCloneCommandPlugin];
6467
*/
6568
async function activate(
6669
app: JupyterFrontEnd,
67-
mainMenu: IMainMenu,
6870
restorer: ILayoutRestorer,
6971
factory: IFileBrowserFactory,
7072
renderMime: IRenderMimeRegistry,
7173
settingRegistry: ISettingRegistry,
7274
docmanager: IDocumentManager,
73-
statusBar: IStatusBar,
74-
translator?: ITranslator
75+
mainMenu: IMainMenu | null,
76+
statusBar: IStatusBar | null,
77+
palette: ICommandPalette | null,
78+
translator: ITranslator | null
7579
): Promise<IGitExtension> {
7680
let gitExtension: GitExtension | null = null;
7781
let settings: ISettingRegistry.ISettings;
@@ -82,7 +86,7 @@ async function activate(
8286
// And it is unlikely that another browser than the default will be used.
8387
// Ref: https://github.com/jupyterlab/jupyterlab-git/issues/1014
8488
const fileBrowser = factory.defaultBrowser;
85-
translator = translator || nullTranslator;
89+
translator = translator ?? nullTranslator;
8690
const trans = translator.load('jupyterlab_git');
8791

8892
// Attempt to load application settings
@@ -186,6 +190,24 @@ async function activate(
186190
gitPlugin.title.icon = gitIcon;
187191
gitPlugin.title.caption = 'Git';
188192

193+
if (palette) {
194+
// Add the commands to the command palette
195+
const category = 'Git Operations';
196+
[
197+
CommandIDs.gitToggleSimpleStaging,
198+
CommandIDs.gitToggleDoubleClickDiff,
199+
CommandIDs.gitOpenGitignore,
200+
CommandIDs.gitShowDiff,
201+
CommandIDs.gitInit,
202+
CommandIDs.gitMerge,
203+
CommandIDs.gitPush,
204+
CommandIDs.gitPull,
205+
CommandIDs.gitResetToRemote,
206+
CommandIDs.gitManageRemote,
207+
CommandIDs.gitTerminalCommand
208+
].forEach(command => palette.addItem({ command, category }));
209+
}
210+
189211
// Let the application restorer track the running panel for restoration of
190212
// application state (e.g. setting the running panel as the current side bar
191213
// widget).
@@ -196,7 +218,7 @@ async function activate(
196218
app.shell.add(gitPlugin, 'left', { rank: 200 });
197219

198220
// Add a menu for the plugin
199-
if (app.version.split('.').slice(0, 2).join('.') < '3.1') {
221+
if (mainMenu && app.version.split('.').slice(0, 2).join('.') < '3.1') {
200222
// Support JLab 3.0
201223
mainMenu.addMenu(createGitMenu(app.commands, trans), { rank: 60 });
202224
}
@@ -205,7 +227,9 @@ async function activate(
205227
addCloneButton(gitExtension, fileBrowser, app.commands, trans);
206228

207229
// Add the status bar widget
208-
addStatusBarWidget(statusBar, gitExtension, settings, trans);
230+
if (statusBar) {
231+
addStatusBarWidget(statusBar, gitExtension, settings, trans);
232+
}
209233

210234
// Add the context menu items for the default file browser
211235
addFileBrowserContextMenu(

ui-tests/jupyter_server_test_config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@
44
opens the server to the world and provide access to JupyterLab
55
JavaScript objects through the global window variable.
66
"""
7+
import sys
78
from tempfile import mkdtemp
89

10+
try:
11+
import jupyter_archive
12+
except ImportError:
13+
print("You must install `jupyter-archive` for the integration tests.")
14+
sys.exit(1)
15+
916
c.ServerApp.port = 8888
1017
c.ServerApp.port_retries = 0
1118
c.ServerApp.open_browser = False

0 commit comments

Comments
 (0)