| # Copyright 2021 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| # pylint: skip-file |
| """Console key bindings.""" |
| import logging |
| from typing import Dict, List |
| |
| from prompt_toolkit.filters import ( |
| Condition, |
| has_focus, |
| ) |
| from prompt_toolkit.key_binding import KeyBindings |
| from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous |
| from prompt_toolkit.key_binding.key_bindings import Binding |
| |
| import pw_console.pw_ptpython_repl |
| |
| __all__ = ('create_key_bindings', ) |
| |
| _LOG = logging.getLogger(__package__) |
| |
| DEFAULT_KEY_BINDINGS: Dict[str, List[str]] = { |
| 'global.open-user-guide': ['f1'], |
| 'global.open-menu-search': ['c-p'], |
| 'global.focus-previous-widget': ['c-left'], |
| 'global.focus-next-widget': ['c-right', 's-tab'], |
| 'global.exit-no-confirmation': ['c-x c-c'], |
| 'global.exit-with-confirmation': ['c-d'], |
| 'log-pane.shift-line-to-top': ['z t'], |
| 'log-pane.shift-line-to-center': ['z z'], |
| 'log-pane.toggle-follow': ['f'], |
| 'log-pane.toggle-wrap-lines': ['w'], |
| 'log-pane.toggle-table-view': ['t'], |
| 'log-pane.duplicate-log-pane': ['insert'], |
| 'log-pane.remove-duplicated-log-pane': ['delete'], |
| 'log-pane.clear-history': ['C'], |
| 'log-pane.toggle-follow': ['f'], |
| 'log-pane.move-cursor-up': ['up', 'k'], |
| 'log-pane.move-cursor-down': ['down', 'j'], |
| 'log-pane.visual-select-up': ['s-up'], |
| 'log-pane.visual-select-down': ['s-down'], |
| 'log-pane.visual-select-all': ['N', 'c-r'], |
| 'log-pane.deselect-cancel-search': ['c-c'], |
| 'log-pane.scroll-page-up': ['pageup'], |
| 'log-pane.scroll-page-down': ['pagedown'], |
| 'log-pane.scroll-to-top': ['g'], |
| 'log-pane.scroll-to-bottom': ['G'], |
| 'log-pane.save-copy': ['c-o'], |
| 'log-pane.search': ['/', 'c-f'], |
| 'log-pane.search-next-match': ['n', 'c-s', 'c-g'], |
| 'log-pane.search-previous-match': ['N', 'c-r'], |
| 'log-pane.search-apply-filter': ['escape c-f'], |
| 'log-pane.clear-filters': ['escape c-r'], |
| 'search-toolbar.toggle-column': ['c-t'], |
| 'search-toolbar.toggle-invert': ['c-v'], |
| 'search-toolbar.toggle-matcher': ['c-n'], |
| 'search-toolbar.cancel': ['escape', 'c-c', 'c-d'], |
| 'search-toolbar.create-filter': ['escape c-f'], |
| 'window-manager.move-pane-left': ['escape c-left'], # Alt-Ctrl- |
| 'window-manager.move-pane-right': ['escape c-right'], # Alt-Ctrl- |
| # NOTE: c-up and c-down seem swapped in prompt-toolkit |
| 'window-manager.move-pane-down': ['escape c-up'], # Alt-Ctrl- |
| 'window-manager.move-pane-up': ['escape c-down'], # Alt-Ctrl- |
| 'window-manager.enlarge-pane': ['escape ='], # Alt-= (mnemonic: Alt Plus) |
| 'window-manager.shrink-pane': |
| ['escape -'], # Alt-minus (mnemonic: Alt Minus) |
| 'window-manager.shrink-split': ['escape ,'], # Alt-, (mnemonic: Alt <) |
| 'window-manager.enlarge-split': ['escape .'], # Alt-. (mnemonic: Alt >) |
| 'window-manager.focus-prev-pane': ['escape c-p'], # Ctrl-Alt-p |
| 'window-manager.focus-next-pane': ['escape c-n'], # Ctrl-Alt-n |
| 'window-manager.balance-window-panes': ['c-u'], |
| 'python-repl.copy-output-selection': ['c-c'], |
| 'python-repl.copy-all-output': ['escape c-c'], |
| 'python-repl.copy-clear-or-cancel': ['c-c'], |
| 'python-repl.paste-to-input': ['c-v'], |
| 'save-as-dialog.cancel': ['escape', 'c-c', 'c-d'], |
| 'quit-dialog.no': ['escape', 'n', 'c-c'], |
| 'quit-dialog.yes': ['y', 'c-d'], |
| 'command-runner.cancel': ['escape', 'c-c'], |
| 'command-runner.select-previous-item': ['up', 's-tab'], |
| 'command-runner.select-next-item': ['down', 'tab'], |
| 'help-window.close': ['q', 'f1', 'escape'], |
| 'help-window.copy-all': ['c-c'], |
| } |
| |
| |
| def create_key_bindings(console_app) -> KeyBindings: |
| """Create custom key bindings. |
| |
| This starts with the key bindings, defined by `prompt-toolkit`, but adds the |
| ones which are specific for the console_app. A console_app instance |
| reference is passed in so key bind functions can access it. |
| """ |
| |
| key_bindings = KeyBindings() |
| register = console_app.prefs.register_keybinding |
| |
| @register('global.open-user-guide', |
| key_bindings, |
| filter=Condition(lambda: not console_app.modal_window_is_open())) |
| def show_help(event): |
| """Toggle user guide window.""" |
| console_app.user_guide_window.toggle_display() |
| |
| # F2 is ptpython settings |
| # F3 is ptpython history |
| |
| @register('global.open-menu-search', |
| key_bindings, |
| filter=Condition(lambda: not console_app.modal_window_is_open())) |
| def show_command_runner(event): |
| """Open command runner window.""" |
| console_app.open_command_runner_main_menu() |
| |
| @register('global.focus-previous-widget', key_bindings) |
| def app_focus_previous(event): |
| """Move focus to the previous widget.""" |
| focus_previous(event) |
| |
| @register('global.focus-next-widget', key_bindings) |
| def app_focus_next(event): |
| """Move focus to the next widget.""" |
| focus_next(event) |
| |
| # Bindings for when the ReplPane input field is in focus. |
| # These are hidden from help window global keyboard shortcuts since the |
| # method names end with `_hidden`. |
| @register('python-repl.copy-clear-or-cancel', |
| key_bindings, |
| filter=has_focus(console_app.pw_ptpython_repl)) |
| def handle_ctrl_c_hidden(event): |
| """Reset the python repl on Ctrl-c""" |
| console_app.repl_pane.ctrl_c() |
| |
| @register('global.exit-no-confirmation', key_bindings) |
| def quit_no_confirm(event): |
| """Quit without confirmation.""" |
| event.app.exit() |
| |
| @register( |
| 'global.exit-with-confirmation', |
| key_bindings, |
| filter=console_app.pw_ptpython_repl.input_empty_if_in_focus_condition( |
| ) | has_focus(console_app.quit_dialog)) |
| def quit(event): |
| """Quit with confirmation dialog.""" |
| # If the python repl is in focus and has text input then Ctrl-d will |
| # delete forward characters instead. |
| console_app.quit_dialog.open_dialog() |
| |
| @register('python-repl.paste-to-input', |
| key_bindings, |
| filter=has_focus(console_app.pw_ptpython_repl)) |
| def paste_into_repl(event): |
| """Reset the python repl on Ctrl-c""" |
| console_app.repl_pane.paste_system_clipboard_to_input_buffer() |
| |
| @register('python-repl.copy-all-output', |
| key_bindings, |
| filter=console_app.repl_pane.input_or_output_has_focus()) |
| def copy_repl_output_text(event): |
| """Copy all Python output to the system clipboard.""" |
| console_app.repl_pane.copy_all_output_text() |
| |
| return key_bindings |