|
@@ -10,7 +10,7 @@ import {
|
|
} from '@jupyterlab/application';
|
|
} from '@jupyterlab/application';
|
|
|
|
|
|
import {
|
|
import {
|
|
- ICommandPalette, IMainMenu
|
|
|
|
|
|
+ ICommandPalette, IMainMenu, IStateDB
|
|
} from '@jupyterlab/apputils';
|
|
} from '@jupyterlab/apputils';
|
|
|
|
|
|
import {
|
|
import {
|
|
@@ -34,7 +34,7 @@ namespace CommandIDs {
|
|
const matchBrackets = 'codemirror:match-brackets';
|
|
const matchBrackets = 'codemirror:match-brackets';
|
|
|
|
|
|
export
|
|
export
|
|
- const vimMode = 'codemirror:vim-mode';
|
|
|
|
|
|
+ const changeKeyMap = 'codemirror:change-keyMap';
|
|
|
|
|
|
export
|
|
export
|
|
const changeTheme = 'codemirror:change-theme';
|
|
const changeTheme = 'codemirror:change-theme';
|
|
@@ -58,7 +58,7 @@ const servicesPlugin: JupyterLabPlugin<IEditorServices> = {
|
|
export
|
|
export
|
|
const commandsPlugin: JupyterLabPlugin<void> = {
|
|
const commandsPlugin: JupyterLabPlugin<void> = {
|
|
id: 'jupyter.services.codemirror-commands',
|
|
id: 'jupyter.services.codemirror-commands',
|
|
- requires: [IEditorTracker, IMainMenu, ICommandPalette],
|
|
|
|
|
|
+ requires: [IEditorTracker, IMainMenu, ICommandPalette, IStateDB],
|
|
activate: activateEditorCommands,
|
|
activate: activateEditorCommands,
|
|
autoStart: true
|
|
autoStart: true
|
|
};
|
|
};
|
|
@@ -74,74 +74,142 @@ export default plugins;
|
|
/**
|
|
/**
|
|
* Set up the editor widget menu and commands.
|
|
* Set up the editor widget menu and commands.
|
|
*/
|
|
*/
|
|
-function activateEditorCommands(app: JupyterLab, tracker: IEditorTracker, mainMenu: IMainMenu, palette: ICommandPalette): void {
|
|
|
|
|
|
+function activateEditorCommands(app: JupyterLab, tracker: IEditorTracker, mainMenu: IMainMenu, palette: ICommandPalette, state: IStateDB): void {
|
|
let { commands } = app;
|
|
let { commands } = app;
|
|
|
|
+ let theme: string = CodeMirrorEditor.DEFAULT_THEME;
|
|
|
|
+ let keyMap: string = 'default';
|
|
|
|
+ let matchBrackets = false;
|
|
|
|
+ let id = 'codemirror:settings';
|
|
|
|
+
|
|
|
|
+ // Fetch the initial state of the settings.
|
|
|
|
+ state.fetch(id).then(settings => {
|
|
|
|
+ if (!settings) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (typeof settings['theme'] === 'string') {
|
|
|
|
+ commands.execute(CommandIDs.changeTheme, settings);
|
|
|
|
+ }
|
|
|
|
+ if (typeof settings['keyMap'] === 'string') {
|
|
|
|
+ commands.execute(CommandIDs.changeKeyMap, settings);
|
|
|
|
+ }
|
|
|
|
+ if (typeof settings['matchBrackets'] === 'boolean') {
|
|
|
|
+ if (settings['matchBrackets'] !== matchBrackets) {
|
|
|
|
+ commands.execute(CommandIDs.matchBrackets);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Toggle editor matching brackets
|
|
|
|
|
|
+ * Save the codemirror settings state.
|
|
*/
|
|
*/
|
|
- function toggleMatchBrackets(): void {
|
|
|
|
- if (tracker.currentWidget) {
|
|
|
|
- let editor = tracker.currentWidget.editor;
|
|
|
|
- if (editor instanceof CodeMirrorEditor) {
|
|
|
|
- let cm = editor.editor;
|
|
|
|
- cm.setOption('matchBrackets', !cm.getOption('matchBrackets'));
|
|
|
|
- }
|
|
|
|
|
|
+ function saveState(): Promise<void> {
|
|
|
|
+ return state.save(id, { theme, keyMap, matchBrackets });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Handle the settings of new widgets.
|
|
|
|
+ */
|
|
|
|
+ tracker.widgetAdded.connect((sender, widget) => {
|
|
|
|
+ if (widget.editor instanceof CodeMirrorEditor) {
|
|
|
|
+ let cm = widget.editor.editor;
|
|
|
|
+ cm.setOption('keyMap', keyMap);
|
|
|
|
+ cm.setOption('theme', theme);
|
|
|
|
+ cm.setOption('matchBrackets', matchBrackets);
|
|
}
|
|
}
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * A test for whether the tracker has an active widget.
|
|
|
|
+ */
|
|
|
|
+ function hasWidget(): boolean {
|
|
|
|
+ return tracker.currentWidget !== null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Toggle the editor's vim mode
|
|
|
|
|
|
+ * Toggle editor matching brackets
|
|
*/
|
|
*/
|
|
- function toggleVim(): void {
|
|
|
|
|
|
+ function toggleMatchBrackets(): Promise<void> {
|
|
|
|
+ matchBrackets = !matchBrackets;
|
|
tracker.forEach(widget => {
|
|
tracker.forEach(widget => {
|
|
- if (widget.editor instanceof CodeMirrorEditor) {
|
|
|
|
- let cm = widget.editor.editor;
|
|
|
|
- let keymap = cm.getOption('keyMap') === 'vim' ? 'default'
|
|
|
|
- : 'vim';
|
|
|
|
- cm.setOption('keyMap', keymap);
|
|
|
|
|
|
+ let editor = widget.editor;
|
|
|
|
+ if (editor instanceof CodeMirrorEditor) {
|
|
|
|
+ let cm = editor.editor;
|
|
|
|
+ cm.setOption('matchBrackets', matchBrackets);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
+ return saveState();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Create a menu for the editor.
|
|
* Create a menu for the editor.
|
|
*/
|
|
*/
|
|
function createMenu(): Menu {
|
|
function createMenu(): Menu {
|
|
- let theme = new Menu({ commands });
|
|
|
|
let menu = new Menu({ commands });
|
|
let menu = new Menu({ commands });
|
|
|
|
+ let themeMenu = new Menu({ commands });
|
|
|
|
+ let keyMapMenu = new Menu({ commands });
|
|
|
|
|
|
menu.title.label = 'Editor';
|
|
menu.title.label = 'Editor';
|
|
- theme.title.label = 'Theme';
|
|
|
|
|
|
+ themeMenu.title.label = 'Theme';
|
|
|
|
+ keyMapMenu.title.label = 'Key Map';
|
|
|
|
|
|
commands.addCommand(CommandIDs.changeTheme, {
|
|
commands.addCommand(CommandIDs.changeTheme, {
|
|
label: args => args['theme'] as string,
|
|
label: args => args['theme'] as string,
|
|
execute: args => {
|
|
execute: args => {
|
|
- let name = args['theme'] as string || CodeMirrorEditor.DEFAULT_THEME;
|
|
|
|
|
|
+ theme = args['theme'] as string || CodeMirrorEditor.DEFAULT_THEME;
|
|
tracker.forEach(widget => {
|
|
tracker.forEach(widget => {
|
|
if (widget.editor instanceof CodeMirrorEditor) {
|
|
if (widget.editor instanceof CodeMirrorEditor) {
|
|
let cm = widget.editor.editor;
|
|
let cm = widget.editor.editor;
|
|
- cm.setOption('theme', name);
|
|
|
|
|
|
+ cm.setOption('theme', theme);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
- }
|
|
|
|
|
|
+ return saveState();
|
|
|
|
+ },
|
|
|
|
+ isEnabled: hasWidget,
|
|
|
|
+ isToggled: args => { return args['theme'] === theme; }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ commands.addCommand(CommandIDs.changeKeyMap, {
|
|
|
|
+ label: args => {
|
|
|
|
+ let title = args['keyMap'] as string;
|
|
|
|
+ return title === 'sublime' ? 'Sublime Text' : title;
|
|
|
|
+ },
|
|
|
|
+ execute: args => {
|
|
|
|
+ keyMap = args['keyMap'] as string || 'default';
|
|
|
|
+ tracker.forEach(widget => {
|
|
|
|
+ if (widget.editor instanceof CodeMirrorEditor) {
|
|
|
|
+ let cm = widget.editor.editor;
|
|
|
|
+ cm.setOption('keyMap', keyMap);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return saveState();
|
|
|
|
+ },
|
|
|
|
+ isEnabled: hasWidget,
|
|
|
|
+ isToggled: args => { return args['keyMap'] === keyMap; }
|
|
});
|
|
});
|
|
|
|
|
|
[
|
|
[
|
|
'jupyter', 'default', 'abcdef', 'base16-dark', 'base16-light',
|
|
'jupyter', 'default', 'abcdef', 'base16-dark', 'base16-light',
|
|
'hopscotch', 'material', 'mbo', 'mdn-like', 'seti', 'the-matrix',
|
|
'hopscotch', 'material', 'mbo', 'mdn-like', 'seti', 'the-matrix',
|
|
'xq-light', 'zenburn'
|
|
'xq-light', 'zenburn'
|
|
- ].forEach(name => theme.addItem({
|
|
|
|
- command: 'codemirror:change-theme',
|
|
|
|
|
|
+ ].forEach(name => themeMenu.addItem({
|
|
|
|
+ command: CommandIDs.changeTheme,
|
|
args: { theme: name }
|
|
args: { theme: name }
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
+ [
|
|
|
|
+ 'default', 'sublime', 'vim', 'emacs'
|
|
|
|
+ ].forEach(name => keyMapMenu.addItem({
|
|
|
|
+ command: CommandIDs.changeKeyMap,
|
|
|
|
+ args: { keyMap: name }
|
|
|
|
+ }));
|
|
|
|
+
|
|
menu.addItem({ command: 'editor:line-numbers' });
|
|
menu.addItem({ command: 'editor:line-numbers' });
|
|
- menu.addItem({ command: 'editor:line-wrap' });
|
|
|
|
|
|
+ menu.addItem({ command: 'editor:word-wrap' });
|
|
menu.addItem({ command: CommandIDs.matchBrackets });
|
|
menu.addItem({ command: CommandIDs.matchBrackets });
|
|
- menu.addItem({ command: CommandIDs.vimMode });
|
|
|
|
menu.addItem({ type: 'separator' });
|
|
menu.addItem({ type: 'separator' });
|
|
- menu.addItem({ type: 'submenu', submenu: theme });
|
|
|
|
|
|
+ menu.addItem({ type: 'submenu', submenu: keyMapMenu });
|
|
|
|
+ menu.addItem({ type: 'submenu', submenu: themeMenu });
|
|
|
|
|
|
return menu;
|
|
return menu;
|
|
}
|
|
}
|
|
@@ -149,20 +217,16 @@ function activateEditorCommands(app: JupyterLab, tracker: IEditorTracker, mainMe
|
|
mainMenu.addMenu(createMenu(), { rank: 30 });
|
|
mainMenu.addMenu(createMenu(), { rank: 30 });
|
|
|
|
|
|
commands.addCommand(CommandIDs.matchBrackets, {
|
|
commands.addCommand(CommandIDs.matchBrackets, {
|
|
- execute: () => { toggleMatchBrackets(); },
|
|
|
|
- label: 'Toggle Match Brackets',
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- commands.addCommand(CommandIDs.vimMode, {
|
|
|
|
- execute: () => { toggleVim(); },
|
|
|
|
- label: 'Toggle Vim Mode'
|
|
|
|
|
|
+ execute: toggleMatchBrackets,
|
|
|
|
+ label: 'Match Brackets',
|
|
|
|
+ isEnabled: hasWidget,
|
|
|
|
+ isToggled: () => { return !matchBrackets; }
|
|
});
|
|
});
|
|
|
|
|
|
[
|
|
[
|
|
'editor:line-numbers',
|
|
'editor:line-numbers',
|
|
'editor:line-wrap',
|
|
'editor:line-wrap',
|
|
CommandIDs.matchBrackets,
|
|
CommandIDs.matchBrackets,
|
|
- CommandIDs.vimMode,
|
|
|
|
'editor:create-console',
|
|
'editor:create-console',
|
|
'editor:run-code'
|
|
'editor:run-code'
|
|
].forEach(command => palette.addItem({ command, category: 'Editor' }));
|
|
].forEach(command => palette.addItem({ command, category: 'Editor' }));
|