Robin's Blog

How to: Get Sublime Text style editing in the IPython/Jupyter notebook

So, I really like the Jupyter notebook (formerly known as the IPython notebook), but I often find myself missing the ‘fancy’ features that ‘proper’ editors have. I particularly miss the amazing multiple cursor functionality of editors like Sublime Text and Atom.

I’ve known for a while that you can edit a cell in your default $EDITOR by running %%edit at the top of the cell – but I’ve recently found out that you can configure Jupyter to use Sublime Text-style keyboard shortcuts when editing cells in the notebook – all thanks to CodeMirror, the javascript-based text editor component that the Jupyter notebook uses. Brilliantly, this also brings with it the multiple-cursor functionality! So, you can get something like this:


So, how do you do this? It’s really simple.

  1. Find your Jupyter configuration folder by running jupyter --config-dir
  2. Open the custom.js file in the custom sub-folder in your favourite editor
  3. Add the following lines to the bottom of the file
require(["codemirror/keymap/sublime", "notebook/js/cell", "base/js/namespace"],
    function(sublime_keymap, cell, IPython) {
        // setTimeout(function(){ // uncomment line to fake race-condition
        cell.Cell.options_default.cm_config.keyMap = 'sublime';
        var cells = IPython.notebook.get_cells();
        for(var cl=0; cl< cells.length ; cl++){
            cells[cl].code_mirror.setOption('keyMap', 'sublime');

        // }, 1000)// uncomment  line to fake race condition 

That should be it – if you start a notebook now all of the Sublime Text shortcuts should be working!

If you found this post useful, please consider buying me a coffee.
This post originally appeared on Robin's Blog.

Categorised as: Programming, Python


  1. Daniel Harding says:

    In the for loop, I believe

    cells.code_mirror.setOption(‘keyMap’, ‘sublime’);

    should be

    cells[c].code_mirror.setOption(‘keyMap’, ‘sublime’);

  2. Robin Wilson says:

    Weirdly, the [c] was in the code, but wasn’t rendering properly. I’ve changed the variable name to cl and it works fine. Weird – but thanks for pointing it out!

  3. Martin Jones says:

    I’ve tried this and it works fine, with one small hitch: ctrl+enter, which executes the cell in Jupyter, is also a keyboard shortcut for “insert a new line after the current one” in the sublime keymap, so now every time I run a cell, I get a new blank line. Have you encountered this problem?

  4. Robin Wilson says:

    Ahh, good point! I hadn’t come across that myself, as I don’t use Ctrl-Enter to execute the cell (I’m on a Mac so use Cmd-Enter) – although I believe Ctrl-Enter does do something on Macs too. I can see that would be very frustrating though!

    All I can suggest is getting in touch with the IPython people and asking if there is a way to make the IPython shortcut take precedence – or alternatively to ‘turn off’ the Ctrl-Enter Sublime shortcut. If you do find out how to do it then please let me know!

  5. Dave Sergeant says:

    I’d love to get this going. I’m on Jupyter 4.1 (was on 4.0.4) but my .jupyter folder is basically empty. I have a file “migrated” that looks to have a timestamp of when I first installed jupyter.

    Searches for custom.js suggest this is an old ipython remnant that exists in some other form now in jupyter.

    Am I barking up the wrong tree?

  6. Robin Wilson says:

    I think this may be a IPython to Jupyter migration problem. First, I’d try running jupyter migrate in a terminal and see if that creates some more files there. If it doesn’t, then I’d have a look at this migration guide. Hope that helps!

  7. Dave Sergeant says:

    [JupyterMigrate] Found nothing to migrate.

    Curious. This is a relatively new machine to me. I never did have iPython notebook on it. I just installed Jupyter from the getgo. Any possibility it could it be related to that? Maybe custom.js was a part of ipython?

    I can’t find any current reference to it in read the docs.

    I’m not using Conda… any chance it’s tied to that?

  8. Robin Wilson says:

    It definitely shouldn’t be related to conda. It seems like it is still in use, but a blank file may not be created automatically nowadays. Try using the example code at to check where your Jupyter config directory is, and create a custom.js file there code in it, and see if it works. (If not, then you can try the ‘Exercise’ listed on that page – and you’ll definitely be able to find out if it works!)

  9. Dave Sergeant says:

    Ay carumba! Thanks.

    First of all, my fault on all of it. I can read, but apparently I can’t READ!

    jupyter –config-dir shows me to the .jupyter folder. You clearly then say IN THE `custom` FOLDER, which I didn’t have, but glossed over. So every attempt I made at making a custom.js in that root folder didn’t take.

    The little snippets in read the docs helped clearly bash me over the head that I didn’t have a custom.js and showed that it was looking in my nonexistent custom folder. Making one and placing it there and it all works swell now. Thanks for pointing me there.

    Now… why can I not search for custom.js in read the docs? eh? It doesn’t like periods search and I can’t seem to escape them. That’s why I couldn’t find any of that when I searched for custom.js. And searches for custom or js only did whole word searches. Sheesh.

    Thanks so much for your help and patience!

  10. Robin Wilson says:

    Really glad to hear you got it working 🙂

    The issue with searching the docs might be worth raising as an issue (either on Github or on the mailing list), as I imagine others may run into the same problem.

  11. Alan C says:

    This only partly works for me. I can do column select, but other key strokes don’t work as expected. CMD-D deletes the selected lines for example.

  12. Adam Andersen says:

    Like Alan, this doesn’t seem to work. Cmd-D still deltes the line. I did the verification-step and got the alert, but no Sublime multi-select goodness.

  13. Robin Wilson says:

    Hmm, that’s weird. I’m not sure what’s going wrong.

    I’ll have a play on another machine and see if I can replicate the error: I wonder if it is a version-related issue or something.

  14. Ciprian Tomoiagă says:

    @Adam, I had to restart the server in order to get it working

  15. Sven says:

    Thanks! I’ve made an addition to your script that will leave the Ctrl-Enter & Shift-Enter keys unchanged (as these are used by jupyter to execute cells). This prevents both the jupyter AND the sublime actions happening:

    console.log(‘loaded sublime custom.js’);
    require([“codemirror/keymap/sublime”, “notebook/js/cell”, “base/js/namespace”],
    function(sublime_keymap, cell, IPython) {
    cell.Cell.options_default.cm_config.keyMap = ‘sublime’;
    cell.Cell.options_default.cm_config.extraKeys[‘Cmd-Enter’] = function(){console.log(‘cmd-enter’)};
    cell.Cell.options_default.cm_config.extraKeys[‘Ctrl-Enter’] = function(){console.log(‘ctrl-enter’)};
    cell.Cell.options_default.cm_config.extraKeys[‘Shift-Enter’] = function(){};
    var cells = IPython.notebook.get_cells();
    for(var cl=0; cl< cells.length ; cl++){
    "Cmd-Enter": function(){},
    "Ctrl-Enter": function(){}
    cells[cl].code_mirror.setOption('keyMap', 'sublime');

  16. Andrea Barbon says:

    Thanks a lot!!!

  17. Jay says:

    Is there a way to get this to work in Jupyter Lab?

    Using the built-in Sublime Text option in Lab doesn’t seem to work, or at least it doesn’t capture all Sublime shortcuts (ctrl D, deletes a line, instead of selecting next instance).

  18. Robin Wilson says:

    That’s a good question Jay – I don’t know. I’ve just installed the latest version of Jupyter Lab myself, and I’ll have a play and post here if I find anything.

  19. Gee says:

    Did you get anywhere with Jupyter Lab?

  20. Robin Wilson says:

    No, sorry, I haven’t had chance.

    Maybe try the Jupyter mailing list and see if anyone can help? I’d love to hear your answer if you manage to work it out!

  21. Gee says:

    Looks like somebody has to write an extension. It already exists for vim style keymaps.

  22. Santas_little_helper says:

    To fix the ctrl-Enter problem on windows:


    require([“codemirror/keymap/sublime”, “notebook/js/cell”, “base/js/namespace”],
    function(sublime_keymap, cell, IPython) {
    cell.Cell.options_default.cm_config.keyMap = ‘sublime’;
    cell.Cell.options_default.cm_config.extraKeys[“Ctrl-Enter”] = function(cm) {}
    var cells = IPython.notebook.get_cells();
    for(var cl=0; cl< cells.length ; cl++){
    cells[cl].code_mirror.setOption('keyMap', 'sublime');
    cells[cl].code_mirror.setOption("extraKeys", {
    "Ctrl-Enter": function(cm) {}

  23. E Both says:

    Genius, thank you!

    The modification by Santas_little_helper works fine on Windows after replacing the typographical quotes with regular double (or simple) quotes. WordPress being overeager again.

    You also have to reload the workbook in the browser (I also restarted the server, but you may not actually have to do that).

  24. Jonathan says:

    This is great! Thanks a lot… will really speed up my workflow. Now just to get it working in JupyterLab.

  25. ethan says:

    Moving line up/down with Ctrl+Shit+Up/Down does not work.

Leave a Reply

Your email address will not be published. Required fields are marked *