def create_python_input_window(): def menu_position(): """ When there is no autocompletion menu to be shown, and we have a signature, set the pop-up position at `bracket_start`. """ b = python_input.default_buffer if b.complete_state is None and python_input.signatures: row, col = python_input.signatures[0].bracket_start index = b.document.translate_row_col_to_index(row - 1, col) return index return Window( BufferControl( buffer=python_input.default_buffer, search_buffer_control=search_toolbar.control, lexer=lexer, include_default_input_processors=False, input_processors=[ ConditionalProcessor( processor=HighlightIncrementalSearchProcessor(), filter=has_focus(SEARCH_BUFFER) | has_focus(search_toolbar.control), ), HighlightSelectionProcessor(), DisplayMultipleCursors(), # Show matching parentheses, but only while editing. ConditionalProcessor( processor=HighlightMatchingBracketProcessor(chars="[](){}"), filter=has_focus(DEFAULT_BUFFER) & ~is_done & Condition( lambda: python_input.highlight_matching_parenthesis ), ), ConditionalProcessor( processor=AppendAutoSuggestion(), filter=~is_done ), ] + extra_buffer_processors, menu_position=menu_position, # Make sure that we always see the result of an reverse-i-search: preview_search=True, ), left_margins=[PythonPromptMargin(python_input)], # Scroll offsets. The 1 at the bottom is important to make sure # the cursor is never below the "Press [Meta+Enter]" message # which is a float. scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4), # As long as we're editing, prefer a minimal height of 6. height=( lambda: ( None if get_app().is_done or python_input.show_exit_confirmation else input_buffer_height ) ), wrap_lines=Condition(lambda: python_input.wrap_lines), )
def __init__(self, body: AnyContainer, title: AnyFormattedText = '', buttons: Optional[Sequence[Button]] = None, modal: bool = True, width: AnyDimension = None, with_background: bool = False) -> None: self.body = body self.title = title buttons = buttons or [] # When a button is selected, handle left/right key bindings. buttons_kb = KeyBindings() if len(buttons) > 1: first_selected = has_focus(buttons[0]) last_selected = has_focus(buttons[-1]) buttons_kb.add('left', filter=~first_selected)(focus_previous) buttons_kb.add('right', filter=~last_selected)(focus_next) frame_body: AnyContainer if buttons: frame_body = HSplit([ # Add optional padding around the body. Box(body=DynamicContainer(lambda: self.body), padding=D(preferred=1, max=1), padding_bottom=0), # The buttons. Box(body=VSplit(buttons, padding=1, key_bindings=buttons_kb), height=D(min=1, max=3, preferred=3)) ]) else: frame_body = body # Key bindings for whole dialog. kb = KeyBindings() kb.add('tab', filter=~has_completions)(focus_next) kb.add('s-tab', filter=~has_completions)(focus_previous) frame = Shadow(body=Frame( title=lambda: self.title, body=frame_body, style='class:dialog.body', width=(None if with_background is None else width), key_bindings=kb, modal=modal, )) self.container: Union[Box, Shadow] if with_background: self.container = Box( body=frame, style='class:dialog', width=width) else: self.container = frame
def create_button_list_kbs(buttons, key_previous, key_next): kb = KeyBindings() if (len(buttons) > 1): is_first_selected = has_focus(buttons[0]) is_last_selected = has_focus(buttons[-1]) kb.add(key_previous, filter=~is_first_selected)(focus_previous) kb.add(key_next, filter=~is_last_selected)(focus_next) return kb
def __init__(self, body, title='', buttons=None, modal=True, width=None, with_background=False): assert is_formatted_text(title) assert buttons is None or isinstance(buttons, list) buttons = buttons or [] # When a button is selected, handle left/right key bindings. buttons_kb = KeyBindings() if len(buttons) > 1: first_selected = has_focus(buttons[0]) last_selected = has_focus(buttons[-1]) buttons_kb.add('left', filter=~first_selected)(focus_previous) buttons_kb.add('right', filter=~last_selected)(focus_next) if buttons: frame_body = HSplit([ # Add optional padding around the body. Box(body=body, padding=D(preferred=1, max=1), padding_bottom=0), # The buttons. Box(body=VSplit(buttons, padding=1, key_bindings=buttons_kb), height=D(min=1, max=3, preferred=3)) ]) else: frame_body = body # Key bindings for whole dialog. kb = KeyBindings() kb.add('tab', filter=~has_completions)(focus_next) kb.add('s-tab', filter=~has_completions)(focus_previous) frame = Shadow(body=Frame( title=title, body=frame_body, style='class:dialog.body', width=(None if with_background is None else width), key_bindings=kb, modal=modal, )) if with_background: self.container = Box(body=frame, style='class:dialog', width=width) else: self.container = frame
def create_python_input_window(): def menu_position(): """ When there is no autocompletion menu to be shown, and we have a signature, set the pop-up position at `bracket_start`. """ b = python_input.default_buffer if b.complete_state is None and python_input.signatures: row, col = python_input.signatures[0].bracket_start index = b.document.translate_row_col_to_index(row - 1, col) return index return Window( BufferControl( buffer=python_input.default_buffer, search_buffer_control=search_toolbar.control, lexer=lexer, include_default_input_processors=False, input_processors=[ ConditionalProcessor( processor=HighlightIncrementalSearchProcessor(), filter=has_focus(SEARCH_BUFFER) | has_focus(search_toolbar.control), ), HighlightSelectionProcessor(), DisplayMultipleCursors(), # Show matching parentheses, but only while editing. ConditionalProcessor( processor=HighlightMatchingBracketProcessor(chars='[](){}'), filter=has_focus(DEFAULT_BUFFER) & ~is_done & Condition(lambda: python_input.highlight_matching_parenthesis)), ConditionalProcessor( processor=AppendAutoSuggestion(), filter=~is_done) ] + extra_buffer_processors, menu_position=menu_position, # Make sure that we always see the result of an reverse-i-search: preview_search=True, ), left_margins=[PythonPromptMargin(python_input)], # Scroll offsets. The 1 at the bottom is important to make sure # the cursor is never below the "Press [Meta+Enter]" message # which is a float. scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4), # As long as we're editing, prefer a minimal height of 6. height=(lambda: ( None if get_app().is_done or python_input.show_exit_confirmation else input_buffer_height)), wrap_lines=Condition(lambda: python_input.wrap_lines), )
def pt_init(self): def get_prompt_tokens(): return [(Token.Prompt, self.prompt)] if self._ptcomp is None: compl = IPCompleter(shell=self.shell, namespace={}, global_namespace={}, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl) kb = KeyBindings() supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP')) kb.add('c-z', filter=supports_suspend)(suspend_to_bg) if self.shell.display_completions == 'readlinelike': kb.add('tab', filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & vi_insert_mode | emacs_insert_mode & ~cursor_in_leading_ws ))(display_completions_like_readline) self.pt_app = PromptSession( message=(lambda: PygmentsTokens(get_prompt_tokens())), editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings=kb, history=self.shell.debugger_history, completer=self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, complete_style=self.shell.pt_complete_style, style=self.shell.style, inputhook=self.shell.inputhook, )
def create_application(self): # Default key bindings. open_in_editor_bindings = load_open_in_editor_bindings() prompt_bindings = create_prompt_bindings() self.app = Application( layout=self.create_layout(), style=merge_styles([ default_style(), DynamicStyle(lambda: self.style), ]), key_bindings=merge_key_bindings([ merge_key_bindings([ ConditionalKeyBindings( open_in_editor_bindings, to_filter(self.enable_open_in_editor) & has_focus(DEFAULT_BUFFER)), prompt_bindings ]), DynamicKeyBindings(lambda: self.extra_key_bindings), ]), editing_mode=self.editing_mode, reverse_vi_search_direction=True, on_render=self.on_render, input=self.input, output=self.output) self.app.mp = self
def _create_prompt_bindings(self): """ Create the KeyBindings for a prompt application. """ kb = KeyBindings() handle = kb.add default_focused = has_focus(DEFAULT_BUFFER) @Condition def do_accept(): return (not _true(self.multiline) and self.app.layout.current_control == self._default_buffer_control) @handle('enter', filter=do_accept & default_focused) def _(event): " Accept input when enter has been pressed. " self.default_buffer.validate_and_handle() @Condition def readline_complete_style(): return self.complete_style == CompleteStyle.READLINE_LIKE @handle('tab', filter=readline_complete_style & default_focused) def _(event): " Display completions (like readline). " display_completions_like_readline(event) @handle('c-c', filter=default_focused) def _(event): " Abort when Control-C has been pressed. " event.app.abort() @Condition def ctrl_d_condition(): """ Ctrl-D binding is only active when the default buffer is selected and empty. """ app = get_app() return (app.current_buffer.name == DEFAULT_BUFFER and not app.current_buffer.text) @handle('c-d', filter=ctrl_d_condition & default_focused) def _(event): " Exit when Control-D has been pressed. " event.app.exit() suspend_supported = Condition(suspend_to_background_supported) @Condition def enable_suspend(): return to_filter(self.enable_suspend)() @handle('c-z', filter=suspend_supported & enable_suspend) def _(event): """ Suspend process to background. """ event.app.suspend_to_background() return kb
def __init__(self, editor): super(CommandLine, self).__init__( Window(BufferControl(buffer=editor.command_buffer, input_processors=[BeforeInput(':')], lexer=create_command_lexer()), height=1), filter=has_focus(editor.command_buffer))
def __init__(self): self.text_control = FormattedTextControl(text=HTML('')) self.window = Window(content=self.text_control, always_hide_cursor=True, align=WindowAlign.LEFT) super(HelpWindow, self).__init__(content=self.window, filter=has_focus(self.window))
def __init__( self, prompt: AnyFormattedText = "Shell command: ", enable_global_bindings: FilterOrBool = True, ) -> None: self.prompt = prompt self.enable_global_bindings = to_filter(enable_global_bindings) self.system_buffer = Buffer(name=SYSTEM_BUFFER) self._bindings = self._build_key_bindings() self.buffer_control = BufferControl( buffer=self.system_buffer, lexer=SimpleLexer(style="class:system-toolbar.text"), input_processors=[ BeforeInput(lambda: self.prompt, style="class:system-toolbar") ], key_bindings=self._bindings, ) self.window = Window( self.buffer_control, height=1, style="class:system-toolbar" ) self.container = ConditionalContainer( content=self.window, filter=has_focus(self.system_buffer) )
def meta_enter_message(python_input: "PythonInput") -> Container: """ Create the `Layout` for the 'Meta+Enter` message. """ def get_text_fragments() -> StyleAndTextTuples: return [("class:accept-message", " [Meta+Enter] Execute ")] @Condition def extra_condition() -> bool: " Only show when... " b = python_input.default_buffer return ( python_input.show_meta_enter_message and ( not b.document.is_cursor_at_the_end or python_input.accept_input_on_enter is None ) and "\n" in b.text ) visible = ~is_done & has_focus(DEFAULT_BUFFER) & extra_condition return ConditionalContainer( content=Window(FormattedTextControl(get_text_fragments)), filter=visible )
def _create_application(self, editing_mode, erase_when_done): """ Create the `Application` object. """ dyncond = self._dyncond # Default key bindings. auto_suggest_bindings = load_auto_suggest_bindings() open_in_editor_bindings = load_open_in_editor_bindings() prompt_bindings = self._create_prompt_bindings() # Create application application = Application( layout=self.layout, style=DynamicStyle(lambda: self.style), include_default_pygments_style=dyncond( 'include_default_pygments_style'), clipboard=DynamicClipboard(lambda: self.clipboard), key_bindings=merge_key_bindings([ merge_key_bindings([ auto_suggest_bindings, ConditionalKeyBindings( open_in_editor_bindings, dyncond('enable_open_in_editor') & has_focus(DEFAULT_BUFFER)), prompt_bindings ]), DynamicKeyBindings(lambda: self.key_bindings), ]), mouse_support=dyncond('mouse_support'), editing_mode=editing_mode, erase_when_done=erase_when_done, reverse_vi_search_direction=True, color_depth=lambda: self.color_depth, # I/O. input=self.input, output=self.output) # During render time, make sure that we focus the right search control # (if we are searching). - This could be useful if people make the # 'multiline' property dynamic. ''' def on_render(app): multiline = _true(self.multiline) current_control = app.layout.current_control if multiline: if current_control == search_buffer_control: app.layout.current_control = search_toolbar.control app.invalidate() else: if current_control == search_toolbar.control: app.layout.current_control = search_buffer_control app.invalidate() app.on_render += on_render ''' return application
def _create_prompt_bindings(self): """ Create the KeyBindings for a prompt application. """ kb = KeyBindings() handle = kb.add default_focused = has_focus(DEFAULT_BUFFER) @Condition def do_accept(): return (not is_true(self.multiline) and self.app.layout.has_focus(DEFAULT_BUFFER)) @handle('enter', filter=do_accept & default_focused) def _(event): " Accept input when enter has been pressed. " self.default_buffer.validate_and_handle() @Condition def readline_complete_style(): return self.complete_style == CompleteStyle.READLINE_LIKE @handle('tab', filter=readline_complete_style & default_focused) def _(event): " Display completions (like Readline). " display_completions_like_readline(event) @handle('c-c', filter=default_focused) def _(event): " Abort when Control-C has been pressed. " event.app.exit(exception=KeyboardInterrupt, style='class:aborting') @Condition def ctrl_d_condition(): """ Ctrl-D binding is only active when the default buffer is selected and empty. """ app = get_app() return (app.current_buffer.name == DEFAULT_BUFFER and not app.current_buffer.text) @handle('c-d', filter=ctrl_d_condition & default_focused) def _(event): " Exit when Control-D has been pressed. " event.app.exit(exception=EOFError, style='class:exiting') suspend_supported = Condition(suspend_to_background_supported) @Condition def enable_suspend(): return to_filter(self.enable_suspend)() @handle('c-z', filter=suspend_supported & enable_suspend) def _(event): """ Suspend process to background. """ event.app.suspend_to_background() return kb
def __init__(self, editor): def get_formatted_text(): eb = editor.window_arrangement.active_editor_buffer lineno = eb.buffer.document.cursor_position_row errors = eb.report_errors for e in errors: if e.lineno == lineno: return e.formatted_text return [] super(ReportMessageToolbar, self).__init__(FormattedTextToolbar(get_formatted_text), filter=~has_focus(editor.command_buffer) & ~is_searching & ~has_focus('system'))
def create_completion_float(self) -> Float: return Float(xcursor=True, ycursor=True, transparent=True, attach_to_window=self.input_window, content=CompletionsMenu(scroll_offset=1, max_height=16, extra_filter=has_focus( self.input_buffer)))
def add_custom_binding(self, key_name, command, arguments, needs_prefix=False): """ Add custom binding (for the "bind-key" command.) Raises ValueError if the give `key_name` is an invalid name. :param key_name: Pymux key name, for instance "C-a" or "M-x". """ assert isinstance(key_name, six.text_type) assert isinstance(command, six.text_type) assert isinstance(arguments, list) # Unbind previous key. self.remove_custom_binding(key_name, needs_prefix=needs_prefix) # Translate the pymux key name into a prompt_toolkit key sequence. # (Can raise ValueError.) keys_sequence = pymux_key_to_prompt_toolkit_key_sequence(key_name) # Create handler and add to Registry. if needs_prefix: filter = HasPrefix(self.pymux) else: filter = ~HasPrefix(self.pymux) filter = filter & ~(WaitsForConfirmation(self.pymux) | has_focus(COMMAND) | has_focus(PROMPT)) def key_handler(event): " The actual key handler. " call_command_handler(command, self.pymux, arguments) self.pymux.get_client_state().has_prefix = False self.custom_key_bindings.add(*keys_sequence, filter=filter)(key_handler) # Store key in `custom_bindings` in order to be able to call # "unbind-key" later on. k = (needs_prefix, key_name) self.custom_bindings[k] = CustomBinding(key_handler, command, arguments)
def __init__(self, options, default_index=0, header_filter=lambda x: x, match_filter=lambda x: x): self.info_window = InfoWindow() self.help_window = HelpWindow() self.message_toolbar = MessageToolbar(style="class:message_toolbar") self.error_toolbar = MessageToolbar(style="class:error_toolbar") self.status_line = MessageToolbar(style="class:status_line") self.status_line_format = user._tui_status_line_format self.options_list = OptionsList( options, default_index, header_filter, match_filter, custom_filter=~has_focus(self.help_window)) self.options_list.search_buffer.on_text_changed += self.update cmd, cmd_key_bindings = command_key_bindings(self) self.command_line_prompt = CommandLinePrompt(commands=cmd) key_bindings = merge_key_bindings( [general_key_bindings(self), cmd_key_bindings]) _root_container = HSplit([ HSplit([ Window(content=BufferControl( input_processors=[BeforeInput('> ')], buffer=self.options_list.search_buffer)), self.options_list, self.info_window, ]), self.help_window, self.error_toolbar, self.message_toolbar, self.status_line, self.command_line_prompt.window, ]) self.set_help_text() self.layout = Layout(_root_container) super(Picker, self).__init__( input=None, output=None, editing_mode=EditingMode.EMACS if user._tui_edit_mode == 'emacs' else EditingMode.VI, layout=self.layout, style=Style.from_dict(user_styles.get_user_styles()), key_bindings=key_bindings, include_default_pygments_style=False, full_screen=True, enable_page_navigation_bindings=True) self.update()
def __init__(self, commands: List[Command] = []): self.commands = commands wc = WordCompleter(sum([c.names for c in commands], [])) self.buf = Buffer(completer=wc, complete_while_typing=True) self.buf.text = '' self.window = Window(content=BufferControl( buffer=self.buf, input_processors=[BeforeInput(':')]), height=1) super(CommandLinePrompt, self).__init__(content=self.window, filter=has_focus(self.window))
def _load_prefix_binding(self): """ Load the prefix key binding. """ pymux = self.pymux # Remove previous binding. if self._prefix_binding: self.custom_key_bindings.remove_binding(self._prefix_binding) # Create new Python binding. @self.custom_key_bindings.add(*self._prefix, filter= ~(HasPrefix(pymux) | has_focus(COMMAND) | has_focus(PROMPT) | WaitsForConfirmation(pymux))) def enter_prefix_handler(event): " Enter prefix mode. " pymux.get_client_state().has_prefix = True self._prefix_binding = enter_prefix_handler
def __init__(self, lexer_name='yaml'): self.buf = Buffer() self.buf.text = '' self.lexer = PygmentsLexer(find_lexer_class_by_name(lexer_name)) self.window = HSplit([ HorizontalLine(), Window(content=BufferControl(buffer=self.buf, lexer=self.lexer)) ], height=Dimension(min=5, max=20, weight=1)) super(InfoWindow, self).__init__(content=self.window, filter=has_focus(self))
def _load_prefix_binding(self): """ Load the prefix key binding. """ pymux = self.pymux # Remove previous binding. if self._prefix_binding: self.custom_key_bindings.remove_binding(self._prefix_binding) # Create new Python binding. @self.custom_key_bindings.add( *self._prefix, filter=~(HasPrefix(pymux) | has_focus(COMMAND) | has_focus(PROMPT) | WaitsForConfirmation(pymux))) def enter_prefix_handler(event): " Enter prefix mode. " pymux.get_client_state().has_prefix = True self._prefix_binding = enter_prefix_handler
def _build_key_bindings(self): focussed = has_focus(self.system_buffer) # Emacs emacs_bindings = KeyBindings() handle = emacs_bindings.add @handle('escape', filter=focussed) @handle('c-g', filter=focussed) @handle('c-c', filter=focussed) def _(event): " Hide system prompt. " self.system_buffer.reset() event.app.layout.focus_previous() @handle('enter', filter=focussed) def _(event): " Run system command. " event.app.run_system_command( self.system_buffer.text, display_before_text=self._get_display_before_text()) self.system_buffer.reset(append_to_history=True) event.app.layout.focus_previous() # Vi. vi_bindings = KeyBindings() handle = vi_bindings.add @handle('escape', filter=focussed) @handle('c-c', filter=focussed) def _(event): " Hide system prompt. " event.app.vi_state.input_mode = InputMode.NAVIGATION self.system_buffer.reset() event.app.layout.focus_previous() @handle('enter', filter=focussed) def _(event): " Run system command. " event.app.vi_state.input_mode = InputMode.NAVIGATION event.app.run_system_command( self.system_buffer.text, display_before_text=self._get_display_before_text()) self.system_buffer.reset(append_to_history=True) event.app.layout.focus_previous() return merge_key_bindings([ ConditionalKeyBindings(emacs_bindings, emacs_mode), ConditionalKeyBindings(vi_bindings, vi_mode), ])
def _create_search_mode_bindings(self): key_bindings = KeyBindings() handle = key_bindings.add default_focused = has_focus(DEFAULT_BUFFER) @handle('c-w', filter=default_focused) def _(event): buf = event.current_buffer self.completer.toggle_search_mode() buf.complete_state = None buf.start_completion() self.update_toolbar_text() return key_bindings
def main(): # The layout. output_field = TextArea(style='class:output-field', text=help_text) input_field = TextArea(height=1, prompt='>>> ', style='class:input-field') container = HSplit([ output_field, Window(height=1, char='-', style='class:line'), input_field ]) # The key bindings. kb = KeyBindings() @kb.add('c-c') @kb.add('c-q') def _(event): " Pressing Ctrl-Q or Ctrl-C will exit the user interface. " event.app.exit() @kb.add('enter', filter=has_focus(input_field)) def _(event): try: output = '\n\nIn: {}\nOut: {}'.format( input_field.text, eval(input_field.text)) # Don't do 'eval' in real code! except BaseException as e: output = '\n\n{}'.format(e) new_text = output_field.text + output output_field.buffer.document = Document(text=new_text, cursor_position=len(new_text)) input_field.text = '' # Style. style = Style([ ('output-field', 'bg:#000044 #ffffff'), ('input-field', 'bg:#000000 #ffffff'), ('line', '#004400'), ]) # Run application. application = Application(layout=Layout(container, focused_element=input_field), key_bindings=kb, style=style, mouse_support=True, full_screen=True) application.run()
def _build_global_key_bindings(self): focussed = has_focus(self.system_buffer) bindings = KeyBindings() @bindings.add(Keys.Escape, '!', filter= ~focussed & emacs_mode) def _(event): " M-'!' will focus this user control. " event.app.layout.focus(self.window) @bindings.add('!', filter=~focussed & vi_mode & vi_navigation_mode) def _(event): " Focus. " event.app.vi_state.input_mode = InputMode.INSERT event.app.layout.focus(self.window) return bindings
def render(self): choices_len = len(self._choices) buttons = [ Button('[%s] %s' % (i, label) if choices_len > 1 else label, handler=self._create_handler(sub_component)) for ((label, sub_component), i) in zip(self._choices, range( 1, choices_len + 1)) # this is still a loop even if it is inline.. ] self._first_button = buttons[0] global global__default_target_focus global__default_target_focus = self._first_button kb = create_vertical_button_list_kbs(buttons) is_first_selected = has_focus(buttons[0]) kb.add('up', filter=is_first_selected)(lambda e: focus_first_element()) return Box( HSplit( [ TextArea( text=self._label.format( name=self._controller.state.username) + '\n', read_only=True, focusable=False, scrollbar=True, # push buttons to bottom of screen height=Dimension(preferred=100000, max=100000)), HSplit([ VSplit(children=[button], align=HorizontalAlign.CENTER) for button in buttons ], padding=1, key_bindings=kb) ], padding=Dimension(preferred=2, max=2), width=Dimension(), align=VerticalAlign.TOP), padding=1)
def main(): key_bindings = KeyBindings() default_focused = has_focus(DEFAULT_BUFFER) # Autocomplete with backspace @key_bindings.add('backspace', filter=default_focused) def _(event): event.current_buffer.delete_before_cursor() event.current_buffer.insert_text('') session = PromptSession( wrap_lines=False, completer=HistoryCompleter(), key_bindings=key_bindings, ) text = '%s\n' % session.prompt('> ') if text.strip(): for c in text: fcntl.ioctl(sys.stdout, termios.TIOCSTI, c)
def create_prompt_bindings(): """ Create the KeyBindings for a prompt application. """ kb = KeyBindings() handle = kb.add default_focussed = has_focus(DEFAULT_BUFFER) @handle('enter', filter=default_focussed) def _(event): " Accept input when enter has been pressed. " event.current_buffer.validate_and_handle() @handle('c-c', filter=default_focussed) def _(event): " Abort when Control-C has been pressed. " event.app.abort() @Condition def ctrl_d_condition(): """ Ctrl-D binding is only active when the default buffer is selected and empty. """ app = get_app() return (app.current_buffer.name == DEFAULT_BUFFER and not app.current_buffer.text) @handle('c-d', filter=ctrl_d_condition & default_focussed) def _(event): " Exit when Control-D has been pressed. " event.app.exit() suspend_supported = Condition(suspend_to_background_supported) @handle('c-z', filter=suspend_supported) def _(event): """ Suspend process to background. """ event.app.suspend_to_background() return kb
def __init__(self, prompt='Shell command: '): self.prompt = prompt self.system_buffer = Buffer(name=SYSTEM_BUFFER) self._global_bindings = self._build_global_key_bindings() self._bindings = self._build_key_bindings() self.buffer_control = BufferControl( buffer=self.system_buffer, lexer=SimpleLexer(style='class:system-toolbar.text'), input_processor=BeforeInput( lambda: self.prompt, style='class:system-toolbar'), key_bindings=self._bindings) self.window = Window( self.buffer_control, height=1, style='class:system-toolbar') self.container = ConditionalContainer( content=self.window, filter=has_focus(self.system_buffer))
def meta_enter_message(python_input): """ Create the `Layout` for the 'Meta+Enter` message. """ def get_text_fragments(): return [('class:accept-message', ' [Meta+Enter] Execute ')] def extra_condition(): " Only show when... " b = python_input.default_buffer return (python_input.show_meta_enter_message and (not b.document.is_cursor_at_the_end or python_input.accept_input_on_enter is None) and '\n' in b.text) visible = ~is_done & has_focus(DEFAULT_BUFFER) & Condition(extra_condition) return ConditionalContainer(content=Window( FormattedTextControl(get_text_fragments)), filter=visible)
def start(self): # layout self.output_field = TextArea(style='class:output-field', text=BANNER, read_only=True, scrollbar=True) self.input_field = TextArea(height=1, prompt='>>> ', style='class:input-field') self.container = HSplit([ output_field, Window(height=1, char='-', style='class:line'), input_field ]) # key bindings self.kb = KeyBindings() @kb.add('c-q') @kb.add('c-c') def _(event): event.app.exit() @kb.add('enter', filter=has_focus(input_field)) def _(event): try: out = "\n\n[ In] {i}\n[Out]{o}".format( i=self.input_field.text, o=self.parseline(self.input_field.text)) except Exception as e: out = "\n\n{e}".format(e=e) new_out = self.output_field.text + out self.output_field.buffer.document = Document( text=new_text, cursor_position=len(new_text)) self.input_field.text = '' # style style = Style(self.LAYOUT_STYLE)
def meta_enter_message(python_input): """ Create the `Layout` for the 'Meta+Enter` message. """ def get_text_fragments(): return [('class:accept-message', ' [Meta+Enter] Execute ')] def extra_condition(): " Only show when... " b = python_input.default_buffer return ( python_input.show_meta_enter_message and (not b.document.is_cursor_at_the_end or python_input.accept_input_on_enter is None) and '\n' in b.text) visible = ~is_done & has_focus(DEFAULT_BUFFER) & Condition(extra_condition) return ConditionalContainer( content=Window(FormattedTextControl(get_text_fragments)), filter=visible)
def __init__(self, prompt='Shell command: ', enable_global_bindings=True): self.prompt = prompt self.enable_global_bindings = to_filter(enable_global_bindings) self.system_buffer = Buffer(name=SYSTEM_BUFFER) self._bindings = self._build_key_bindings() self.buffer_control = BufferControl( buffer=self.system_buffer, lexer=SimpleLexer(style='class:system-toolbar.text'), input_processors=[BeforeInput( lambda: self.prompt, style='class:system-toolbar')], key_bindings=self._bindings) self.window = Window( self.buffer_control, height=1, style='class:system-toolbar') self.container = ConditionalContainer( content=self.window, filter=has_focus(self.system_buffer))
def set_kb(self): @self.kb.add('c-d') def _(event): " Pressing Ctrl-D will exit the user interface. " event.app.exit() @self.kb.add('c-q') def _(event): " Pressing Ctrl-Q will exit the user interface. " self.quit = True event.app.exit() @self.kb.add('enter', filter=has_focus(self.elements['input'])) def _(event): try: selection = self.elements['input'].text if selection == 'start': self.start_game() else: self.make_moves(selection) except BaseException as e: self.set_text('board', str(e)) self.elements['input'].text = ''
def configure(repl): """ Configuration method. This is called during the start-up of ptpython. :param repl: `PythonRepl` instance. """ # Show function signature (bool). repl.show_signature = True # Show docstring (bool). repl.show_docstring = False # Accept when pressing Enter 'n' times. # 'None' means that meta-enter is always required. repl.accept_input_on_enter = 3 # Show the "[Meta+Enter] Execute" message when pressing [Enter] only # inserts a newline instead of executing the code. repl.show_meta_enter_message = True # Show completions. (NONE, POP_UP, MULTI_COLUMN or TOOLBAR) repl.completion_visualisation = CompletionVisualisation.POP_UP # When CompletionVisualisation.POP_UP has been chosen, use this # scroll_offset in the completion menu. repl.completion_menu_scroll_offset = 0 # Show line numbers (when the input contains multiple lines.) repl.show_line_numbers = True # Show status bar. repl.show_status_bar = True # When the sidebar is visible, also show the help text. repl.show_sidebar_help = True # Highlight matching parethesis. repl.highlight_matching_parenthesis = True # Line wrapping. (Instead of horizontal scrolling.) repl.wrap_lines = True # Mouse support. repl.enable_mouse_support = True # Complete while typing. (Don't require tab before the # completion menu is shown.) repl.complete_while_typing = True # Vi mode. repl.vi_mode = False # Paste mode. (When True, don't insert whitespace after new line.) repl.paste_mode = False # Use the classic prompt. (Display '>>>' instead of 'In [1]'.) repl.prompt_style = 'classic' # 'classic' or 'ipython' # Don't insert a blank line after the output. repl.insert_blank_line_after_output = False # History Search. # When True, going back in history will filter the history on the records # starting with the current input. (Like readline.) # Note: When enable, please disable the `complete_while_typing` option. # otherwise, when there is a completion available, the arrows will # browse through the available completions instead of the history. repl.enable_history_search = False # Enable auto suggestions. (Pressing right arrow will complete the input, # based on the history.) repl.enable_auto_suggest = True # Enable open-in-editor. Pressing C-X C-E in emacs mode or 'v' in # Vi navigation mode will open the input in the current editor. repl.enable_open_in_editor = True # Enable system prompt. Pressing meta-! will display the system prompt. # Also enables Control-Z suspend. repl.enable_system_bindings = True # Ask for confirmation on exit. repl.confirm_exit = True # Enable input validation. (Don't try to execute when the input contains # syntax errors.) repl.enable_input_validation = True # Use this colorscheme for the code. repl.use_code_colorscheme('pastie') # Set color depth (keep in mind that not all terminals support true color). #repl.color_depth = 'DEPTH_1_BIT' # Monochrome. #repl.color_depth = 'DEPTH_4_BIT' # ANSI colors only. repl.color_depth = 'DEPTH_8_BIT' # The default, 256 colors. #repl.color_depth = 'DEPTH_24_BIT' # True color. # Syntax. repl.enable_syntax_highlighting = True # Install custom colorscheme named 'my-colorscheme' and use it. """ repl.install_ui_colorscheme('my-colorscheme', _custom_ui_colorscheme) repl.use_ui_colorscheme('my-colorscheme') """ Keys.CtrlEnter = "<ignore>" ANSI_SEQUENCES['\x1b_KrEz\x1b\\'] = Keys.CtrlEnter Keys.ShiftEnter = "<ignore>" ANSI_SEQUENCES['\x1b_KrBz\x1b\\'] = Keys.ShiftEnter @Condition def is_multiline(): return document_is_multiline_python(repl.default_buffer.document) @repl.add_key_binding(Keys.CtrlEnter, filter=has_focus(DEFAULT_BUFFER)) @repl.add_key_binding(Keys.ShiftEnter, filter=has_focus(DEFAULT_BUFFER)) def _(event): "Shift-Enter accepts line" b = event.cli.current_buffer b.validate_and_handle() @repl.add_key_binding('c-m', filter=( ~has_selection & has_focus(DEFAULT_BUFFER) & ~is_multiline & (vi_insert_mode | emacs_insert_mode) )) def _(event): "Enter inserts new line from first line" b = event.cli.current_buffer b.insert_text('\n') # Add custom key binding for PDB. """ @repl.add_key_binding(Keys.ControlB) def _(event): ' Pressing Control-B will insert "pdb.set_trace()" ' event.cli.current_buffer.insert_text('\nimport pdb; pdb.set_trace()\n') """ # Typing ControlE twice should also execute the current command. # (Alternative for Meta-Enter.) """ @repl.add_key_binding(Keys.ControlE, Keys.ControlE) def _(event): event.current_buffer.validate_and_handle() """ # Typing 'jj' in Vi Insert mode, should send escape. (Go back to navigation # mode.) """ @repl.add_key_binding('j', 'j', filter=ViInsertMode()) def _(event): " Map 'jj' to Escape. " event.cli.key_processor.feed(KeyPress(Keys.Escape)) """ # Custom key binding for some simple autocorrection while typing. """
def _create_application(self, editing_mode, erase_when_done): """ Create the `Application` object. """ dyncond = self._dyncond # Default key bindings. auto_suggest_bindings = load_auto_suggest_bindings() open_in_editor_bindings = load_open_in_editor_bindings() prompt_bindings = self._create_prompt_bindings() # Create application application = Application( layout=self.layout, style=DynamicStyle(lambda: self.style), style_transformation=merge_style_transformations([ DynamicStyleTransformation(lambda: self.style_transformation), ConditionalStyleTransformation( SwapLightAndDarkStyleTransformation(), dyncond('swap_light_and_dark_colors'), ), ]), include_default_pygments_style=dyncond('include_default_pygments_style'), clipboard=DynamicClipboard(lambda: self.clipboard), key_bindings=merge_key_bindings([ merge_key_bindings([ auto_suggest_bindings, ConditionalKeyBindings(open_in_editor_bindings, dyncond('enable_open_in_editor') & has_focus(DEFAULT_BUFFER)), prompt_bindings ]), DynamicKeyBindings(lambda: self.key_bindings), ]), mouse_support=dyncond('mouse_support'), editing_mode=editing_mode, erase_when_done=erase_when_done, reverse_vi_search_direction=True, color_depth=lambda: self.color_depth, # I/O. input=self.input, output=self.output) # During render time, make sure that we focus the right search control # (if we are searching). - This could be useful if people make the # 'multiline' property dynamic. ''' def on_render(app): multiline = is_true(self.multiline) current_control = app.layout.current_control if multiline: if current_control == search_buffer_control: app.layout.current_control = search_toolbar.control app.invalidate() else: if current_control == search_toolbar.control: app.layout.current_control = search_buffer_control app.invalidate() app.on_render += on_render ''' return application
def init_prompt_toolkit_cli(self): if self.simple_prompt or ('JUPYTER_CONSOLE_TEST' in os.environ): # Simple restricted interface for tests so we can find prompts with # pexpect. Multi-line input not supported. def prompt(): return cast_unicode_py2(input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt self.print_out_prompt = \ lambda: print('Out[%d]: ' % self.execution_count, end='') return kb = KeyBindings() insert_mode = vi_insert_mode | emacs_insert_mode @kb.add("enter", filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode )) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return # Pressing enter flushes any pending display. This also ensures # the displayed execution_count is correct. self.handle_iopub() more, indent = self.check_complete(d.text) if (not more) and b.accept_handler: b.validate_and_handle() else: b.insert_text('\n' + indent) @kb.add("c-c", filter=has_focus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() @kb.add("c-\\", filter=has_focus(DEFAULT_BUFFER)) def _(event): raise EOFError @kb.add("c-z", filter=Condition(lambda: suspend_to_background_supported())) def _(event): event.cli.suspend_to_background() # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail(self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cast_unicode_py2(cell.rstrip()) if cell and (cell != last_cell): history.append_string(cell) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#ff2200', Token.OutPromptNum: '#ff0000 bold', } if self.highlighting_style: style_cls = get_style_by_name(self.highlighting_style) else: style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = merge_styles([ style_from_pygments_cls(style_cls), style_from_pygments_dict(style_overrides), ]) editing_mode = getattr(EditingMode, self.editing_mode.upper()) langinfo = self.kernel_info.get('language_info', {}) lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text')) # If enabled in the settings, highlight matching brackets # when the DEFAULT_BUFFER has the focus input_processors = [ConditionalProcessor( processor=HighlightMatchingBracketProcessor(chars='[](){}'), filter=has_focus(DEFAULT_BUFFER) & ~is_done & Condition(lambda: self.highlight_matching_brackets)) ] self.pt_cli = PromptSession( message=(lambda: PygmentsTokens(self.get_prompt_tokens())), multiline=True, editing_mode=editing_mode, lexer=PygmentsLexer(get_pygments_lexer(lexer)), prompt_continuation=( lambda width, lineno, is_soft_wrap: PygmentsTokens(self.get_continuation_tokens(width)) ), key_bindings=kb, history=history, completer=JupyterPTCompleter(self.Completer), enable_history_search=True, style=style, input_processors=input_processors, color_depth=(ColorDepth.TRUE_COLOR if self.true_color else None), )
def create_ipython_shortcuts(shell): """Set up the prompt_toolkit keyboard shortcuts for IPython""" kb = KeyBindings() insert_mode = vi_insert_mode | emacs_insert_mode if getattr(shell, 'handle_return', None): return_handler = shell.handle_return(shell) else: return_handler = newline_or_execute_outer(shell) kb.add('enter', filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode ))(return_handler) kb.add('c-\\')(force_exit) kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)) )(previous_history_or_previous_completion) kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)) )(next_history_or_next_completion) kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions) )(dismiss_completion) kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer) kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer) supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP')) kb.add('c-z', filter=supports_suspend)(suspend_to_bg) # Ctrl+I == Tab kb.add('tab', filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode & cursor_in_leading_ws ))(indent_buffer) kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode))(newline_autoindent_outer(shell.input_splitter)) kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor) if shell.display_completions == 'readlinelike': kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode & ~cursor_in_leading_ws ))(display_completions_like_readline) if sys.platform == 'win32': kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste) return kb
def _load_builtins(self): """ Fill the Registry with the hard coded key bindings. """ pymux = self.pymux kb = KeyBindings() # Create filters. has_prefix = HasPrefix(pymux) waits_for_confirmation = WaitsForConfirmation(pymux) prompt_or_command_focus = has_focus(COMMAND) | has_focus(PROMPT) display_pane_numbers = Condition(lambda: pymux.display_pane_numbers) in_scroll_buffer_not_searching = InScrollBufferNotSearching(pymux) @kb.add(Keys.Any, filter=has_prefix) def _(event): " Ignore unknown Ctrl-B prefixed key sequences. " pymux.get_client_state().has_prefix = False @kb.add('c-c', filter=prompt_or_command_focus & ~has_prefix) @kb.add('c-g', filter=prompt_or_command_focus & ~has_prefix) # @kb.add('backspace', filter=has_focus(COMMAND) & ~has_prefix & # Condition(lambda: cli.buffers[COMMAND].text == '')) def _(event): " Leave command mode. " pymux.leave_command_mode(append_to_history=False) @kb.add('y', filter=waits_for_confirmation) @kb.add('Y', filter=waits_for_confirmation) def _(event): """ Confirm command. """ client_state = pymux.get_client_state() command = client_state.confirm_command client_state.confirm_command = None client_state.confirm_text = None pymux.handle_command(command) @kb.add('n', filter=waits_for_confirmation) @kb.add('N', filter=waits_for_confirmation) @kb.add('c-c' , filter=waits_for_confirmation) def _(event): """ Cancel command. """ client_state = pymux.get_client_state() client_state.confirm_command = None client_state.confirm_text = None @kb.add('c-c', filter=in_scroll_buffer_not_searching) @kb.add('enter', filter=in_scroll_buffer_not_searching) @kb.add('q', filter=in_scroll_buffer_not_searching) def _(event): " Exit scroll buffer. " pane = pymux.arrangement.get_active_pane() pane.exit_scroll_buffer() @kb.add(' ', filter=in_scroll_buffer_not_searching) def _(event): " Enter selection mode when pressing space in copy mode. " event.current_buffer.start_selection(selection_type=SelectionType.CHARACTERS) @kb.add('enter', filter=in_scroll_buffer_not_searching & has_selection) def _(event): " Copy selection when pressing Enter. " clipboard_data = event.current_buffer.copy_selection() event.app.clipboard.set_data(clipboard_data) @kb.add('v', filter=in_scroll_buffer_not_searching & has_selection) def _(event): " Toggle between selection types. " types = [SelectionType.LINES, SelectionType.BLOCK, SelectionType.CHARACTERS] selection_state = event.current_buffer.selection_state try: index = types.index(selection_state.type) except ValueError: # Not in list. index = 0 selection_state.type = types[(index + 1) % len(types)] @Condition def popup_displayed(): return self.pymux.get_client_state().display_popup @kb.add('q', filter=popup_displayed, eager=True) def _(event): " Quit pop-up dialog. " self.pymux.get_client_state().display_popup = False @kb.add(Keys.Any, eager=True, filter=display_pane_numbers) def _(event): " When the pane numbers are shown. Any key press should hide them. " pymux.display_pane_numbers = False @Condition def clock_displayed(): " " pane = pymux.arrangement.get_active_pane() return pane.clock_mode @kb.add(Keys.Any, eager=True, filter=clock_displayed) def _(event): " When the clock is displayed. Any key press should hide it. " pane = pymux.arrangement.get_active_pane() pane.clock_mode = False return kb
def create_key_bindings(editor): """ Create custom key bindings. This starts with the key bindings, defined by `prompt-toolkit`, but adds the ones which are specific for the editor. """ kb = KeyBindings() # Filters. @Condition def vi_buffer_focussed(): app = get_app() if app.layout.has_focus(editor.search_buffer) or app.layout.has_focus(editor.command_buffer): return False return True in_insert_mode = vi_insert_mode & vi_buffer_focussed in_navigation_mode = vi_navigation_mode & vi_buffer_focussed @kb.add('c-t') def _(event): """ Override default behaviour of prompt-toolkit. (Control-T will swap the last two characters before the cursor, because that's what readline does.) """ pass @kb.add('c-t', filter=in_insert_mode) def indent_line(event): """ Indent current line. """ b = event.application.current_buffer # Move to start of line. pos = b.document.get_start_of_line_position(after_whitespace=True) b.cursor_position += pos # Insert tab. if editor.expand_tab: b.insert_text(' ') else: b.insert_text('\t') # Restore cursor. b.cursor_position -= pos @kb.add('c-r', filter=in_navigation_mode, save_before=(lambda e: False)) def redo(event): """ Redo. """ event.app.current_buffer.redo() @kb.add(':', filter=in_navigation_mode) def enter_command_mode(event): """ Entering command mode. """ editor.enter_command_mode() @kb.add('tab', filter=vi_insert_mode & ~has_focus(editor.command_buffer) & whitespace_before_cursor_on_line) def autocomplete_or_indent(event): """ When the 'tab' key is pressed with only whitespace character before the cursor, do autocompletion. Otherwise, insert indentation. """ b = event.app.current_buffer if editor.expand_tab: b.insert_text(' ') else: b.insert_text('\t') @kb.add('escape', filter=has_focus(editor.command_buffer)) @kb.add('c-c', filter=has_focus(editor.command_buffer)) @kb.add('backspace', filter=has_focus(editor.command_buffer) & Condition(lambda: editor.command_buffer.text == '')) def leave_command_mode(event): """ Leaving command mode. """ editor.leave_command_mode() @kb.add('c-w', 'c-w', filter=in_navigation_mode) def focus_next_window(event): editor.window_arrangement.cycle_focus() editor.sync_with_prompt_toolkit() @kb.add('c-w', 'n', filter=in_navigation_mode) def horizontal_split(event): """ Split horizontally. """ editor.window_arrangement.hsplit(None) editor.sync_with_prompt_toolkit() @kb.add('c-w', 'v', filter=in_navigation_mode) def vertical_split(event): """ Split vertically. """ editor.window_arrangement.vsplit(None) editor.sync_with_prompt_toolkit() @kb.add('g', 't', filter=in_navigation_mode) def focus_next_tab(event): editor.window_arrangement.go_to_next_tab() editor.sync_with_prompt_toolkit() @kb.add('g', 'T', filter=in_navigation_mode) def focus_previous_tab(event): editor.window_arrangement.go_to_previous_tab() editor.sync_with_prompt_toolkit() @kb.add('f1') def show_help(event): editor.show_help() @Condition def in_file_explorer_mode(): return bool(editor.current_editor_buffer and editor.current_editor_buffer.in_file_explorer_mode) @kb.add('enter', filter=in_file_explorer_mode) def open_path(event): """ Open file/directory in file explorer mode. """ name_under_cursor = event.current_buffer.document.current_line new_path = os.path.normpath(os.path.join( editor.current_editor_buffer.location, name_under_cursor)) editor.window_arrangement.open_buffer( new_path, show_in_current_window=True) editor.sync_with_prompt_toolkit() @kb.add('-', filter=in_file_explorer_mode) def to_parent_directory(event): new_path = os.path.normpath(os.path.join( editor.current_editor_buffer.location, '..')) editor.window_arrangement.open_buffer( new_path, show_in_current_window=True) editor.sync_with_prompt_toolkit() return kb
def create_key_bindings(history, python_input, history_mapping): """ Key bindings. """ bindings = KeyBindings() handle = bindings.add @handle(' ', filter=has_focus(history.history_buffer)) def _(event): """ Space: select/deselect line from history pane. """ b = event.current_buffer line_no = b.document.cursor_position_row if not history_mapping.history_lines: # If we've no history, then nothing to do return if line_no in history_mapping.selected_lines: # Remove line. history_mapping.selected_lines.remove(line_no) history_mapping.update_default_buffer() else: # Add line. history_mapping.selected_lines.add(line_no) history_mapping.update_default_buffer() # Update cursor position default_buffer = history.default_buffer default_lineno = sorted(history_mapping.selected_lines).index(line_no) + \ history_mapping.result_line_offset default_buffer.cursor_position = \ default_buffer.document.translate_row_col_to_index(default_lineno, 0) # Also move the cursor to the next line. (This way they can hold # space to select a region.) b.cursor_position = b.document.translate_row_col_to_index(line_no + 1, 0) @handle(' ', filter=has_focus(DEFAULT_BUFFER)) @handle('delete', filter=has_focus(DEFAULT_BUFFER)) @handle('c-h', filter=has_focus(DEFAULT_BUFFER)) def _(event): """ Space: remove line from default pane. """ b = event.current_buffer line_no = b.document.cursor_position_row - history_mapping.result_line_offset if line_no >= 0: try: history_lineno = sorted(history_mapping.selected_lines)[line_no] except IndexError: pass # When `selected_lines` is an empty set. else: history_mapping.selected_lines.remove(history_lineno) history_mapping.update_default_buffer() help_focussed = has_focus(history.help_buffer) main_buffer_focussed = has_focus(history.history_buffer) | has_focus(history.default_buffer) @handle('tab', filter=main_buffer_focussed) @handle('c-x', filter=main_buffer_focussed, eager=True) # Eager: ignore the Emacs [Ctrl-X Ctrl-X] binding. @handle('c-w', filter=main_buffer_focussed) def _(event): " Select other window. " _select_other_window(history) @handle('f4') def _(event): " Switch between Emacs/Vi mode. " python_input.vi_mode = not python_input.vi_mode @handle('f1') def _(event): " Display/hide help. " _toggle_help(history) @handle('enter', filter=help_focussed) @handle('c-c', filter=help_focussed) @handle('c-g', filter=help_focussed) @handle('escape', filter=help_focussed) def _(event): " Leave help. " event.app.layout.focus_previous() @handle('q', filter=main_buffer_focussed) @handle('f3', filter=main_buffer_focussed) @handle('c-c', filter=main_buffer_focussed) @handle('c-g', filter=main_buffer_focussed) def _(event): " Cancel and go back. " event.app.exit(result=None) @handle('enter', filter=main_buffer_focussed) def _(event): " Accept input. " event.app.exit(result=history.default_buffer.text) enable_system_bindings = Condition(lambda: python_input.enable_system_bindings) @handle('c-z', filter=enable_system_bindings) def _(event): " Suspend to background. " event.app.suspend_to_background() return bindings
def __init__(self, history): search_toolbar = SearchToolbar() self.help_buffer_control = BufferControl( buffer=history.help_buffer, lexer=PygmentsLexer(RstLexer)) help_window = _create_popup_window( title='History Help', body=Window( content=self.help_buffer_control, right_margins=[ScrollbarMargin(display_arrows=True)], scroll_offsets=ScrollOffsets(top=2, bottom=2))) self.default_buffer_control = BufferControl( buffer=history.default_buffer, input_processors=[GrayExistingText(history.history_mapping)], lexer=PygmentsLexer(PythonLexer)) self.history_buffer_control = BufferControl( buffer=history.history_buffer, lexer=PygmentsLexer(PythonLexer), search_buffer_control=search_toolbar.control, preview_search=True) history_window = Window( content=self.history_buffer_control, wrap_lines=False, left_margins=[HistoryMargin(history)], scroll_offsets=ScrollOffsets(top=2, bottom=2)) self.root_container = HSplit([ # Top title bar. Window( content=FormattedTextControl(_get_top_toolbar_fragments), align=WindowAlign.CENTER, style='class:status-toolbar'), FloatContainer( content=VSplit([ # Left side: history. history_window, # Separator. Window(width=D.exact(1), char=BORDER.LIGHT_VERTICAL, style='class:separator'), # Right side: result. Window( content=self.default_buffer_control, wrap_lines=False, left_margins=[ResultMargin(history)], scroll_offsets=ScrollOffsets(top=2, bottom=2)), ]), floats=[ # Help text as a float. Float(width=60, top=3, bottom=2, content=ConditionalContainer( content=help_window, filter=has_focus(history.help_buffer))), ] ), # Bottom toolbars. ArgToolbar(), search_toolbar, Window( content=FormattedTextControl( partial(_get_bottom_toolbar_fragments, history=history)), style='class:status-toolbar'), ]) self.layout = Layout(self.root_container, history_window)
def load_python_bindings(python_input): """ Custom key bindings. """ bindings = KeyBindings() sidebar_visible = Condition(lambda: python_input.show_sidebar) handle = bindings.add @handle('c-l') def _(event): """ Clear whole screen and render again -- also when the sidebar is visible. """ event.app.renderer.clear() @handle('c-z') def _(event): """ Suspend. """ if python_input.enable_system_bindings: event.app.suspend_to_background() @handle('f2') def _(event): """ Show/hide sidebar. """ python_input.show_sidebar = not python_input.show_sidebar if python_input.show_sidebar: event.app.layout.focus(python_input.ptpython_layout.sidebar) else: event.app.layout.focus_last() @handle('f3') def _(event): """ Select from the history. """ python_input.enter_history() @handle('f4') def _(event): """ Toggle between Vi and Emacs mode. """ python_input.vi_mode = not python_input.vi_mode @handle('f6') def _(event): """ Enable/Disable paste mode. """ python_input.paste_mode = not python_input.paste_mode @handle('tab', filter= ~sidebar_visible & ~has_selection & tab_should_insert_whitespace) def _(event): """ When tab should insert whitespace, do that instead of completion. """ event.app.current_buffer.insert_text(' ') @Condition def is_multiline(): return document_is_multiline_python(python_input.default_buffer.document) @handle('enter', filter= ~sidebar_visible & ~has_selection & (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER) & ~is_multiline) @handle(Keys.Escape, Keys.Enter, filter= ~sidebar_visible & emacs_mode) def _(event): """ Accept input (for single line input). """ b = event.current_buffer if b.validate(): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. b.document = Document( text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.validate_and_handle() @handle('enter', filter= ~sidebar_visible & ~has_selection & (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER) & is_multiline) def _(event): """ Behaviour of the Enter key. Auto indent after newline/Enter. (When not in Vi navigaton mode, and when multiline is enabled.) """ b = event.current_buffer empty_lines_required = python_input.accept_input_on_enter or 10000 def at_the_end(b): """ we consider the cursor at the end when there is no text after the cursor, or only whitespace. """ text = b.document.text_after_cursor return text == '' or (text.isspace() and not '\n' in text) if python_input.paste_mode: # In paste mode, always insert text. b.insert_text('\n') elif at_the_end(b) and b.document.text.replace(' ', '').endswith( '\n' * (empty_lines_required - 1)): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. if b.validate(): b.document = Document( text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.validate_and_handle() else: auto_newline(b) @handle('c-d', filter=~sidebar_visible & has_focus(python_input.default_buffer) & Condition(lambda: # The current buffer is empty. not get_app().current_buffer.text)) def _(event): """ Override Control-D exit, to ask for confirmation. """ if python_input.confirm_exit: python_input.show_exit_confirmation = True else: event.app.exit(exception=EOFError) @handle('c-c', filter=has_focus(python_input.default_buffer)) def _(event): " Abort when Control-C has been pressed. " event.app.exit(exception=KeyboardInterrupt, style='class:aborting') return bindings
def __init__(self, text: str = '', multiline: FilterOrBool = True, password: FilterOrBool = False, lexer: Optional[Lexer] = None, auto_suggest: Optional[AutoSuggest] = None, completer: Optional[Completer] = None, complete_while_typing: FilterOrBool = True, accept_handler: Optional[BufferAcceptHandler] = None, history: Optional[History] = None, focusable: FilterOrBool = True, focus_on_click: FilterOrBool = False, wrap_lines: FilterOrBool = True, read_only: FilterOrBool = False, width: AnyDimension = None, height: AnyDimension = None, dont_extend_height: FilterOrBool = False, dont_extend_width: FilterOrBool = False, line_numbers: bool = False, get_line_prefix: Optional[GetLinePrefixCallable] = None, scrollbar: bool = False, style: str = '', search_field: Optional[SearchToolbar] = None, preview_search: FilterOrBool = True, prompt: AnyFormattedText = '', input_processors: Optional[List[Processor]] = None) -> None: if search_field is None: search_control = None elif isinstance(search_field, SearchToolbar): search_control = search_field.control if input_processors is None: input_processors = [] # Writeable attributes. self.completer = completer self.complete_while_typing = complete_while_typing self.lexer = lexer self.auto_suggest = auto_suggest self.read_only = read_only self.wrap_lines = wrap_lines self.buffer = Buffer( document=Document(text, 0), multiline=multiline, read_only=Condition(lambda: is_true(self.read_only)), completer=DynamicCompleter(lambda: self.completer), complete_while_typing=Condition( lambda: is_true(self.complete_while_typing)), auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), accept_handler=accept_handler, history=history) self.control = BufferControl( buffer=self.buffer, lexer=DynamicLexer(lambda: self.lexer), input_processors=[ ConditionalProcessor( AppendAutoSuggestion(), has_focus(self.buffer) & ~is_done), ConditionalProcessor( processor=PasswordProcessor(), filter=to_filter(password) ), BeforeInput(prompt, style='class:text-area.prompt'), ] + input_processors, search_buffer_control=search_control, preview_search=preview_search, focusable=focusable, focus_on_click=focus_on_click) if multiline: if scrollbar: right_margins = [ScrollbarMargin(display_arrows=True)] else: right_margins = [] if line_numbers: left_margins = [NumberedMargin()] else: left_margins = [] else: height = D.exact(1) left_margins = [] right_margins = [] style = 'class:text-area ' + style self.window = Window( height=height, width=width, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, content=self.control, style=style, wrap_lines=Condition(lambda: is_true(self.wrap_lines)), left_margins=left_margins, right_margins=right_margins, get_line_prefix=get_line_prefix)
def _create_container_for_process(pymux, window, arrangement_pane, zoom=False): """ Create a `Container` with a titlebar for a process. """ @Condition def clock_is_visible(): return arrangement_pane.clock_mode @Condition def pane_numbers_are_visible(): return pymux.display_pane_numbers terminal_is_focused = has_focus(arrangement_pane.terminal) def get_terminal_style(): if terminal_is_focused(): result = 'class:terminal.focused' else: result = 'class:terminal' return result def get_titlebar_text_fragments(): result = [] if zoom: result.append(('class:titlebar-zoom', ' Z ')) if arrangement_pane.process.is_terminated: result.append(('class:terminated', ' Terminated ')) # Scroll buffer info. if arrangement_pane.display_scroll_buffer: result.append(('class:copymode', ' %s ' % arrangement_pane.scroll_buffer_title)) # Cursor position. document = arrangement_pane.scroll_buffer.document result.append(('class:copymode.position', ' %i,%i ' % ( document.cursor_position_row, document.cursor_position_col))) if arrangement_pane.name: result.append(('class:name', ' %s ' % arrangement_pane.name)) result.append(('', ' ')) return result + [ ('', format_pymux_string(pymux, ' #T ', pane=arrangement_pane)) # XXX: Make configurable. ] def get_pane_index(): try: w = pymux.arrangement.get_active_window() index = w.get_pane_index(arrangement_pane) except ValueError: index = '/' return '%3s ' % index def on_click(): " Click handler for the clock. When clicked, select this pane. " arrangement_pane.clock_mode = False pymux.arrangement.get_active_window().active_pane = arrangement_pane pymux.invalidate() return HighlightBordersIfActive( window, arrangement_pane, get_terminal_style, FloatContainer( HSplit([ # The terminal. TracePaneWritePosition( pymux, arrangement_pane, content=arrangement_pane.terminal), ]), # floats=[ # The title bar. Float(content= ConditionalContainer( content=VSplit([ Window( height=1, content=FormattedTextControl( get_titlebar_text_fragments)), Window( height=1, width=4, content=FormattedTextControl(get_pane_index), style='class:paneindex') ], style='class:titlebar'), filter=Condition(lambda: pymux.enable_pane_status)), left=0, right=0, top=-1, height=1, z_index=Z_INDEX.WINDOW_TITLE_BAR), # The clock. Float( content=ConditionalContainer(BigClock(on_click), filter=clock_is_visible)), # Pane number. Float(content=ConditionalContainer( content=PaneNumber(pymux, arrangement_pane), filter=pane_numbers_are_visible)), ] ) )
def _build_key_bindings(self): focused = has_focus(self.system_buffer) # Emacs emacs_bindings = KeyBindings() handle = emacs_bindings.add @handle('escape', filter=focused) @handle('c-g', filter=focused) @handle('c-c', filter=focused) def _(event): " Hide system prompt. " self.system_buffer.reset() event.app.layout.focus_last() @handle('enter', filter=focused) def _(event): " Run system command. " event.app.run_system_command( self.system_buffer.text, display_before_text=self._get_display_before_text()) self.system_buffer.reset(append_to_history=True) event.app.layout.focus_last() # Vi. vi_bindings = KeyBindings() handle = vi_bindings.add @handle('escape', filter=focused) @handle('c-c', filter=focused) def _(event): " Hide system prompt. " event.app.vi_state.input_mode = InputMode.NAVIGATION self.system_buffer.reset() event.app.layout.focus_last() @handle('enter', filter=focused) def _(event): " Run system command. " event.app.vi_state.input_mode = InputMode.NAVIGATION event.app.run_system_command( self.system_buffer.text, display_before_text=self._get_display_before_text()) self.system_buffer.reset(append_to_history=True) event.app.layout.focus_last() # Global bindings. (Listen to these bindings, even when this widget is # not focussed.) global_bindings = KeyBindings() handle = global_bindings.add @handle(Keys.Escape, '!', filter= ~focused & emacs_mode, is_global=True) def _(event): " M-'!' will focus this user control. " event.app.layout.focus(self.window) @handle('!', filter=~focused & vi_mode & vi_navigation_mode, is_global=True) def _(event): " Focus. " event.app.vi_state.input_mode = InputMode.INSERT event.app.layout.focus(self.window) return merge_key_bindings([ ConditionalKeyBindings(emacs_bindings, emacs_mode), ConditionalKeyBindings(vi_bindings, vi_mode), ConditionalKeyBindings(global_bindings, self.enable_global_bindings), ])
def __init__(self, text='', multiline=True, password=False, lexer=None, auto_suggest=None, completer=None, complete_while_typing=True, accept_handler=None, history=None, focusable=True, focus_on_click=False, wrap_lines=True, read_only=False, width=None, height=None, dont_extend_height=False, dont_extend_width=False, line_numbers=False, get_line_prefix=None, scrollbar=False, style='', search_field=None, preview_search=True, prompt='', input_processors=None): # assert isinstance(text, six.text_type) if search_field is None: search_control = None if input_processors is None: input_processors = [] # Writeable attributes. self.completer = completer self.complete_while_typing = complete_while_typing self.lexer = lexer self.auto_suggest = auto_suggest self.read_only = read_only self.wrap_lines = wrap_lines self.buffer = Buffer( document=Document(text, 0), multiline=multiline, read_only=Condition(lambda: is_true(self.read_only)), completer=DynamicCompleter(lambda: self.completer), complete_while_typing=Condition( lambda: is_true(self.complete_while_typing)), auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), accept_handler=accept_handler, history=history) self.control = BufferControl( buffer=self.buffer, lexer=DynamicLexer(lambda: self.lexer), input_processors=[ ConditionalProcessor( AppendAutoSuggestion(), has_focus(self.buffer) & is_done), # ConditionalProcessor( processor=PasswordProcessor(), filter=to_filter(password) ), BeforeInput(prompt, style='class:text-area.prompt'), ] + input_processors, search_buffer_control=search_control, preview_search=preview_search, focusable=focusable, focus_on_click=focus_on_click) if multiline: if scrollbar: right_margins = [ScrollbarMargin(display_arrows=True)] else: right_margins = [] if line_numbers: left_margins = [NumberedMargin()] else: left_margins = [] else: height = D.exact(1) left_margins = [] right_margins = [] style = 'class:text-area ' + style self.window = Window( height=height, width=width, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, content=self.control, style=style, wrap_lines=Condition(lambda: is_true(self.wrap_lines)), left_margins=left_margins, right_margins=right_margins, get_line_prefix=get_line_prefix)
def _create_layout(self): """ Create `Layout` for this prompt. """ dyncond = self._dyncond # Create functions that will dynamically split the prompt. (If we have # a multiline prompt.) has_before_fragments, get_prompt_text_1, get_prompt_text_2 = \ _split_multiline_prompt(self._get_prompt) default_buffer = self.default_buffer search_buffer = self.search_buffer # Create processors list. all_input_processors = [ HighlightIncrementalSearchProcessor(), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), has_focus(default_buffer) & ~is_done), ConditionalProcessor(PasswordProcessor(), dyncond('is_password')), DisplayMultipleCursors(), # Users can insert processors here. DynamicProcessor(lambda: merge_processors(self.input_processors or [])), ] # Create bottom toolbars. bottom_toolbar = ConditionalContainer( Window(FormattedTextControl( lambda: self.bottom_toolbar, style='class:bottom-toolbar.text'), style='class:bottom-toolbar', dont_extend_height=True, height=Dimension(min=1)), filter=~is_done & renderer_height_is_known & Condition(lambda: self.bottom_toolbar is not None)) search_toolbar = SearchToolbar( search_buffer, ignore_case=dyncond('search_ignore_case')) search_buffer_control = SearchBufferControl( buffer=search_buffer, input_processors=[ ReverseSearchProcessor(), ], ignore_case=dyncond('search_ignore_case')) system_toolbar = SystemToolbar( enable_global_bindings=dyncond('enable_system_prompt')) def get_search_buffer_control(): " Return the UIControl to be focused when searching start. " if is_true(self.multiline): return search_toolbar.control else: return search_buffer_control default_buffer_control = BufferControl( buffer=default_buffer, search_buffer_control=get_search_buffer_control, input_processors=all_input_processors, include_default_input_processors=False, lexer=DynamicLexer(lambda: self.lexer), preview_search=True) default_buffer_window = Window( default_buffer_control, height=self._get_default_buffer_control_height, get_line_prefix=partial( self._get_line_prefix, get_prompt_text_2=get_prompt_text_2), wrap_lines=dyncond('wrap_lines')) @Condition def multi_column_complete_style(): return self.complete_style == CompleteStyle.MULTI_COLUMN # Build the layout. layout = HSplit([ # The main input, with completion menus floating on top of it. FloatContainer( HSplit([ ConditionalContainer( Window( FormattedTextControl(get_prompt_text_1), dont_extend_height=True), Condition(has_before_fragments) ), ConditionalContainer( default_buffer_window, Condition(lambda: get_app().layout.current_control != search_buffer_control), ), ConditionalContainer( Window(search_buffer_control), Condition(lambda: get_app().layout.current_control == search_buffer_control), ), ]), [ # Completion menus. # NOTE: Especially the multi-column menu needs to be # transparent, because the shape is not always # rectangular due to the meta-text below the menu. Float(xcursor=True, ycursor=True, transparent=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=has_focus(default_buffer) & ~multi_column_complete_style)), Float(xcursor=True, ycursor=True, transparent=True, content=MultiColumnCompletionsMenu( show_meta=True, extra_filter=has_focus(default_buffer) & multi_column_complete_style)), # The right prompt. Float(right=0, top=0, hide_when_covering_content=True, content=_RPrompt(lambda: self.rprompt)), ] ), ConditionalContainer( ValidationToolbar(), filter=~is_done), ConditionalContainer( system_toolbar, dyncond('enable_system_prompt') & ~is_done), # In multiline mode, we use two toolbars for 'arg' and 'search'. ConditionalContainer( Window(FormattedTextControl(self._get_arg_text), height=1), dyncond('multiline') & has_arg), ConditionalContainer(search_toolbar, dyncond('multiline') & ~is_done), bottom_toolbar, ]) return Layout(layout, default_buffer_window)
def __init__(self, pager): self.pager = pager self.dynamic_body = _DynamicBody(pager) # Build an interface. has_colon = HasColon(pager) self.examine_control = BufferControl( buffer=pager.examine_buffer, lexer=SimpleLexer(style='class:examine,examine-text'), input_processors=[ BeforeInput(lambda: [('class:examine', ' Examine: ')]) ]) self.search_toolbar = SearchToolbar( vi_mode=True, search_buffer=pager.search_buffer) self.container = FloatContainer( content=HSplit([ ConditionalContainer( content=Titlebar(pager), filter=Condition(lambda: pager.display_titlebar)), self.dynamic_body, self.search_toolbar, SystemToolbar(), ConditionalContainer( content=VSplit([ Window(height=1, content=FormattedTextControl(self._get_statusbar_left_tokens), style='class:statusbar'), Window(height=1, content=FormattedTextControl(self._get_statusbar_right_tokens), style='class:statusbar.cursorposition', align=WindowAlign.RIGHT), ]), filter=~HasSearch() & ~has_focus(SYSTEM_BUFFER) & ~has_colon & ~has_focus('EXAMINE')), ConditionalContainer( content=Window( FormattedTextControl(' :'), height=1, style='class:examine'), filter=has_colon), ConditionalContainer( content=Window( self.examine_control, height=1, style='class:examine'), filter=has_focus(pager.examine_buffer)), ]), floats=[ Float(right=0, height=1, bottom=1, content=_Arg()), Float(bottom=1, left=0, right=0, height=1, content=ConditionalContainer( content=MessageToolbarBar(pager), filter=Condition(lambda: bool(pager.message))) ), Float(right=0, height=1, bottom=1, content=ConditionalContainer( content=FormattedTextToolbar( lambda: [('class:loading', ' Loading... ')], ), filter=Condition(lambda: pager.current_source_info.waiting_for_input_stream))), Float(xcursor=True, ycursor=True, content=MultiColumnCompletionsMenu()), ] )
def _create_layout(self): """ Generate the main prompt_toolkit layout. """ waits_for_confirmation = WaitsForConfirmation(self.pymux) return FloatContainer( content=HSplit([ # The main window. FloatContainer( Background(), floats=[ Float(width=lambda: self.pymux.get_window_size().columns, height=lambda: self.pymux.get_window_size().rows, content=DynamicBody(self.pymux)) ]), # Status bar. ConditionalContainer( content=VSplit([ # Left. Window( height=1, width=(lambda: D(max=self.pymux.status_left_length)), dont_extend_width=True, content=FormattedTextControl(self._get_status_left_tokens)), # List of windows in the middle. Window( height=1, char=' ', align=self._get_align, content=FormattedTextControl(self._get_status_tokens)), # Right. Window( height=1, width=(lambda: D(max=self.pymux.status_right_length)), dont_extend_width=True, align=WindowAlign.RIGHT, content=FormattedTextControl(self._get_status_right_tokens)) ], z_index=Z_INDEX.STATUS_BAR, style='class:statusbar'), filter=Condition(lambda: self.pymux.enable_status), ) ]), floats=[ Float(bottom=1, left=0, z_index=Z_INDEX.MESSAGE_TOOLBAR, content=MessageToolbar(self.client_state)), Float(left=0, right=0, bottom=0, content=HSplit([ # Wait for confirmation toolbar. ConditionalContainer( content=Window( height=1, content=ConfirmationToolbar(self.pymux, self.client_state), z_index=Z_INDEX.COMMAND_LINE, ), filter=waits_for_confirmation, ), # ':' prompt toolbar. ConditionalContainer( content=Window( height=D(min=1), # Can be more if the command is multiline. style='class:commandline', dont_extend_height=True, content=BufferControl( buffer=self.client_state.command_buffer, preview_search=True, input_processors=[ AppendAutoSuggestion(), BeforeInput(':', style='class:commandline-prompt'), ShowArg(), HighlightSelectionProcessor(), ]), z_index=Z_INDEX.COMMAND_LINE, ), filter=has_focus(self.client_state.command_buffer), ), # Other command-prompt commands toolbar. ConditionalContainer( content=Window( height=1, style='class:commandline', content=BufferControl( buffer=self.client_state.prompt_buffer, input_processors=[ BeforeInput(self._before_prompt_command_tokens), AppendAutoSuggestion(), HighlightSelectionProcessor(), ]), z_index=Z_INDEX.COMMAND_LINE, ), filter=has_focus(self.client_state.prompt_buffer), ), ])), # Keys pop-up. Float( content=ConditionalContainer( content=self.popup_dialog, filter=Condition(lambda: self.client_state.display_popup), ), left=3, right=3, top=5, bottom=5, z_index=Z_INDEX.POPUP, ), Float(xcursor=True, ycursor=True, content=CompletionsMenu(max_height=12)), ] )
def create_key_bindings(pager): kb = KeyBindings() handle = kb.add @Condition def has_colon(): return pager.in_colon_mode @Condition def default_focus(): app = get_app() return app.layout.current_window == pager.current_source_info.window @Condition def displaying_help(): return pager.displaying_help for c in '01234556789': @handle(c, filter=default_focus) def _(event, c=c): event.append_to_arg_count(c) @handle('q', filter=default_focus) @handle('Q', filter=default_focus | has_colon) @handle('Z', 'Z', filter=default_focus) def _(event): " Quit. " if pager.displaying_help: pager.quit_help() else: event.app.exit() @handle(' ', filter=default_focus) @handle('f', filter=default_focus) @handle(Keys.ControlF, filter=default_focus) @handle(Keys.ControlV, filter=default_focus) def _(event): " Page down." scroll_page_down(event) @handle('b', filter=default_focus) @handle(Keys.ControlB, filter=default_focus) @handle(Keys.Escape, 'v', filter=default_focus) def _(event): " Page up." scroll_page_up(event) @handle('d', filter=default_focus) @handle(Keys.ControlD, filter=default_focus) def _(event): " Half page down." scroll_half_page_down(event) @handle('u', filter=default_focus) @handle(Keys.ControlU, filter=default_focus) def _(event): " Half page up." scroll_half_page_up(event) @handle('e', filter=default_focus) @handle('j', filter=default_focus) @handle(Keys.ControlE, filter=default_focus) @handle(Keys.ControlN, filter=default_focus) @handle(Keys.ControlJ, filter=default_focus) @handle(Keys.ControlM, filter=default_focus) @handle(Keys.Down, filter=default_focus) def _(event): " Scoll one line down." if event.arg > 1: # When an argument is given, go this amount of lines down. event.current_buffer.auto_down(count=event.arg) else: scroll_one_line_down(event) @handle('y', filter=default_focus) @handle('k', filter=default_focus) @handle(Keys.ControlY, filter=default_focus) @handle(Keys.ControlK, filter=default_focus) @handle(Keys.ControlP, filter=default_focus) @handle(Keys.Up, filter=default_focus) def _(event): " Scoll one line up." if event.arg > 1: event.current_buffer.auto_up(count=event.arg) else: scroll_one_line_up(event) @handle(Keys.Escape, 'u') def _(event): " Toggle search highlighting. " pager.highlight_search = not pager.highlight_search @handle('=', filter=default_focus) @handle(Keys.ControlG, filter=default_focus) @handle('f', filter=has_colon) def _(event): " Print the current file name. " pager.message = ' {} '.format(pager.current_source.get_name()) @handle('h', filter=default_focus & ~displaying_help) @handle('H', filter=default_focus & ~displaying_help) def _(event): " Display Help. " pager.display_help() @handle('g', filter=default_focus) @handle('<', filter=default_focus) @handle(Keys.Escape, '<', filter=default_focus) def _(event): " Go to the first line of the file. " event.current_buffer.cursor_position = 0 @handle('G', filter=default_focus) @handle('>', filter=default_focus) @handle(Keys.Escape, '>', filter=default_focus) def _(event): " Go to the last line of the file. " b = event.current_buffer b.cursor_position = len(b.text) @handle('m', Keys.Any, filter=default_focus) def _(event): " Mark current position. " source_info = pager.current_source_info source_info.marks[event.data] = ( event.current_buffer.cursor_position, source_info.window.vertical_scroll) @handle("'", Keys.Any, filter=default_focus) def _(event): " Go to a previously marked position. " go_to_mark(event, event.data) @handle(Keys.ControlX, Keys.ControlX, filter=default_focus) def _(event): " Same as '. " go_to_mark(event, '.') def go_to_mark(event, mark): b = event.current_buffer source_info = pager.current_source_info try: if mark == '^': # Start of file. cursor_pos, vertical_scroll = 0, 0 elif mark == '$': # End of file - mark. cursor_pos, vertical_scroll = len(b.text), 0 else: # Custom mark. cursor_pos, vertical_scroll = source_info.marks[mark] except KeyError: pass # TODO: show warning. else: b.cursor_position = cursor_pos source_info.window.vertical_scroll = vertical_scroll @handle('F', filter=default_focus) def _(event): " Forward forever, like 'tail -f'. " pager.forward_forever = True @handle('r', filter=default_focus) @handle('R', filter=default_focus) def _(event): event.app.renderer.clear() def search_buffer_is_empty(): " Returns True when the search buffer is empty. " return pager.search_buffer.text == '' @handle('backspace', filter=has_focus(pager.search_buffer) & Condition(search_buffer_is_empty)) def _(event): " Cancel search when backspace is pressed. " stop_search() @handle(Keys.Left, filter=default_focus) @handle(Keys.Escape, '(', filter=default_focus) def _(event): " Scroll half page to the left. " w = event.app.layout.current_window b = event.app.current_buffer if w and w.render_info: info = w.render_info amount = info.window_width // 2 # Move cursor horizontally. value = b.cursor_position - min(amount, len(b.document.current_line_before_cursor)) b.cursor_position = value # Scroll. w.horizontal_scroll = max(0, w.horizontal_scroll - amount) @handle(Keys.Right, filter=default_focus) @handle(Keys.Escape, ')', filter=default_focus) def _(event): " Scroll half page to the right. " w = event.app.layout.current_window b = event.app.current_buffer if w and w.render_info: info = w.render_info amount = info.window_width // 2 # Move the cursor first to a visible line that is long enough to # have the cursor visible after scrolling. (Otherwise, the Window # will scroll back.) xpos = w.horizontal_scroll + amount for line in info.displayed_lines: if len(b.document.lines[line]) >= xpos: b.cursor_position = b.document.translate_row_col_to_index(line, xpos) break # Scroll. w.horizontal_scroll = max(0, w.horizontal_scroll + amount) @handle(':', filter=default_focus & ~displaying_help) def _(event): pager.in_colon_mode = True @handle('n', filter=has_colon) def _(event): " Go to next file. " pager.focus_next_source() @handle('p', filter=has_colon) def _(event): " Go to previous file. " pager.focus_previous_source() @handle('e', filter=has_colon) @handle(Keys.ControlX, Keys.ControlV, filter=default_focus) def _(event): event.app.layout.focus(pager.layout.examine_control) pager.in_colon_mode = False @handle('d', filter=has_colon) def _(event): pager.remove_current_source() @handle('backspace', filter=has_colon) @handle('q', filter=has_colon) def _(event): pager.in_colon_mode = False @handle(Keys.Any, filter=has_colon) def _(event): pager.in_colon_mode = False pager.message = 'No command.' @handle(Keys.ControlC, filter=has_focus('EXAMINE')) @handle(Keys.ControlG, filter=has_focus('EXAMINE')) def _(event): " Cancel 'Examine' input. " event.app.layout.focus(pager.current_source_info.window) @handle(Keys.ControlZ, filter=Condition(lambda: suspend_to_background_supported())) def _(event): " Suspend to bakground. " event.app.suspend_to_background() return kb