-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create shortcut editor for the notebook
1) finish the step allowing the use of es6 - this include some tweak to web pack configuration to speed up recompile in watch mode (in particular cache sourcemaps). - enable eslint (error only), on obvious mistakes. - setup babel to compile to es5 as a target. 2) Make the test pass under Casper that does not always have `Function.prototype.bind` defined, which we cannot patch only in the tests. 3) Write an actual shortcut editor that list and allow to modify most of the command mode shortcut. The logic to persist the shortcuts is a bit tricky as there are default keyboard shortcuts, and so when you "unbind" them you need to re-unbind them at next startup. This does not work for a few shortcut for technical reasons: `<Esc>`, `<Shift>`, as well as `<Ctrl-Shift-P>` and `<F>` which register asynchronously, so are not detected as "default" shortcuts.
- Loading branch information
Showing
15 changed files
with
324 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"presets": ["es2015"], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
*.min.js | ||
*components* | ||
*node_modules* | ||
*built* | ||
*build* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"parserOptions": { | ||
"ecmaVersion": 6, | ||
"sourceType": "module" | ||
}, | ||
"rules": { | ||
"semi": 1, | ||
"no-cond-assign": 2, | ||
"no-debugger": 2, | ||
"comma-dangle": 1, | ||
"no-unreachable" : 2 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
// Copyright (c) Jupyter Development Team. | ||
// Distributed under the terms of the Modified BSD License. | ||
|
||
|
||
import QH from "notebook/js/quickhelp"; | ||
import dialog from "base/js/dialog"; | ||
import {render} from "preact"; | ||
import {createElement, createClass} from "preact-compat"; | ||
import marked from 'components/marked/lib/marked'; | ||
|
||
/** | ||
* Humanize the action name to be consumed by user. | ||
* internally the actions name are of the form | ||
* <namespace>:<description-with-dashes> | ||
* we drop <namespace> and replace dashes for space. | ||
*/ | ||
const humanize_action_id = function(str) { | ||
return str.split(':')[1].replace(/-/g, ' ').replace(/_/g, '-'); | ||
}; | ||
|
||
/** | ||
* given an action id return 'command-shortcut', 'edit-shortcut' or 'no-shortcut' | ||
* for the action. This allows us to tag UI in order to visually distinguish | ||
* Wether an action have a keybinding or not. | ||
**/ | ||
|
||
const KeyBinding = createClass({ | ||
displayName: 'KeyBindings', | ||
getInitialState: function() { | ||
return {shrt:''}; | ||
}, | ||
handleShrtChange: function (element){ | ||
this.setState({shrt:element.target.value}); | ||
}, | ||
render: function(){ | ||
const that = this; | ||
const available = this.props.available(this.state.shrt); | ||
const empty = (this.state.shrt === ''); | ||
return createElement('div', {className:'jupyter-keybindings'}, | ||
createElement('i', {className: "pull-right fa fa-plus", alt: 'add-keyboard-shortcut', | ||
onClick:()=>{ | ||
available?that.props.onAddBindings(that.state.shrt, that.props.ckey):null; | ||
that.state.shrt=''; | ||
} | ||
}), | ||
createElement('input', { | ||
type:'text', | ||
placeholder:'add shortcut', | ||
className:'pull-right'+((available||empty)?'':' alert alert-danger'), | ||
value:this.state.shrt, | ||
onChange:this.handleShrtChange | ||
}), | ||
this.props.shortcuts?this.props.shortcuts.map((item, index) => { | ||
return createElement('span', {className: 'pull-right'}, | ||
createElement('kbd', {}, [ | ||
item.h, | ||
createElement('i', {className: "fa fa-times", alt: 'remove '+item.h, | ||
onClick:()=>{ | ||
that.props.unbind(item.raw); | ||
} | ||
}) | ||
]) | ||
); | ||
}):null, | ||
createElement('div', {title: '(' +this.props.ckey + ')' , className:'jupyter-keybindings-text'}, this.props.display ) | ||
); | ||
} | ||
}); | ||
|
||
const KeyBindingList = createClass({ | ||
displayName: 'KeyBindingList', | ||
getInitialState: function(){ | ||
return {data:[]}; | ||
}, | ||
componentDidMount: function(){ | ||
this.setState({data:this.props.callback()}); | ||
}, | ||
render: function() { | ||
const childrens = this.state.data.map((binding)=>{ | ||
return createElement(KeyBinding, Object.assign({}, binding, {onAddBindings:(shortcut, action)=>{ | ||
this.props.bind(shortcut, action); | ||
this.setState({data:this.props.callback()}); | ||
}, | ||
available:this.props.available, | ||
unbind: (shortcut) => { | ||
this.props.unbind(shortcut); | ||
this.setState({data:this.props.callback()}); | ||
} | ||
})); | ||
}); | ||
childrens.unshift(createElement('div', {className:'well', key:'disclamer', dangerouslySetInnerHTML: | ||
{__html: | ||
marked( | ||
|
||
"This dialog allows you to modify the keymap of the command mode, and persist the changes. "+ | ||
"You can define many type of shorctuts and sequences of keys. "+ | ||
"\n\n"+ | ||
" - Use dashes `-` to represent keys that should be pressed with modifiers, "+ | ||
"for example `Shift-a`, or `Ctrl-;`. \n"+ | ||
" - Separate by commas if the keys need to be pressed in sequence: `h,a,l,t`.\n"+ | ||
"\n\nYou can combine the two: `Ctrl-x,Meta-c,Meta-b,u,t,t,e,r,f,l,y`.\n"+ | ||
"Casing will have no effects: (e.g: `;` and `:` are the same on english keyboards)."+ | ||
" You need to explicitelty write the `Shift` modifier.\n"+ | ||
"Valid modifiers are `Cmd`, `Ctrl`, `Alt` ,`Meta`, `Cmdtrl`. Refer to developper docs "+ | ||
"for their signification depending on the platform." | ||
)} | ||
})); | ||
return createElement('div',{}, childrens); | ||
} | ||
}); | ||
|
||
const get_shortcuts_data = function(notebook) { | ||
const actions = Object.keys(notebook.keyboard_manager.actions._actions); | ||
const src = []; | ||
|
||
for (let i = 0; i < actions.length; i++) { | ||
const action_id = actions[i]; | ||
const action = notebook.keyboard_manager.actions.get(action_id); | ||
|
||
let shortcuts = notebook.keyboard_manager.command_shortcuts.get_action_shortcuts(action_id); | ||
let hshortcuts; | ||
if (shortcuts.length > 0) { | ||
hshortcuts = shortcuts.map((raw)=>{ | ||
return {h:QH._humanize_sequence(raw),raw:raw};} | ||
); | ||
} | ||
src.push({ | ||
display: humanize_action_id(action_id), | ||
shortcuts: hshortcuts, | ||
key:action_id, // react specific thing | ||
ckey: action_id | ||
}); | ||
} | ||
return src; | ||
}; | ||
|
||
|
||
export const ShortcutEditor = function(notebook) { | ||
|
||
if(!notebook){ | ||
throw new Error("CommandPalette takes a notebook non-null mandatory arguement"); | ||
} | ||
|
||
const body = $('<div>'); | ||
const mod = dialog.modal({ | ||
notebook: notebook, | ||
keyboard_manager: notebook.keyboard_manager, | ||
title : "Edit Command mode Shortcuts", | ||
body : body, | ||
buttons : { | ||
OK : {} | ||
} | ||
}); | ||
|
||
const src = get_shortcuts_data(notebook); | ||
|
||
mod.addClass("modal_stretch"); | ||
|
||
mod.modal('show'); | ||
render( | ||
createElement(KeyBindingList, { | ||
callback:()=>{ return get_shortcuts_data(notebook);}, | ||
bind: (shortcut, command) => { | ||
return notebook.keyboard_manager.command_shortcuts._persist_shortcut(shortcut, command); | ||
}, | ||
unbind: (shortcut) => { | ||
return notebook.keyboard_manager.command_shortcuts._persist_remove_shortcut(shortcut); | ||
}, | ||
available: (shrt) => { return notebook.keyboard_manager.command_shortcuts.is_available_shortcut(shrt);} | ||
}), | ||
body.get(0) | ||
); | ||
}; |
Oops, something went wrong.