Notebook: Add shortcut to duplicate line

Created on 6 Oct 2016  路  11Comments  路  Source: jupyter/notebook

Would be nice to have an IDE-like ctrl-D shortcut to duplicate a line.
Otherwise, best tool ever ! Thanks to all of you.

Most helpful comment

All 11 comments

It looks like the notebook does not currently provide a "duplicate line" or "duplicate cell" feature. However, if it did then you could define a custom keyboard shortcut by following these instructions. FYI, notebook uses Codemirror for the text editor, so any features that it supports are also supported in notebook.

Nice work, @setivolkylany! I have copied and pasted your code here for future reference. Add the following to your custom.js:

CodeMirror.keyMap.pcDefault["Ctrl-Down"] = function(cm){

    // get a position of a current cursor in a current cell
    var current_cursor = cm.doc.getCursor();

    // read a content from a line where is the current cursor
    var line_content = cm.doc.getLine(current_cursor.line);

    // go to the end the current line
    CodeMirror.commands.goLineEnd(cm);

    // make a break for a new line
    CodeMirror.commands.newlineAndIndent(cm);

    // filled a content of the new line content from line above it
    cm.doc.replaceSelection(line_content);

    // restore position cursor on the new line
    cm.doc.setCursor(current_cursor.line + 1, current_cursor.ch);
};

Thanks a lot. Excuse my ignorance, but how do you load CodeMirror from custom.js ?

  • I copied the content of the CodeMirror src folder inside the .jupyter/custom folder
  • and added on top of the custom.js script :
$.getScript("codemirror.js", function(){
});

but not to avail. Any feedback would be greatly appreciated.
I suppose my request wont be implemented in a future version of Jupyter ?

@clementlefevre You want to use the notebook's CodeMirror module vs. importing your own (which is conveniently available as a global CodeMirror). However, setting CodeMirror.keyMap.pcDefault["Ctrl-Down"] in custom.js will affect the global CodeMirror but none of the instances used by the Cell objects in the notebook (because custom.js is being executed after the notebook is loaded), so if you want to add this keymap to work in your cells, you will need to get all of the cells and modify their code_mirror instances. An example custom.js:

// Get instances of `Cell` in the notebook
var cells = $('#notebook-container').find('.cell').map(function() { 
  return $(this).data('cell')
});
// For each instance of `Cell`, add a keymap to `Cell.code_mirror`
cells.each(function(index) {
    this.code_mirror.addKeyMap({
        'Ctrl-Down': function(cm) {
            var current_cursor = cm.doc.getCursor();
            var line_content = cm.doc.getLine(current_cursor.line);
            CodeMirror.commands.goLineEnd(cm);
            CodeMirror.commands.newlineAndIndent(cm);
            cm.doc.replaceSelection(line_content);
            cm.doc.setCursor(current_cursor.line + 1, current_cursor.ch);
        }
    });
});

This should be in the core!

Another ref: https://github.com/codemirror/CodeMirror/issues/5141

I wanted to duplicate lines above or below and I also wanted to duplicate selected lines rather than just a single line. I have edited the code above to do so:

// create the extra keys in CodeMirror to call duplicate lines
"Shift-Alt-Up": function(cm) { duplicateLines(cm, true); };
"Shift-Alt-Down": function(cm) { duplicateLines(cm, false); };

// create the functions that duplicates a line or selected lines in codemirror
function duplicateLines(cm, above) {

    // get selection or cursor start and end position
    var startPosition = cm.doc.getCursor(true);
    var endPosition = cm.doc.getCursor(false);

    // gets the start cursor position without the beginning tabs
    cm.doc.setCursor(startPosition.line, startPosition.ch);
    CodeMirror.commands.goLineEnd(cm);
    CodeMirror.commands.goLineStartSmart(cm);
    var startCursor = cm.doc.getCursor();
    var start = {"line": startCursor.line, "ch": startCursor.ch};

    // gets the end cursor position
    cm.doc.setCursor(endPosition.line, endPosition.ch);
    CodeMirror.commands.goLineEnd(cm);
    var endCursor = cm.doc.getCursor();
    var end = {"line": endCursor.line, "ch": endCursor.ch};

    // gets the content of the line or selected lines
    var content = cm.doc.getRange(start, end);

    // checks if the code is duplicated above
    if (above) {

        // handles duplication on the first line
        if (startPosition.line == 0) {

            // sets the cursor to the beginning, duplicates the content, then adds a new line
            cm.doc.setCursor(0, 0);
            cm.doc.replaceSelection(content);
            CodeMirror.commands.newlineAndIndent(cm);

        // handles duplication on all other lines
        } else {

            // sets the cursor to the end of the line, adds a new line, then duplicates the content
            cm.doc.setCursor(startPosition.line - 1);
            CodeMirror.commands.goLineEnd(cm);
            CodeMirror.commands.newlineAndIndent(cm);
            cm.doc.replaceSelection(content);
        }

    // adds a new line and duplicates the content below
    } else {
        CodeMirror.commands.newlineAndIndent(cm);
        cm.doc.replaceSelection(content);
    }

    // restores the cursor position if duplicating above
    if (above) {
        cm.doc.setSelection(startPosition, endPosition);

    // restores the cursor position if duplicating one line below
    } else if (endPosition.line == startPosition.line) {
        cm.doc.setCursor(endPosition.line + 1, endPosition.ch);

    // restores the cursor position if duplicating more than one line below
    } else {
        var lineCount = endPosition.line - startPosition.line + 1;
        startPosition.line += lineCount;
        endPosition.line += lineCount;
        cm.doc.setSelection(startPosition, endPosition);
    }
}

I hope this goes into the core.

What's the status of this? Did it made it into the core?

please make this a core feature!

I have done it
http://stackoverflow.com/a/40505055/6003870

```js
CodeMirror.keyMap.pcDefault["Ctrl-Down"] = function(cm){

// get a position of a current cursor in a current cell
var current_cursor = cm.doc.getCursor();

// read a content from a line where is the current cursor & removes indentation
var line_content = cm.doc.getLine(current_cursor.line).replaceAll("\t", "").replaceAll("    ", "");

// removes tabs for proper identations in Jupyter
line_content.replaceAll("\t", "")


// go to the end the current line
CodeMirror.commands.goLineEnd(cm);

// make a break for a new line
CodeMirror.commands.newlineAndIndent(cm);

// filled a content of the new line content from line above it
cm.doc.replaceSelection(line_content);

// restore position cursor on the new line
cm.doc.setCursor(current_cursor.line + 1, current_cursor.ch);

};
```

Slightly adapted the code for proper indentatin in Jupyter notebook (removing tabs from copied line)

Was this page helpful?
0 / 5 - 0 ratings