blob: c44d8663c66329a3a213c8fe665126863dd1dc27 [file] [log] [blame]
# 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