Пользовательский ярлык клавиатуры для клавиатуры IPython для дублирования текущей строки в режиме редактирования

В среде IPython Notebook можно определить пользовательские сочетания клавиш, используя API-интерфейс IPython Javascript. Используя магию %%javascript, можно написать javascript в интерактивной консоли IPython следующим образом (пример описан здесь):

%%javascript

IPython.keyboard_manager.command_shortcuts.add_shortcut('r', {
    help : 'run cell',
    help_index : 'zz',
    handler : function (event) {
        IPython.notebook.execute_cell();
        return false;
    }}
);

Я хотел бы написать javascript, который создает ярлык во время режима редактирования, который связывает Ctrl-Alt-Down с действием "дублировать текущую строку" --- то есть переместите курсор в начало текущей строки, выберите строку, скопируйте строку, верните, вставьте. По сути, я хочу эмулировать сочетание клавиш Eclipse или Ctrl-d в Notepad ++ или C-C-SPACE C-n M-w C-y в Emacs. Файл javascript будет выглядеть следующим образом:

%%javascript

IPython.keyboard_manager.edit_shortcuts.add_shortcut('ctrl-alt-down', {
    help : 'run cell',
    help_index : 'zz',
    handler : function (event) {
        [Code that duplicates the line];
        return false;
    }}
);

хотя мои попытки предполагают, что "ctrl-alt-down" - это неправильный способ представления последовательности ярлыков, и я не могу найти документацию для keyboard_manager.

Я бы предпочел не пойти с (например,) решением AutoHotKey, так как хочу ограничить этот ярлык режимом редактирования IPython Notebook.

Ответ 1

Шаг 1.

Создайте новый файл JS в ~/.jupyter/custom/custom.js если он не существует, и добавьте следующий код:

/**
*
* Duplicate a current line in the Jupyter Notebook
* Used only CodeMirror API - https://codemirror.net
*
**/
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);
};

Шаг 2.

Перезагрузите Jupyter

Результат

enter image description here

Протестировано в следующей среде

[email protected] ~ $ google-chrome --version
Google Chrome 53.0.2785.116 
[email protected] ~ $ jupyter --version
4.1.0
[email protected] ~ $ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Ответ 2

Это простая настройка для Seti Volkylany отличный ответ, выполнив dasWesen запрос, чтобы избежать удвоения вкладок. Эта версия использует функцию CodeMirror goLineStartSmart для перехода к началу текущей строки текста, чтобы при копировании текста он не захватывал начальные пробелы или табуляции.

Как упоминалось в сообщении Сети, поместите код в файл ~/.jupyter/custom/custom.js

В Windows я нашел папку .jupyter в C:\Users\YourUserName, а затем C:\Users\YourUserName пришлось создать папку \custom и файл custom.js. Перезапуск Jupyter подобрал изменения.

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

    // get current cursor position
    var current_cursor = cm.doc.getCursor();

    // First go to end of line, to avoid the problem where if cursor was at start
    // of indented text, goLineStartSmart would go to very beginning of line,
    // and so we'd get unwanted tabs/spaces in the getRange function.
    CodeMirror.commands.goLineEnd(cm);
    // now we can safely call goLineStartSmart
    CodeMirror.commands.goLineStartSmart(cm);
    var start_cursor = cm.doc.getCursor();
    var start = {'line': start_cursor.line, 'ch': start_cursor.ch};

    // go to the end of line
    CodeMirror.commands.goLineEnd(cm);
    var end_cursor = cm.doc.getCursor();
    var end = {'line': end_cursor.line, 'ch': end_cursor.ch};

    // get content
    var line_content = cm.doc.getRange(start, end);

    // 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);
};