Merge pull request #5296 from ivanov/more-shortcuts

unify keyboard shortcut and codemirror interaction
pull/37/head
Min RK 12 years ago
commit e64c2cb5d8

@ -237,6 +237,12 @@ IPython.keyboard = (function (IPython) {
return true;
}
ShortcutManager.prototype.handles = function (event) {
var shortcut = event_to_shortcut(event);
var data = this._shortcuts[shortcut];
return !( data === undefined )
}
return {
keycodes : keycodes,
inv_keycodes : inv_keycodes,

@ -19,6 +19,7 @@ var IPython = (function (IPython) {
"use strict";
var utils = IPython.utils;
var keycodes = IPython.keyboard.keycodes;
/**
* The Base `Cell` class from which to inherit
@ -153,6 +154,30 @@ var IPython = (function (IPython) {
});
}
};
/**
* This method gets called in CodeMirror's onKeyDown/onKeyPress
* handlers and is used to provide custom key handling.
*
* To have custom handling, subclasses should override this method, but still call it
* in order to process the Edit mode keyboard shortcuts.
*
* @method handle_codemirror_keyevent
* @param {CodeMirror} editor - The codemirror instance bound to the cell
* @param {event} event - key press event which either should or should not be handled by CodeMirror
* @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
*/
Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
var that = this;
var shortcuts = IPython.keyboard_manager.edit_shortcuts;
// if this is an edit_shortcuts shortcut, the global keyboard/shortcut
// manager will handle it
if (shortcuts.handles(event)) { return true; }
return false;
};
/**
* Triger typsetting of math by mathjax on current cell element
@ -229,6 +254,52 @@ var IPython = (function (IPython) {
}
};
/**
* Delegates keyboard shortcut handling to either IPython keyboard
* manager when in command mode, or CodeMirror when in edit mode
*
* @method handle_keyevent
* @param {CodeMirror} editor - The codemirror instance bound to the cell
* @param {event} - key event to be handled
* @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
*/
Cell.prototype.handle_keyevent = function (editor, event) {
// console.log('CM', this.mode, event.which, event.type)
if (this.mode === 'command') {
return true;
} else if (this.mode === 'edit') {
return this.handle_codemirror_keyevent(editor, event);
}
};
/**
* @method at_top
* @return {Boolean}
*/
Cell.prototype.at_top = function () {
var cm = this.code_mirror;
var cursor = cm.getCursor();
if (cursor.line === 0 && cursor.ch === 0) {
return true;
}
return false;
};
/**
* @method at_bottom
* @return {Boolean}
* */
Cell.prototype.at_bottom = function () {
var cm = this.code_mirror;
var cursor = cm.getCursor();
if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
return true;
}
return false;
};
/**
* enter the command mode for the cell
* @method command_mode

@ -171,16 +171,6 @@ var IPython = (function (IPython) {
);
};
CodeCell.prototype.handle_keyevent = function (editor, event) {
// console.log('CM', this.mode, event.which, event.type)
if (this.mode === 'command') {
return true;
} else if (this.mode === 'edit') {
return this.handle_codemirror_keyevent(editor, event);
}
};
/**
* This method gets called in CodeMirror's onKeyDown/onKeyPress
@ -204,60 +194,26 @@ var IPython = (function (IPython) {
this.auto_highlight();
}
if (event.keyCode === keycodes.enter && (event.shiftKey || event.ctrlKey || event.altKey)) {
// Always ignore shift-enter in CodeMirror as we handle it.
return true;
} else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
if (event.which === keycodes.down && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
// triger on keypress (!) otherwise inconsistent event.which depending on plateform
// browser and keyboard layout !
// Pressing '(' , request tooltip, don't forget to reappend it
// The second argument says to hide the tooltip if the docstring
// is actually empty
IPython.tooltip.pending(that, true);
} else if (event.which === keycodes.up && event.type === 'keydown') {
// If we are not at the top, let CM handle the up arrow and
// prevent the global keydown handler from handling it.
if (!that.at_top()) {
event.stop();
return false;
} else {
return true;
}
} else if (event.which === keycodes.esc && event.type === 'keydown') {
// First see if the tooltip is active and if so cancel it.
if (tooltip_closed) {
// The call to remove_and_cancel_tooltip above in L177 doesn't pass
// force=true. Because of this it won't actually close the tooltip
// if it is in sticky mode. Thus, we have to check again if it is open
// and close it with force=true.
if (!IPython.tooltip._hidden) {
IPython.tooltip.remove_and_cancel_tooltip(true);
}
// If we closed the tooltip, don't let CM or the global handlers
// handle this event.
event.stop();
return true;
}
if (that.code_mirror.options.keyMap === "vim-insert") {
// vim keyMap is active and in insert mode. In this case we leave vim
// insert mode, but remain in notebook edit mode.
// Let' CM handle this event and prevent global handling.
event.stop();
return false;
} else {
// vim keyMap is not active. Leave notebook edit mode.
// Don't let CM handle the event, defer to global handling.
return true;
}
} else if (event.which === keycodes.down && event.type === 'keydown') {
// If we are not at the bottom, let CM handle the down arrow and
// prevent the global keydown handler from handling it.
if (!that.at_bottom()) {
event.stop();
return false;
} else {
return true;
} else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
// If tooltip is active, cancel it. The call to
// remove_and_cancel_tooltip above doesn't pass, force=true.
// Because of this it won't actually close the tooltip
// if it is in sticky mode. Thus, we have to check again if it is open
// and close it with force=true.
if (!IPython.tooltip._hidden) {
IPython.tooltip.remove_and_cancel_tooltip(true);
}
// If we closed the tooltip, don't let CM or the global handlers
// handle this event.
event.stop();
return true;
} else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
if (editor.somethingSelected()){
var anchor = editor.getCursor("anchor");
@ -285,12 +241,11 @@ var IPython = (function (IPython) {
this.completer.startCompletion();
return true;
}
} else {
// keypress/keyup also trigger on TAB press, and we don't want to
// use those to disable tab completion.
return false;
}
return false;
}
// keyboard event wasn't one of those unique to code cells, let's see
// if it's one of the generic ones (i.e. check edit mode shortcuts)
return IPython.Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
};
// Kernel related calls.
@ -455,7 +410,7 @@ var IPython = (function (IPython) {
CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
var ns;
if (prompt_value == undefined) {
if (prompt_value === undefined) {
ns = " ";
} else {
ns = encodeURIComponent(prompt_value);
@ -501,26 +456,6 @@ var IPython = (function (IPython) {
};
CodeCell.prototype.at_top = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === 0 && cursor.ch === 0) {
return true;
} else {
return false;
}
};
CodeCell.prototype.at_bottom = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
return true;
} else {
return false;
}
};
CodeCell.prototype.clear_output = function (wait) {
this.output_area.clear_output(wait);
this.set_input_prompt();

@ -100,15 +100,21 @@ var IPython = (function (IPython) {
help_index : '',
handler : function (event) {
var index = IPython.notebook.get_selected_index();
if (index !== null && index !== 0) {
var cell = IPython.notebook.get_cell(index);
if (cell && cell.at_top()) {
event.preventDefault();
IPython.notebook.command_mode();
IPython.notebook.select_prev();
IPython.notebook.edit_mode();
return false;
}
var cell = IPython.notebook.get_cell(index);
if (cell && cell.at_top() && index !== 0) {
event.preventDefault();
IPython.notebook.command_mode();
IPython.notebook.select_prev();
IPython.notebook.edit_mode();
var cm = IPython.notebook.get_selected_cell().code_mirror;
cm.setCursor(cm.lastLine(), 0);
return false;
} else if (cell) {
var cm = cell.code_mirror;
var cursor = cm.getCursor();
cursor.line -= 1;
cm.setCursor(cursor);
return false;
}
}
},
@ -117,15 +123,21 @@ var IPython = (function (IPython) {
help_index : '',
handler : function (event) {
var index = IPython.notebook.get_selected_index();
if (index !== null && index !== (IPython.notebook.ncells()-1)) {
var cell = IPython.notebook.get_cell(index);
if (cell && cell.at_bottom()) {
event.preventDefault();
IPython.notebook.command_mode();
IPython.notebook.select_next();
IPython.notebook.edit_mode();
return false;
}
var cell = IPython.notebook.get_cell(index);
if (cell.at_bottom() && index !== (IPython.notebook.ncells()-1)) {
event.preventDefault();
IPython.notebook.command_mode();
IPython.notebook.select_next();
IPython.notebook.edit_mode();
var cm = IPython.notebook.get_selected_cell().code_mirror;
cm.setCursor(0, 0);
return false;
} else {
var cm = cell.code_mirror;
var cursor = cm.getCursor();
cursor.line += 1;
cm.setCursor(cursor);
return false;
}
}
},

@ -113,68 +113,6 @@ var IPython = (function (IPython) {
});
};
TextCell.prototype.handle_keyevent = function (editor, event) {
// console.log('CM', this.mode, event.which, event.type)
if (this.mode === 'command') {
return true;
} else if (this.mode === 'edit') {
return this.handle_codemirror_keyevent(editor, event);
}
};
/**
* This method gets called in CodeMirror's onKeyDown/onKeyPress
* handlers and is used to provide custom key handling.
*
* Subclass should override this method to have custom handeling
*
* @method handle_codemirror_keyevent
* @param {CodeMirror} editor - The codemirror instance bound to the cell
* @param {event} event -
* @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
*/
TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
var that = this;
if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) {
// Always ignore shift-enter in CodeMirror as we handle it.
return true;
} else if (event.which === keycodes.up && event.type === 'keydown') {
// If we are not at the top, let CM handle the up arrow and
// prevent the global keydown handler from handling it.
if (!that.at_top()) {
event.stop();
return false;
} else {
return true;
};
} else if (event.which === keycodes.down && event.type === 'keydown') {
// If we are not at the bottom, let CM handle the down arrow and
// prevent the global keydown handler from handling it.
if (!that.at_bottom()) {
event.stop();
return false;
} else {
return true;
};
} else if (event.which === keycodes.esc && event.type === 'keydown') {
if (that.code_mirror.options.keyMap === "vim-insert") {
// vim keyMap is active and in insert mode. In this case we leave vim
// insert mode, but remain in notebook edit mode.
// Let' CM handle this event and prevent global handling.
event.stop();
return false;
} else {
// vim keyMap is not active. Leave notebook edit mode.
// Don't let CM handle the event, defer to global handling.
return true;
}
}
return false;
};
// Cell level actions
TextCell.prototype.select = function () {
@ -242,39 +180,6 @@ var IPython = (function (IPython) {
this.element.find('div.text_cell_render').html(text);
};
/**
* @method at_top
* @return {Boolean}
*/
TextCell.prototype.at_top = function () {
if (this.rendered) {
return true;
} else {
var cursor = this.code_mirror.getCursor();
if (cursor.line === 0 && cursor.ch === 0) {
return true;
} else {
return false;
}
}
};
/**
* @method at_bottom
* @return {Boolean}
* */
TextCell.prototype.at_bottom = function () {
if (this.rendered) {
return true;
} else {
var cursor = this.code_mirror.getCursor();
if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
return true;
} else {
return false;
}
}
};
/**
* Create Text cell from JSON

Loading…
Cancel
Save