|
| 1 | + |
| 2 | +var cmd = IPython.keyboard_manager.command_shortcuts; |
| 3 | +var edit = IPython.keyboard_manager.edit_shortcuts; |
| 4 | +var def_cmd = IPython.default_command_shortcuts; |
| 5 | +var def_edit = IPython.default_edit_shortcuts; |
| 6 | + |
| 7 | +// get the code mirror editor of a curently selected cell |
| 8 | +function C() { return IPython.notebook.get_selected_cell().code_mirror; }; |
| 9 | + |
| 10 | +// change the mode of all current and future CodeMirror instances |
| 11 | +function to(mode) { |
| 12 | + var mode = mode || 'vim' |
| 13 | + // first let's apply vim mode to all current cells |
| 14 | + function to_mode(c) { return c.code_mirror.setOption('keyMap', mode);}; |
| 15 | + IPython.notebook.get_cells().map(to_mode); |
| 16 | + // apply the mode to future cells created |
| 17 | + IPython.Cell.options_default.cm_config.keyMap = mode; |
| 18 | +} |
| 19 | + |
| 20 | +function getCSS(path) { |
| 21 | + $('<link/>', { |
| 22 | + rel: 'stylesheet', |
| 23 | + type: 'text/css', |
| 24 | + href: path, |
| 25 | + }).appendTo('head'); |
| 26 | +} |
| 27 | + |
| 28 | + |
| 29 | +var p = "/static/components/codemirror/addon/"; |
| 30 | +// Grab the CodeMirror vim keymap |
| 31 | +$.getScript("/static/components/codemirror/keymap/vim.js"); |
| 32 | + |
| 33 | +// also make search work |
| 34 | +$.getScript(p + "search/search.js"); |
| 35 | +$.getScript(p + "search/searchcursor.js"); |
| 36 | +// TODO: hook-up gq to perform a harwrap |
| 37 | +$.getScript(p + "wrap/hardwrap.js"); |
| 38 | +$.getScript(p + "selection/active-line.js"); |
| 39 | +//$.getScript("/static/components/codemirror/addon/ |
| 40 | +// WARNING |
| 41 | + |
| 42 | +$.getScript(p + "display/fullscreen.js"); |
| 43 | +getCSS(p + "display/fullscreen.css"); |
| 44 | + |
| 45 | +getCSS(p + "dialog/dialog.css"); |
| 46 | +$.getScript("static/components/codemirror/addon/dialog/dialog.js"); |
| 47 | + |
| 48 | +//$.getScript("/static/components/codemirror/addon/ |
| 49 | +$.getScript(p + "fold/indent-fold.js"); |
| 50 | +$.getScript(p + "fold/foldcode.js"); |
| 51 | +$.getScript(p + "fold/foldgutter.js"); |
| 52 | + |
| 53 | +IPython.CodeCell.options_default.cm_config.foldGutter = true; |
| 54 | +IPython.CodeCell.options_default.cm_config.gutters = ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]; |
| 55 | +getCSS(p + "fold/foldgutter.css"); |
| 56 | + |
| 57 | +var p = "/static/components/codemirror/"; |
| 58 | +//themes |
| 59 | +getCSS(p + "theme/tomorrow-night-eighties.css"); |
| 60 | +getCSS(p + "theme/twilight.css"); |
| 61 | +getCSS(p + "theme/vibrant-ink.css"); |
| 62 | + |
| 63 | +// on all code mirror instances on this page, apply the function f |
| 64 | +function all_cm(f) { |
| 65 | + // apply f to every code mirror instance. f takes one parameter |
| 66 | + IPython.notebook.get_cells().map(function (c) { f(c.code_mirror); } ); |
| 67 | +} |
| 68 | + |
| 69 | + |
| 70 | +to('vim'); |
| 71 | +function vim_up(event) { |
| 72 | + var cell = IPython.notebook.get_selected_cell(); |
| 73 | + console.log('IPython "k" handler invoked') |
| 74 | + if (cell && cell.at_top() && cell.code_mirror.options.keyMap === 'vim') { |
| 75 | + console.log('inside the business logic k'); |
| 76 | + event.preventDefault(); |
| 77 | + IPython.notebook.command_mode() |
| 78 | + IPython.notebook.select_prev(); |
| 79 | + IPython.notebook.edit_mode(); |
| 80 | + return false; |
| 81 | + }; |
| 82 | +} |
| 83 | + |
| 84 | +function vim_down(event) { |
| 85 | + var cell = IPython.notebook.get_selected_cell(); |
| 86 | + if (cell && cell.at_bottom() && cell.code_mirror.options.keyMap === 'vim') { |
| 87 | + event.preventDefault(); |
| 88 | + IPython.notebook.command_mode() |
| 89 | + IPython.notebook.select_next(); |
| 90 | + IPython.notebook.edit_mode(); |
| 91 | + return false; |
| 92 | + }; |
| 93 | + } |
| 94 | + |
| 95 | +var m = '(vim) ' |
| 96 | +var shortcuts = { |
| 97 | + 'k' : { |
| 98 | + help : m + 'up a line, even across cells', |
| 99 | + help_index : 'AA', |
| 100 | + handler : vim_up |
| 101 | + }, |
| 102 | + 'j' : { |
| 103 | + help : m + 'down a line, even across cells', |
| 104 | + help_index : 'AA', |
| 105 | + handler : vim_down |
| 106 | + }, |
| 107 | +}; |
| 108 | +edit.add_shortcuts(shortcuts); |
| 109 | +//edit.add_shortcuts('k', def_edit['up'].handler); |
| 110 | +//edit.add_shortcut('j', def_edit['down'].handler); |
| 111 | + |
| 112 | +// N.B. This code looks fairly simple, but it took me forever to |
| 113 | +// figure out how to do this, |
| 114 | +// |
| 115 | +// there's a problem here, Ctrl-[ is already handled by CodeMirror by the time we |
| 116 | +// (IPython.keyboard_manager) get it CodeMirror issues signals on mode change, |
| 117 | +// so we have to hook into that to get Ctrl-[ |
| 118 | +edit.remove_shortcut('Ctrl+['); |
| 119 | + |
| 120 | +CodeMirror.commands.leaveInsertOrEdit = function (cm) { |
| 121 | + if ( cm.state.vim.insertMode ) { |
| 122 | + // do magic here to get out of insert mode |
| 123 | + CodeMirror.keyMap['vim-insert']['Esc'](cm); |
| 124 | + } else { |
| 125 | + IPython.notebook.command_mode(); |
| 126 | + IPython.notebook.focus_cell(); |
| 127 | + } |
| 128 | +}; |
| 129 | + |
| 130 | +//C().options.extraKeys['Ctrl-['] = 'leaveInsertOrEdit'; |
| 131 | +all_cm( function (cm) { |
| 132 | + cm.options.extraKeys['Ctrl-['] = 'leaveInsertOrEdit'; |
| 133 | + if ( CodeMirror.defaults.extraKeys === null ) { |
| 134 | + CodeMirror.defaults.extraKeys = {}; |
| 135 | + } |
| 136 | + // TODO: make this change permanent |
| 137 | + // this part seems to be ignore when adding a new cell |
| 138 | + // - alternative solution would be to listen for NewCell events and rerun the CM function on it |
| 139 | + // - it could also be the case that when we instatiate CodeMirror, we somehow leave out CM.defaults.extraKeys |
| 140 | + IPython.CodeCell.options_default.cm_config.extraKeys['Ctrl-['] = 'leaveInsertOrEdit'; |
| 141 | + IPython.TextCell.options_default.cm_config.extraKeys['Ctrl-['] = 'leaveInsertOrEdit'; |
| 142 | +}) |
| 143 | + |
| 144 | +// On blur, make sure we go back to command mode for CodeMirror (in case user clicked away) |
| 145 | +// TODO: Make this permanent - how to get CodeMirror to do this for new cells created after |
| 146 | +all_cm( function (cm) { |
| 147 | + cm.on('blur', function(cm) { |
| 148 | + // TODO: I wish I understood a better way to do this, but fake pressing Escape work |
| 149 | + CodeMirror.keyMap['vim-insert']['Esc'](cm); |
| 150 | + CodeMirror.keyMap['vim']['Esc'](cm); |
| 151 | + cm.setOption('styleActiveLine', false); |
| 152 | + }); |
| 153 | + cm.on('focus', function(cm) { |
| 154 | + cm.setOption('styleActiveLine', true); |
| 155 | + }); |
| 156 | +}); |
| 157 | + |
| 158 | +// 'i' by default interrupts the kernel (what Ctrl-C does at the terminal) |
| 159 | +cmd.remove_shortcut('i'); |
| 160 | +cmd.add_shortcut('i', def_cmd.enter); |
| 161 | + |
| 162 | +// not quite what we want - 'i' requires a double-tap |
| 163 | +// add documentation for this. |
| 164 | +cmd.add_shortcut('ctrl+c', function(e) { IPython.notebook.kernel.interrupt(); return false}); |
| 165 | + |
| 166 | + |
| 167 | +function focus_last(e) { |
| 168 | + var cells = IPython.notebook.get_cells(); |
| 169 | + cells[cells.length-1].focus_cell(); |
| 170 | +}; |
| 171 | + |
| 172 | +function focus_first(e) { |
| 173 | + console.log('focus first called'); |
| 174 | + var cells = IPython.notebook.get_cells(); |
| 175 | + cells[0].focus_cell(); |
| 176 | +}; |
| 177 | + |
| 178 | +function combo_tap(combo, action) { |
| 179 | + var that = this; |
| 180 | + function f() { |
| 181 | + console.log('f called once'); |
| 182 | + |
| 183 | + // redo this so that when an action is performed, we restore the original combo |
| 184 | + cmd.add_shortcut(combo[1], action); |
| 185 | + setTimeout(function () { |
| 186 | + console.log('resetting f'); |
| 187 | + reset(); |
| 188 | + //cmd.add_shortcut(combo[0], reset) |
| 189 | + }, 800); |
| 190 | + } |
| 191 | + function reset(e) { |
| 192 | + console.log('reset called'); |
| 193 | + //that(combo, action); |
| 194 | + cmd.add_shortcut(combo[0], f); |
| 195 | + } |
| 196 | + |
| 197 | + reset(); |
| 198 | +}; |
| 199 | +cmd.add_shortcut('shift+g', focus_last); |
| 200 | +combo_tap('gg', focus_first); |
| 201 | + |
| 202 | +// cut |
| 203 | +combo_tap('dd', def_cmd['x']); |
| 204 | + |
| 205 | +// copy |
| 206 | +combo_tap('yy', def_cmd['c']); |
| 207 | + |
| 208 | +// paste |
| 209 | +cmd.add_shortcut('p', def_cmd['v']); |
| 210 | + |
| 211 | +// undo |
| 212 | +cmd.add_shortcut('u', def_cmd['z']); |
| 213 | + |
| 214 | +// Join (merge down with cell below) |
| 215 | +cmd.add_shortcut('shift+j', def_cmd['shift+m']) |
| 216 | + |
| 217 | +//edit.add_shortcut('k', def_edit['up'].handler); |
| 218 | +//[edit.add_shortcut('j', def_edit['down'].handler); |
| 219 | +edit.remove_shortcut('ctrl+['); |
| 220 | + |
| 221 | +CodeMirror.prototype.save = function() { |
| 222 | + IPython.notebook.save_checkpoint() |
| 223 | +} |
| 224 | + |
| 225 | +function focus_last(e) { |
| 226 | + var cells = IPython.notebook.get_cells(); |
| 227 | + cells[cells.length-1].focus_cell(); |
| 228 | +}; |
| 229 | + |
| 230 | +function focus_first(e) { |
| 231 | + console.log('focus first called'); |
| 232 | + var cells = IPython.notebook.get_cells(); |
| 233 | + cells[0].focus_cell(); |
| 234 | +}; |
| 235 | + |
| 236 | +function combo_tap(combo, action) { |
| 237 | + var that = this; |
| 238 | + function f() { |
| 239 | + console.log('f called once'); |
| 240 | + setTimeout(function () { |
| 241 | + console.log('resetting f'); |
| 242 | + // this wasn't a double tap, so maybe do the default action for the single key, if there was one |
| 243 | + def_cmd[combo[0]].handler(); |
| 244 | + reset(); |
| 245 | + //cmd.add_shortcut(combo[0], reset) |
| 246 | + }, 800); |
| 247 | + cmd.add_shortcut(combo[1], action); |
| 248 | + } |
| 249 | + function reset(e) { |
| 250 | + console.log('reset called'); |
| 251 | + //that(combo, action); |
| 252 | + cmd.add_shortcut(combo[0], f); |
| 253 | + } |
| 254 | + |
| 255 | + reset(); |
| 256 | +}; |
| 257 | + |
| 258 | +cmd.add_shortcut('shift+g', focus_last); |
| 259 | +combo_tap('gg', focus_first); |
| 260 | + |
| 261 | +// get rid of the default Ctrl-W binding |
| 262 | +// this only works for Firefox |
| 263 | +$(document).ready(function() { |
| 264 | + $(this).bind('keypress', function(e) { |
| 265 | + var key = (e.keyCode ? e.keyCode : e.charCode); |
| 266 | + if (key == '119' && e.ctrlKey) { |
| 267 | + return false; |
| 268 | + } |
| 269 | + }); |
| 270 | +}); |
| 271 | + |
| 272 | +window.addEventListener("beforeunload", function( event ) { |
| 273 | + var press = jQuery.Event("keypress"); |
| 274 | + press.ctrlKey = false; |
| 275 | + press.which = 27; // escape |
| 276 | + $(document).trigger(press); |
| 277 | + event.returnValue = "\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; |
| 278 | + event.returnValue +="\nX Chrome sucks at captruring Ctrl-W, sorry X"; |
| 279 | + event.returnValue += "\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; |
| 280 | +}); |
| 281 | + |
| 282 | +// update the keyboard shortcuts |
| 283 | +IPython.quick_help = new IPython.QuickHelp(); |
| 284 | + |
| 285 | +//IPython.CodeCell.options_default.cm_config.styleActiveLine = true; |
| 286 | + |
| 287 | +all_cm( function (cm) { |
| 288 | + cm.setOption('foldGutter', true); |
| 289 | + cm.setOption('gutters', ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]); |
| 290 | + cm.options.extraKeys["Ctrl-F"] = function(cm){ cm.foldCode(cm.getCursor()); }, |
| 291 | + cm.options.extraKeys["Ctrl-F"] = function(cm) { cm.setOption("fullScreen", !cm.getOption("fullScreen"))}; |
| 292 | + IPython.CodeCell.options_default.cm_config.extraKeys['Ctrl-F'] = function(cm){ cm.foldCode(cm.getCursor()); }; |
| 293 | + IPython.TextCell.options_default.cm_config.extraKeys['Ctrl-F'] = function(cm){ cm.foldCode(cm.getCursor()); }; |
| 294 | + |
| 295 | + // todo - do this for new cells as well |
| 296 | + cm.setOption('theme', "tomorrow-night-eighties"); |
| 297 | + // support this a :only? turn off full screen on blur |
| 298 | + cm.options.extraKeys["F11"] = function(cm) { cm.setOption("fullScreen", !cm.getOption("fullScreen"))}; |
| 299 | + cm.options.extraKeys["Esc"] = function(cm) { |
| 300 | + if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); |
| 301 | + }; |
| 302 | + cm.options.extraKeys["Ctrl-A"] = function(cm) { |
| 303 | + if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); |
| 304 | + }; |
| 305 | + //all_cm( function (cm) { |
| 306 | + cm.on('blur',function(cm) { |
| 307 | + if (cm.getOption("fullScreen")) { |
| 308 | + cm.setOption('fullScreen', false); |
| 309 | + // fullScreen the newly selected code mirror (doesn't work) |
| 310 | + //setTimeout(100, function() { |
| 311 | + // console.log(IPython.notebook.get_selected_cell().code_mirror); |
| 312 | + // IPython.notebook.get_selected_cell().code_mirror.setOption('fullScreen', true); |
| 313 | + //}); |
| 314 | + } |
| 315 | + }); |
| 316 | +}); |
| 317 | + |
| 318 | +setTimeout(200, function() {IPython.notebook.get_selected_cell().set_input_prompt('vim');}) |
| 319 | + |
| 320 | +$("#ipython_notebook").append('<img src="http://www.vim.org/images/vim_on_fire.gif" style="' |
| 321 | + + 'position: absolute; left: 51px; top: -10px; height: initial;">'); |
| 322 | + |
| 323 | + |
0 commit comments