def CreatePromptLayout(config, lexer=None, is_password=False, get_prompt_tokens=None, get_continuation_tokens=None, get_bottom_status_tokens=None, get_bottom_toolbar_tokens=None, extra_input_processors=None, multiline=False, show_help=True, wrap_lines=True): """Create a container instance for the prompt.""" assert get_bottom_status_tokens is None or callable( get_bottom_status_tokens) assert get_bottom_toolbar_tokens is None or callable( get_bottom_toolbar_tokens) assert get_prompt_tokens is None or callable(get_prompt_tokens) assert not (config.prompt and get_prompt_tokens) multi_column_completion_menu = filters.to_cli_filter( config.multi_column_completion_menu) multiline = filters.to_cli_filter(multiline) if get_prompt_tokens is None: get_prompt_tokens = lambda _: [(token.Token.Prompt, config.prompt)] has_before_tokens, get_prompt_tokens_1, get_prompt_tokens_2 = ( shortcuts._split_multiline_prompt(get_prompt_tokens)) # pylint: disable=protected-access # TODO(b/35347840): reimplement _split_multiline_prompt to remove # protected-access. # Create processors list. input_processors = [ 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.) processors.HighlightSearchProcessor(preview_search=True), filters.HasFocus(enums.SEARCH_BUFFER)), processors.HighlightSelectionProcessor(), processors.ConditionalProcessor( processors.AppendAutoSuggestion(), filters.HasFocus(enums.DEFAULT_BUFFER) & ~filters.IsDone()), processors.ConditionalProcessor(processors.PasswordProcessor(), is_password), ] if extra_input_processors: input_processors.extend(extra_input_processors) # Show the prompt before the input using the DefaultPrompt processor. # This also replaces it with reverse-i-search and 'arg' when required. # (Only for single line mode.) # (DefaultPrompt should always be at the end of the processors.) input_processors.append( processors.ConditionalProcessor( prompt.DefaultPrompt(get_prompt_tokens_2), ~multiline)) # Create toolbars toolbars = [] if config.fixed_prompt_position: help_height = dimension.LayoutDimension.exact(config.help_lines) help_filter = (show_help & ~filters.IsDone() & filters.RendererHeightIsKnown()) else: help_height = dimension.LayoutDimension(preferred=config.help_lines, max=config.help_lines) help_filter = (show_help & UserTypingFilter & ~filters.IsDone() & filters.RendererHeightIsKnown()) toolbars.append( containers.ConditionalContainer(layout.HSplit([ layout.Window( controls.FillControl(char=screen.Char('_', token.Token.HSep)), height=dimension.LayoutDimension.exact(1)), layout.Window(help_window.HelpWindowControl( default_char=screen.Char(' ', token.Token.Toolbar)), height=help_height), ]), filter=help_filter)) if (config.bottom_status_line and get_bottom_status_tokens or config.bottom_bindings_line and get_bottom_toolbar_tokens): windows = [] windows.append( layout.Window( controls.FillControl(char=screen.Char('_', token.Token.HSep)), height=dimension.LayoutDimension.exact(1))) if config.bottom_status_line and get_bottom_status_tokens: windows.append( layout.Window(controls.TokenListControl( get_bottom_status_tokens, default_char=screen.Char(' ', token.Token.Toolbar)), height=dimension.LayoutDimension.exact(1))) if config.bottom_bindings_line and get_bottom_toolbar_tokens: windows.append( layout.Window(controls.TokenListControl( get_bottom_toolbar_tokens, default_char=screen.Char(' ', token.Token.Toolbar)), height=dimension.LayoutDimension.exact(1))) toolbars.append( containers.ConditionalContainer(layout.HSplit(windows), filter=~filters.IsDone() & filters.RendererHeightIsKnown())) def GetHeight(cli): """Determine the height for the input buffer.""" # If there is an autocompletion menu to be shown, make sure that our # layout has at least a minimal height in order to display it. if cli.config.completion_menu_lines and not cli.is_done: # Reserve the space, either when there are completions, or when # `complete_while_typing` is true and we expect completions very # soon. buf = cli.current_buffer # if UserTypingFilter(cli) or not buf.text or buf.complete_state: if UserTypingFilter(cli) or buf.complete_state: return dimension.LayoutDimension( min=cli.config.completion_menu_lines + 1) return dimension.LayoutDimension() # Create and return Container instance. return layout.HSplit([ # The main input, with completion menus floating on top of it. containers.FloatContainer( layout.HSplit([ containers.ConditionalContainer( layout.Window( controls.TokenListControl(get_prompt_tokens_1), dont_extend_height=True, wrap_lines=wrap_lines, ), filters.Condition(has_before_tokens), ), layout.Window( controls.BufferControl( input_processors=input_processors, lexer=lexer, # Enable preview_search, we want to have immediate # feedback in reverse-i-search mode. preview_search=True, ), get_height=GetHeight, left_margins=[ # In multiline mode, use the window margin to display # the prompt and continuation tokens. margins.ConditionalMargin( margins.PromptMargin(get_prompt_tokens_2, get_continuation_tokens), filter=multiline, ), ], wrap_lines=wrap_lines, ), ]), [ # Completion menus. layout.Float( xcursor=True, ycursor=True, content=menus.CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=(filters.HasFocus(enums.DEFAULT_BUFFER) & ~multi_column_completion_menu), ), ), layout.Float( ycursor=True, content=menus.MultiColumnCompletionsMenu( show_meta=True, extra_filter=(filters.HasFocus(enums.DEFAULT_BUFFER) & multi_column_completion_menu), ), ), ], ), pt_toolbars.ValidationToolbar(), pt_toolbars.SystemToolbar(), # In multiline mode, we use two toolbars for 'arg' and 'search'. containers.ConditionalContainer(pt_toolbars.ArgToolbar(), multiline), containers.ConditionalContainer(pt_toolbars.SearchToolbar(), multiline), ] + toolbars)
def __init__( self, switchboard_inst: switchboard_base.SwitchboardBase, device_log_file_name: str, configuration: console_config.ConsoleConfiguration, console_input: Optional[prompt_toolkit_input.Input] = None, console_output: Optional[output.Output] = None) -> None: """Initializes a prompt_toolkit console application. Args: switchboard_inst: Switchboard capability instance. device_log_file_name: The device log file to read (stream) logs from. configuration: Console configuration. console_input: Console input. Defaults to stdin. console_output: Console output. Defaults to stdout. """ self._device_log_file_name = device_log_file_name self._window_line_transforms = configuration.window_line_transforms self._line_to_window_id = configuration.line_to_window_id self._log_file = None # Set by run(). body_frames = [] self._window_id_to_text_area = collections.OrderedDict() for window in configuration.windows: text_area, enclosing_frame = _make_ui_window(window, switchboard_inst) self._window_id_to_text_area[window.window_id] = text_area body_frames.append(enclosing_frame) self._menu_exit_item = widgets.MenuItem("Exit", handler=_do_exit) if body_frames: self._default_focused_element = body_frames[0] else: self._default_focused_element = self._menu_exit_item help_window_visibility = _HelpWindowVisibility( focus_element_on_hide=self._default_focused_element) close_help_button = widgets.Button( text="Ok", handler=help_window_visibility.hide) help_window_visibility.focus_element_on_show = close_help_button help_float_dialog = containers.Float( containers.ConditionalContainer( content=widgets.Dialog( title="Console help", body=widgets.TextArea( text=_HELP_TEXT, focusable=True, read_only=True, scrollbar=True, width=_HELP_TEXT_WIDTH, ), buttons=[close_help_button], ), filter=filters.Condition(help_window_visibility.is_visible), ), ) root_container = widgets.MenuContainer( body=containers.FloatContainer( content=containers.HSplit(body_frames), floats=[help_float_dialog], ), menu_items=[ self._menu_exit_item, widgets.MenuItem("Help", handler=help_window_visibility.show), ], ) button_list = switchboard_inst.button_list button_names = button_list[0].valid_buttons() if button_list else [] if button_names: button_menu_items = [ _make_button_menu_item(button_name, switchboard_inst, root_container) for button_name in button_names ] self._button_dropdown = widgets.MenuItem( "Buttons", children=button_menu_items) root_container.menu_items.append(self._button_dropdown) else: self._button_dropdown = None # Global key bindings. bindings = key_binding.KeyBindings() bindings.add("tab")(focus.focus_next) bindings.add("s-tab")(focus.focus_previous) style = styles.Style.from_dict({ "menu-bar": "bg:#aaaaaa #888888", }) self._application = application.Application( layout=layout.Layout( container=root_container, focused_element=self._default_focused_element, ), style=style, full_screen=True, key_bindings=bindings, editing_mode=enums.EditingMode.VI, input=console_input, output=console_output, )