123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import {
- JSONObject
- } from '@phosphor/coreutils';
- import {
- Menu
- } from '@phosphor/widgets';
- import {
- JupyterLab, JupyterLabPlugin
- } from '@jupyterlab/application';
- import {
- ICommandPalette, IMainMenu
- } from '@jupyterlab/apputils';
- import {
- IEditorServices
- } from '@jupyterlab/codeeditor';
- import {
- editorServices, CodeMirrorEditor, Mode
- } from '@jupyterlab/codemirror';
- import {
- ISettingRegistry, IStateDB
- } from '@jupyterlab/coreutils';
- import {
- IEditorTracker
- } from '@jupyterlab/fileeditor';
- /**
- * The command IDs used by the codemirror plugin.
- */
- namespace CommandIDs {
- export
- const changeKeyMap = 'codemirror:change-keymap';
- export
- const changeTheme = 'codemirror:change-theme';
- export
- const changeMode = 'codemirror:change-mode';
- export
- const find = 'codemirror:find';
- export
- const findAndReplace = 'codemirror:find-and-replace';
- }
- /**
- * The editor services.
- */
- const services: JupyterLabPlugin<IEditorServices> = {
- id: '@jupyterlab/codemirror-extension:services',
- provides: IEditorServices,
- activate: (): IEditorServices => editorServices
- };
- /**
- * The editor commands.
- */
- const commands: JupyterLabPlugin<void> = {
- id: '@jupyterlab/codemirror-extension:commands',
- requires: [
- IEditorTracker,
- IMainMenu,
- ICommandPalette,
- IStateDB,
- ISettingRegistry
- ],
- activate: activateEditorCommands,
- autoStart: true
- };
- /**
- * Export the plugins as default.
- */
- const plugins: JupyterLabPlugin<any>[] = [commands, services];
- export default plugins;
- /**
- * The plugin ID used as the key in the setting registry.
- */
- const id = commands.id;
- /**
- * Set up the editor widget menu and commands.
- */
- function activateEditorCommands(app: JupyterLab, tracker: IEditorTracker, mainMenu: IMainMenu, palette: ICommandPalette, state: IStateDB, settingRegistry: ISettingRegistry): void {
- const { commands, restored } = app;
- let { theme, keyMap } = CodeMirrorEditor.defaultConfig;
- /**
- * Update the setting values.
- */
- function updateSettings(settings: ISettingRegistry.ISettings): void {
- keyMap = settings.get('keyMap').composite as string | null || keyMap;
- theme = settings.get('theme').composite as string | null || theme;
- }
- /**
- * Update the settings of the current tracker instances.
- */
- function updateTracker(): void {
- tracker.forEach(widget => {
- if (widget.editor instanceof CodeMirrorEditor) {
- let cm = widget.editor.editor;
- cm.setOption('keyMap', keyMap);
- cm.setOption('theme', theme);
- }
- });
- }
- // Fetch the initial state of the settings.
- Promise.all([settingRegistry.load(id), restored]).then(([settings]) => {
- updateSettings(settings);
- updateTracker();
- settings.changed.connect(() => {
- updateSettings(settings);
- updateTracker();
- });
- }).catch((reason: Error) => {
- console.error(reason.message);
- updateTracker();
- });
- /**
- * 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);
- }
- });
- // Update the command registry when the codemirror state changes.
- tracker.currentChanged.connect(() => {
- if (tracker.size <= 1) {
- commands.notifyCommandChanged(CommandIDs.changeKeyMap);
- }
- });
- /**
- * A test for whether the tracker has an active widget.
- */
- function hasWidget(): boolean {
- return tracker.currentWidget !== null;
- }
- /**
- * Create a menu for the editor.
- */
- function createMenu(): Menu {
- const menu = new Menu({ commands });
- const themeMenu = new Menu({ commands });
- const keyMapMenu = new Menu({ commands });
- const modeMenu = new Menu({ commands });
- const tabMenu = new Menu({ commands });
- menu.title.label = 'Editor';
- themeMenu.title.label = 'Theme';
- keyMapMenu.title.label = 'Key Map';
- modeMenu.title.label = 'Language';
- tabMenu.title.label = 'Tabs';
- commands.addCommand(CommandIDs.changeTheme, {
- label: args => args['theme'] as string,
- execute: args => {
- const key = 'theme';
- const value = theme = args['theme'] as string || theme;
- updateTracker();
- return settingRegistry.set(id, key, value).catch((reason: Error) => {
- console.error(`Failed to set ${id}:${key} - ${reason.message}`);
- });
- },
- isEnabled: hasWidget,
- isToggled: args => args['theme'] === theme
- });
- commands.addCommand(CommandIDs.changeKeyMap, {
- label: args => {
- let title = args['keyMap'] as string;
- return title === 'sublime' ? 'Sublime Text' : title;
- },
- execute: args => {
- const key = 'keyMap';
- const value = keyMap = args['keyMap'] as string || keyMap;
- updateTracker();
- return settingRegistry.set(id, key, value).catch((reason: Error) => {
- console.error(`Failed to set ${id}:${key} - ${reason.message}`);
- });
- },
- isEnabled: hasWidget,
- isToggled: args => args['keyMap'] === keyMap
- });
- commands.addCommand(CommandIDs.find, {
- label: 'Find',
- execute: () => {
- let widget = tracker.currentWidget;
- if (!widget) {
- return;
- }
- let editor = widget.editor as CodeMirrorEditor;
- editor.execCommand('find');
- },
- isEnabled: hasWidget
- });
- commands.addCommand(CommandIDs.findAndReplace, {
- label: 'Find & Replace',
- execute: () => {
- let widget = tracker.currentWidget;
- if (!widget) {
- return;
- }
- let editor = widget.editor as CodeMirrorEditor;
- editor.execCommand('replace');
- },
- isEnabled: hasWidget
- });
- commands.addCommand(CommandIDs.changeMode, {
- label: args => args['name'] as string,
- execute: args => {
- let name = args['name'] as string;
- let widget = tracker.currentWidget;
- if (name && widget) {
- let spec = Mode.findByName(name);
- if (spec) {
- widget.model.mimeType = spec.mime;
- }
- }
- },
- isEnabled: hasWidget,
- isToggled: args => {
- let widget = tracker.currentWidget;
- if (!widget) {
- return false;
- }
- let mime = widget.model.mimeType;
- let spec = Mode.findByMIME(mime);
- let name = spec && spec.name;
- return args['name'] === name;
- }
- });
- Mode.getModeInfo().sort((a, b) => {
- let aName = a.name || '';
- let bName = b.name || '';
- return aName.localeCompare(bName);
- }).forEach(spec => {
- // Avoid mode name with a curse word.
- if (spec.mode.indexOf('brainf') === 0) {
- return;
- }
- modeMenu.addItem({
- command: CommandIDs.changeMode,
- args: {...spec}
- });
- });
- [
- 'jupyter', 'default', 'abcdef', 'base16-dark', 'base16-light',
- 'hopscotch', 'material', 'mbo', 'mdn-like', 'seti', 'the-matrix',
- 'xq-light', 'zenburn'
- ].forEach(name => themeMenu.addItem({
- command: CommandIDs.changeTheme,
- args: { theme: name }
- }));
- ['default', 'sublime', 'vim', 'emacs'].forEach(name => {
- keyMapMenu.addItem({
- command: CommandIDs.changeKeyMap,
- args: { keyMap: name }
- });
- });
- let args: JSONObject = {
- insertSpaces: false, size: 4, name: 'Indent with Tab'
- };
- let command = 'fileeditor:change-tabs';
- tabMenu.addItem({ command, args });
- palette.addItem({ command, args, category: 'Editor' });
- for (let size of [1, 2, 4, 8]) {
- let args: JSONObject = {
- insertSpaces: true, size, name: `Spaces: ${size} `
- };
- tabMenu.addItem({ command, args });
- palette.addItem({ command, args, category: 'Editor' });
- }
- menu.addItem({ type: 'submenu', submenu: modeMenu });
- menu.addItem({ type: 'submenu', submenu: tabMenu });
- menu.addItem({ command: CommandIDs.find });
- menu.addItem({ command: CommandIDs.findAndReplace });
- menu.addItem({ type: 'separator' });
- menu.addItem({ command: 'fileeditor:toggle-line-numbers' });
- menu.addItem({ command: 'fileeditor:toggle-line-wrap' });
- menu.addItem({ command: 'fileeditor:toggle-match-brackets' });
- menu.addItem({ command: 'fileeditor:toggle-autoclosing-brackets' });
- menu.addItem({ type: 'submenu', submenu: keyMapMenu });
- menu.addItem({ type: 'submenu', submenu: themeMenu });
- return menu;
- }
- mainMenu.addMenu(createMenu(), { rank: 30 });
- [
- 'editor:line-numbers',
- 'editor:line-wrap',
- 'editor:match-brackets',
- 'editor-autoclosing-brackets',
- 'editor:create-console',
- 'editor:run-code'
- ].forEach(command => palette.addItem({ command, category: 'Editor' }));
- }
|