Skip to content

Commit 692f9c0

Browse files
authored
Update to JLab 3.1 (#979)
* Allow menu and context menu customization Bump to JLab 3.1 * Fix jest configuration * Fix context menu * Fix tests
1 parent 282cb6d commit 692f9c0

File tree

8 files changed

+2952
-1129
lines changed

8 files changed

+2952
-1129
lines changed

jest.config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ var tsOptions = tsConfig['compilerOptions'];
55
tsOptions['rootDir'] = null;
66
tsOptions['inlineSourceMap'] = true;
77

8+
const esModules = [
9+
'.*@jupyterlab/',
10+
'lib0',
11+
'y\\-protocols',
12+
'y\\-websocket',
13+
'yjs'
14+
].join('|');
15+
816
module.exports = {
917
automock: false,
1018
moduleNameMapper: {
@@ -20,7 +28,7 @@ module.exports = {
2028
setupFiles: ['<rootDir>/testutils/jest-setup-files.js'],
2129
testPathIgnorePatterns: ['/lib/', '/node_modules/', '/jupyterlab_git/'],
2230
testRegex: '/tests/.*.spec.ts[x]?$',
23-
transformIgnorePatterns: ['/node_modules/(?!(@jupyterlab/.*)/)'],
31+
transformIgnorePatterns: [`/node_modules/(?!${esModules}).+`],
2432
globals: {
2533
'ts-jest': {
2634
tsconfig: tsOptions

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"@lumino/commands": "^1.12.0",
6666
"@lumino/coreutils": "^1.5.3",
6767
"@lumino/disposable": "^1.4.3",
68+
"@lumino/messaging": "^1.7.1",
6869
"@lumino/polling": "^1.3.3",
6970
"@lumino/signaling": "^1.4.3",
7071
"@lumino/widgets": "^1.17.0",

schema/plugin.json

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,87 @@
7272
"keys": ["Accel Enter"],
7373
"selector": ".jp-git-CommitBox"
7474
}
75-
]
75+
],
76+
"jupyter.lab.menus": {
77+
"main": [
78+
{
79+
"id": "jp-mainmenu-git",
80+
"label": "Git",
81+
"rank": 60,
82+
"items": [
83+
{
84+
"command": "git:init"
85+
},
86+
{
87+
"command": "git:clone"
88+
},
89+
{
90+
"command": "git:push"
91+
},
92+
{
93+
"command": "git:pull"
94+
},
95+
{
96+
"command": "git:add-remote"
97+
},
98+
{
99+
"command": "git:terminal-command"
100+
},
101+
{
102+
"type": "separator"
103+
},
104+
{
105+
"command": "git:toggle-simple-staging"
106+
},
107+
{
108+
"command": "git:toggle-double-click-diff"
109+
},
110+
{
111+
"type": "separator"
112+
},
113+
{
114+
"command": "git:open-gitignore"
115+
},
116+
{
117+
"type": "separator"
118+
},
119+
{
120+
"type": "submenu",
121+
"submenu": {
122+
"id": "jp-mainmenu-git-help",
123+
"label": "Help",
124+
"items": [
125+
{
126+
"command": "git:open-url",
127+
"args": {
128+
"text": "Set Up Remotes",
129+
"url": "https://www.atlassian.com/git/tutorials/setting-up-a-repository"
130+
}
131+
},
132+
{
133+
"command": "git:open-url",
134+
"args": {
135+
"text": "Git Documentation",
136+
"url": "https://git-scm.com/doc"
137+
}
138+
}
139+
]
140+
}
141+
}
142+
]
143+
}
144+
],
145+
"context": [
146+
{
147+
"type": "submenu",
148+
"selector": ".jp-DirListing-item[data-isdir=\"false\"]",
149+
"rank": 5,
150+
"submenu": {
151+
"id": "jp-contextmenu-git",
152+
"label": "Git",
153+
"icon": "git"
154+
}
155+
}
156+
]
157+
}
76158
}

src/commandsAndMenu.tsx

Lines changed: 118 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { ArrayExt, toArray } from '@lumino/algorithm';
2020
import { CommandRegistry } from '@lumino/commands';
2121
import { PromiseDelegate } from '@lumino/coreutils';
2222
import { Message } from '@lumino/messaging';
23-
import { Menu, Panel } from '@lumino/widgets';
23+
import { ContextMenu, Menu, Panel } from '@lumino/widgets';
2424
import * as React from 'react';
2525
import { DiffModel } from './components/diff/model';
2626
import { createPlainTextDiff } from './components/diff/PlainTextDiff';
@@ -1009,9 +1009,6 @@ export function createGitMenu(
10091009
return menu;
10101010
}
10111011

1012-
// matches only non-directory items
1013-
const selectorNotDir = '.jp-DirListing-item[data-isdir="false"]';
1014-
10151012
export function addMenuItems(
10161013
commands: ContextCommandIDs[],
10171014
contextMenu: Menu,
@@ -1043,12 +1040,11 @@ export function addMenuItems(
10431040
}
10441041

10451042
/**
1046-
* Add Git context (sub)menu to the file browser context menu.
1043+
* Populate Git context submenu depending on the selected files.
10471044
*/
10481045
export function addFileBrowserContextMenu(
10491046
model: IGitExtension,
10501047
tracker: WidgetTracker<FileBrowser>,
1051-
commands: CommandRegistry,
10521048
contextMenu: ContextMenuSvg
10531049
): void {
10541050
function getSelectedBrowserItems(): Contents.IModel[] {
@@ -1059,112 +1055,133 @@ export function addFileBrowserContextMenu(
10591055
return toArray(widget.selectedItems());
10601056
}
10611057

1062-
class GitMenu extends Menu {
1063-
private _commands: ContextCommandIDs[];
1064-
private _paths: string[];
1065-
1066-
protected onBeforeAttach(msg: Message) {
1067-
// Render using the most recent model (even if possibly outdated)
1068-
this.updateItems();
1069-
const renderedStatus = model.status;
1070-
1071-
// Trigger refresh before the menu is displayed
1072-
model
1073-
.refreshStatus()
1074-
.then(() => {
1075-
if (model.status !== renderedStatus) {
1076-
// update items if needed
1077-
this.updateItems();
1078-
}
1079-
})
1080-
.catch(error => {
1081-
console.error(
1082-
'Fail to refresh model when displaying git context menu.',
1083-
error
1084-
);
1085-
});
1086-
super.onBeforeAttach(msg);
1087-
}
1088-
1089-
protected updateItems(): void {
1090-
const wasShown = this.isVisible;
1091-
const parent = this.parentMenu;
1092-
1093-
const items = getSelectedBrowserItems();
1094-
const statuses = new Set<Git.Status>(
1095-
items.map(item => model.getFile(item.path).status)
1096-
);
1097-
1098-
// get commands and de-duplicate them
1099-
const allCommands = new Set<ContextCommandIDs>(
1100-
// flatten the list of lists of commands
1101-
[]
1102-
.concat(...[...statuses].map(status => CONTEXT_COMMANDS[status]))
1103-
// filter out the Open and Delete commands as
1104-
// those are not needed in file browser
1105-
.filter(
1106-
command =>
1107-
command !== ContextCommandIDs.gitFileOpen &&
1108-
command !== ContextCommandIDs.gitFileDelete &&
1109-
typeof command !== 'undefined'
1110-
)
1111-
// replace stage and track with a single "add" operation
1112-
.map(command =>
1113-
command === ContextCommandIDs.gitFileStage ||
1114-
command === ContextCommandIDs.gitFileTrack
1115-
? ContextCommandIDs.gitFileAdd
1116-
: command
1117-
)
1058+
let gitMenu: Menu;
1059+
let _commands: ContextCommandIDs[];
1060+
let _paths: string[];
1061+
1062+
function updateItems(menu: Menu): void {
1063+
const wasShown = menu.isVisible;
1064+
const parent = menu.parentMenu;
1065+
1066+
const items = getSelectedBrowserItems();
1067+
const statuses = new Set<Git.Status>(
1068+
items
1069+
.map(item => model.getFile(item.path)?.status)
1070+
.filter(status => typeof status !== 'undefined')
1071+
);
1072+
1073+
// get commands and de-duplicate them
1074+
const allCommands = new Set<ContextCommandIDs>(
1075+
// flatten the list of lists of commands
1076+
[]
1077+
.concat(...[...statuses].map(status => CONTEXT_COMMANDS[status]))
1078+
// filter out the Open and Delete commands as
1079+
// those are not needed in file browser
1080+
.filter(
1081+
command =>
1082+
command !== ContextCommandIDs.gitFileOpen &&
1083+
command !== ContextCommandIDs.gitFileDelete &&
1084+
typeof command !== 'undefined'
1085+
)
1086+
// replace stage and track with a single "add" operation
1087+
.map(command =>
1088+
command === ContextCommandIDs.gitFileStage ||
1089+
command === ContextCommandIDs.gitFileTrack
1090+
? ContextCommandIDs.gitFileAdd
1091+
: command
1092+
)
1093+
);
1094+
1095+
const commandsChanged =
1096+
!_commands ||
1097+
_commands.length !== allCommands.size ||
1098+
!_commands.every(command => allCommands.has(command));
1099+
1100+
const paths = items.map(item => item.path);
1101+
1102+
const filesChanged = !_paths || !ArrayExt.shallowEqual(_paths, paths);
1103+
1104+
if (commandsChanged || filesChanged) {
1105+
const commandsList = [...allCommands];
1106+
menu.clearItems();
1107+
addMenuItems(
1108+
commandsList,
1109+
menu,
1110+
paths
1111+
.map(path => model.getFile(path))
1112+
// if file cannot be resolved (has no action available),
1113+
// omit the undefined result
1114+
.filter(file => typeof file !== 'undefined')
11181115
);
11191116

1120-
// if looking at a tracked file without any actions available
1121-
// (although `git rm` would be a valid action)
1122-
if (allCommands.size === 0) {
1123-
allCommands.add(ContextCommandIDs.gitNoAction);
1117+
if (wasShown) {
1118+
// show the menu again after downtime for refresh
1119+
parent.triggerActiveItem();
11241120
}
1121+
_commands = commandsList;
1122+
_paths = paths;
1123+
}
1124+
}
11251125

1126-
const commandsChanged =
1127-
!this._commands ||
1128-
this._commands.length !== allCommands.size ||
1129-
!this._commands.every(command => allCommands.has(command));
1130-
1131-
const paths = items.map(item => item.path);
1126+
function updateGitMenu(contextMenu: ContextMenu) {
1127+
if (!gitMenu) {
1128+
gitMenu =
1129+
contextMenu.menu.items.find(
1130+
item =>
1131+
item.type === 'submenu' && item.submenu?.id === 'jp-contextmenu-git'
1132+
)?.submenu ?? null;
1133+
}
11321134

1133-
const filesChanged =
1134-
!this._paths || !ArrayExt.shallowEqual(this._paths, paths);
1135+
if (!gitMenu) {
1136+
return; // Bail early if the open with menu is not displayed
1137+
}
11351138

1136-
if (commandsChanged || filesChanged) {
1137-
const commandsList = [...allCommands];
1138-
this.clearItems();
1139-
addMenuItems(
1140-
commandsList,
1141-
this,
1142-
paths.map(path => model.getFile(path))
1143-
);
1144-
if (wasShown) {
1145-
// show the menu again after downtime for refresh
1146-
parent.triggerActiveItem();
1139+
// Render using the most recent model (even if possibly outdated)
1140+
updateItems(gitMenu);
1141+
const renderedStatus = model.status;
1142+
1143+
// Trigger refresh before the menu is displayed
1144+
model
1145+
.refreshStatus()
1146+
.then(() => {
1147+
if (model.status !== renderedStatus) {
1148+
// update items if needed
1149+
updateItems(gitMenu);
11471150
}
1148-
this._commands = commandsList;
1149-
this._paths = paths;
1150-
}
1151-
}
1151+
})
1152+
.catch(error => {
1153+
console.error(
1154+
'Fail to refresh model when displaying git context menu.',
1155+
error
1156+
);
1157+
});
1158+
}
11521159

1153-
onBeforeShow(msg: Message): void {
1154-
super.onBeforeShow(msg);
1160+
// as any is to support JLab 3.1 feature
1161+
if ((contextMenu as any).opened) {
1162+
(contextMenu as any).opened.connect(updateGitMenu);
1163+
} else {
1164+
// matches only non-directory items
1165+
1166+
class GitMenu extends Menu {
1167+
protected onBeforeAttach(msg: Message): void {
1168+
updateGitMenu(contextMenu);
1169+
super.onBeforeAttach(msg);
1170+
}
11551171
}
1156-
}
11571172

1158-
const gitMenu = new GitMenu({ commands });
1159-
gitMenu.title.label = 'Git';
1160-
gitMenu.title.icon = gitIcon.bindprops({ stylesheet: 'menuItem' });
1173+
const selectorNotDir = '.jp-DirListing-item[data-isdir="false"]';
1174+
gitMenu = new GitMenu({ commands: contextMenu.menu.commands });
1175+
gitMenu.title.label = 'Git';
1176+
gitMenu.title.icon = gitIcon.bindprops({ stylesheet: 'menuItem' });
11611177

1162-
contextMenu.addItem({
1163-
type: 'submenu',
1164-
submenu: gitMenu,
1165-
selector: selectorNotDir,
1166-
rank: 5
1167-
});
1178+
contextMenu.addItem({
1179+
type: 'submenu',
1180+
submenu: gitMenu,
1181+
selector: selectorNotDir,
1182+
rank: 5
1183+
});
1184+
}
11681185
}
11691186

11701187
/* eslint-disable no-inner-declarations */

src/components/diff/NotebookDiff.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ export class NotebookDiff extends Panel implements Git.Diff.IDiffWidget {
203203

204204
const model = new NotebookDiffModel(data.base, data.diff);
205205

206+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
207+
// @ts-ignore
206208
return new NotebookDiffWidget(model, this._renderMime);
207209
}
208210

0 commit comments

Comments
 (0)