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 load_key_bindings(): """ Create a KeyBindings object that contains the default key bindings. """ all_bindings = merge_key_bindings([ # Load basic bindings. load_basic_bindings(), # Load emacs bindings. load_emacs_bindings(), load_emacs_search_bindings(), # Load Vi bindings. load_vi_bindings(), load_vi_search_bindings(), ]) return merge_key_bindings([ # Make sure that the above key bindings are only active if the # currently focussed control is a `BufferControl`. For other controls, we # don't want these key bindings to intervene. (This would break "ptterm" # for instance, which handles 'Keys.Any' in the user control itself.) ConditionalKeyBindings(all_bindings, buffer_has_focus), # Active, even when no buffer has been focussed. load_mouse_bindings(), load_cpr_bindings(), ])
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_key_bindings(self, current_window, other_controls): """ Create a `KeyBindings` object that merges the `KeyBindings` from the `UIControl` with all the parent controls and the global key bindings. """ key_bindings = [] # Collect key bindings from currently focused control and all parent # controls. Don't include key bindings of container parent controls. container = current_window while container is not None: kb = container.get_key_bindings() if kb is not None: key_bindings.append(kb) if container.is_modal(): break container = self.app.layout.get_parent(container) # Add App key bindings if self.app.key_bindings: key_bindings.append(self.app.key_bindings) # Add mouse bindings. key_bindings.append( ConditionalKeyBindings(self.app._page_navigation_bindings, self.app.enable_page_navigation_bindings)) key_bindings.append(self.app._default_bindings) # Reverse this list. The current control's key bindings should come # last. They need priority. key_bindings = key_bindings[::-1] return merge_key_bindings(key_bindings)
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_key_bindings( self, current_window: Window, other_controls: List[UIControl] ) -> KeyBindingsBase: """ Create a `KeyBindings` object that merges the `KeyBindings` from the `UIControl` with all the parent controls and the global key bindings. """ key_bindings = [] collected_containers = set() # Collect key bindings from currently focused control and all parent # controls. Don't include key bindings of container parent controls. container: Container = current_window while True: collected_containers.add(container) kb = container.get_key_bindings() if kb is not None: key_bindings.append(kb) if container.is_modal(): break parent = self.app.layout.get_parent(container) if parent is None: break else: container = parent # Include global bindings (starting at the top-model container). for c in walk(container): if c not in collected_containers: kb = c.get_key_bindings() if kb is not None: key_bindings.append(GlobalOnlyKeyBindings(kb)) # Add App key bindings if self.app.key_bindings: key_bindings.append(self.app.key_bindings) # Add mouse bindings. key_bindings.append( ConditionalKeyBindings( self.app._page_navigation_bindings, self.app.enable_page_navigation_bindings, ) ) key_bindings.append(self.app._default_bindings) # Reverse this list. The current control's key bindings should come # last. They need priority. key_bindings = key_bindings[::-1] return merge_key_bindings(key_bindings)
def load_page_navigation_bindings() -> KeyBindingsBase: """ Load both the Vi and Emacs bindings for page navigation. """ # Only enable when a `Buffer` is focused, otherwise, we would catch keys # when another widget is focused (like for instance `c-d` in a # ptterm.Terminal). return ConditionalKeyBindings( merge_key_bindings([ load_emacs_page_navigation_bindings(), load_vi_page_navigation_bindings(), ]), buffer_has_focus)
def load_emacs_page_navigation_bindings() -> KeyBindingsBase: """ Key bindings, for scrolling up and down through pages. This are separate bindings, because GNU readline doesn't have them. """ key_bindings = KeyBindings() handle = key_bindings.add handle('c-v')(scroll_page_down) handle('pagedown')(scroll_page_down) handle('escape', 'v')(scroll_page_up) handle('pageup')(scroll_page_up) return ConditionalKeyBindings(key_bindings, emacs_mode)
def load_vi_page_navigation_bindings() -> KeyBindingsBase: """ Key bindings, for scrolling up and down through pages. This are separate bindings, because GNU readline doesn't have them. """ key_bindings = KeyBindings() handle = key_bindings.add handle('c-f')(scroll_forward) handle('c-b')(scroll_backward) handle('c-d')(scroll_half_page_down) handle('c-u')(scroll_half_page_up) handle('c-e')(scroll_one_line_down) handle('c-y')(scroll_one_line_up) handle('pagedown')(scroll_page_down) handle('pageup')(scroll_page_up) return ConditionalKeyBindings(key_bindings, vi_mode)
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 _create_application(self, editing_mode, erase_when_done): def dyncond(attr_name): """ Dynamically take this setting from this 'Prompt' class. `attr_name` represents an attribute name of this class. Its value can either be a boolean or a `Filter`. This returns something that can be used as either a `Filter` or `Filter`. """ @Condition def dynamic(): value = getattr(self, attr_name) return to_filter(value)() return dynamic # 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) # Create buffers list. def accept(buff): """ Accept the content of the default buffer. This is called when the validation succeeds. """ self.app.set_result(buff.document.text) # Reset content before running again. self.app.pre_run_callables.append(buff.reset) default_buffer = Buffer( name=DEFAULT_BUFFER, # Make sure that complete_while_typing is disabled when # enable_history_search is enabled. (First convert to Filter, # to avoid doing bitwise operations on bool objects.) complete_while_typing=Condition( lambda: _true(self.complete_while_typing) and not _true( self.enable_history_search) and not self.complete_style == CompleteStyle.READLINE_LIKE), validate_while_typing=dyncond('validate_while_typing'), enable_history_search=dyncond('enable_history_search'), validator=DynamicValidator(lambda: self.validator), completer=ThreadedCompleter( completer=DynamicCompleter(lambda: self.completer), in_thread=dyncond('complete_in_thread'), ), history=DynamicHistory(lambda: self.history), auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), accept_handler=accept, get_tempfile_suffix=lambda: self.tempfile_suffix) search_buffer = Buffer(name=SEARCH_BUFFER) # Create processors list. input_processor = merge_processors([ ConditionalProcessor( # By default, only highlight search when the search # input has the focus. (Note that this doesn't mean # there is no search: the Vi 'n' binding for instance # still allows to jump to the next match in # navigation mode.) HighlightSearchProcessor(preview_search=True), has_focus(search_buffer)), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), has_focus(default_buffer) & ~is_done), ConditionalProcessor(PasswordProcessor(), dyncond('is_password')), DisplayMultipleCursors(), # Users can insert processors here. DynamicProcessor(lambda: self.extra_input_processor), # For single line mode, show the prompt before the input. ConditionalProcessor( merge_processors([ BeforeInput(get_prompt_text_2), ShowArg(), ]), ~dyncond('multiline')) ]) # 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, get_search_state=lambda: default_buffer_control.get_search_state()) search_buffer_control = BufferControl(buffer=search_buffer, input_processor=merge_processors( [ ReverseSearchProcessor(), ShowArg(), ])) system_toolbar = SystemToolbar() def get_search_buffer_control(): " Return the UIControl to be focused when searching start. " if _true(self.multiline): return search_toolbar.control else: return search_buffer_control default_buffer_control = BufferControl( buffer=default_buffer, get_search_buffer_control=get_search_buffer_control, input_processor=input_processor, lexer=DynamicLexer(lambda: self.lexer), preview_search=True) default_buffer_window = Window( default_buffer_control, height=self._get_default_buffer_control_height, left_margins=[ # In multiline mode, use the window margin to display # the prompt and continuation fragments. ConditionalMargin( PromptMargin(get_prompt_text_2, self._get_continuation), filter=dyncond('multiline'), ) ], 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. Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=has_focus(default_buffer) & ~multi_column_complete_style)), Float(xcursor=True, ycursor=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, ]) # 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=Layout(layout, default_buffer_window), 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 ]), ConditionalKeyBindings( system_toolbar.get_global_key_bindings(), dyncond('enable_system_prompt')), DynamicKeyBindings(lambda: self.extra_key_bindings), ]), mouse_support=dyncond('mouse_support'), editing_mode=editing_mode, erase_when_done=erase_when_done, reverse_vi_search_direction=True, # 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, default_buffer, default_buffer_control