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 create_python_input_window(): def menu_position(cli): """ When there is no autocompletion menu to be shown, and we have a signature, set the pop-up position at `bracket_start`. """ b = cli.buffers[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_name=DEFAULT_BUFFER, lexer=lexer, input_processors=[ ConditionalProcessor( processor=HighlightSearchProcessor( preview_search=True), filter=HasFocus(SEARCH_BUFFER), ), HighlightSelectionProcessor(), # Show matching parentheses, but only while editing. ConditionalProcessor( processor=HighlightMatchingBracketProcessor( chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: python_input. highlight_matching_parenthesis)), ConditionalProcessor(processor=AppendAutoSuggestion(), filter=~IsDone()) ] + 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. get_height=(lambda cli: (None if cli.is_done or python_input. show_exit_confirmation else input_buffer_height)), wrap_lines=Condition(lambda cli: python_input.wrap_lines), )
def __init__(self, shell_ctx): self.shell_ctx = shell_ctx @Condition def show_default(_): return self.shell_ctx.is_showing_default @Condition def show_symbol(_): return self.shell_ctx.is_symbols @Condition def show_progress(_): progress = get_progress_message() done = get_done() return progress != '' and not done @Condition def has_default_scope(_): return self.shell_ctx.default_command == '' self.has_default_scope = has_default_scope self.show_default = show_default self.show_symbol = show_symbol self.show_progress = show_progress # TODO fix this somehow self.input_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=Always()), HasFocus(SEARCH_BUFFER)), HighlightSelectionProcessor(), ConditionalProcessor( AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & self.has_default_scope) ]
def _create_layout(self): """ Generate the main prompt_toolkit layout. """ waits_for_confirmation = WaitsForConfirmation(self.pymux) waits_for_prompt = WaitsForPrompt(self.pymux) in_command_mode = InCommandMode(self.pymux) return FloatContainer( content=HSplit([ # The main window. HighlightBorders(self, self.pymux, FloatContainer( Background(), floats=[ Float(get_width=lambda cli: self.pymux.get_window_size(cli).columns, get_height=lambda cli: self.pymux.get_window_size(cli).rows, content=TraceBodyWritePosition(self.pymux, DynamicBody(self.pymux))) ])), # Status bar. ConditionalContainer( content=VSplit([ # Left. Window( height=D.exact(1), get_width=(lambda cli: D(max=self.pymux.status_left_length)), dont_extend_width=True, content=TokenListControl( self._get_status_left_tokens, default_char=Char(' ', Token.StatusBar))), # List of windows in the middle. Window( height=D.exact(1), content=TokenListControl( self._get_status_tokens, align_right=Condition(self._status_align_right), align_center=Condition(self._status_align_center), default_char=Char(' ', Token.StatusBar))), # Right. Window( height=D.exact(1), get_width=(lambda cli: D(max=self.pymux.status_right_length)), dont_extend_width=True, content=TokenListControl( self._get_status_right_tokens, align_right=True, default_char=Char(' ', Token.StatusBar))) ]), filter=Condition(lambda cli: self.pymux.enable_status), ) ]), floats=[ Float(bottom=1, left=0, content=MessageToolbar(self.pymux)), Float(left=0, right=0, bottom=0, content=HSplit([ # Wait for confirmation toolbar. ConditionalContainer( content=Window( height=D.exact(1), content=ConfirmationToolbar(self.pymux), ), filter=waits_for_confirmation, ), # ':' prompt toolbar. ConditionalContainer( content=Window( height=D(min=1), # Can be more if the command is multiline. dont_extend_height=True, content=BufferControl( buffer_name=COMMAND, default_char=Char(' ', Token.CommandLine), lexer=SimpleLexer(Token.CommandLine), preview_search=True, highlighters=[SelectionHighlighter()], input_processors=[ AppendAutoSuggestion(), DefaultPrompt(lambda cli:[(Token.CommandLine.Prompt, ':')]), ]) ), filter=in_command_mode, ), # Other command-prompt commands toolbar. ConditionalContainer( content=Window( height=D.exact(1), content=BufferControl( buffer_name=PROMPT, default_char=Char(' ', Token.CommandLine), lexer=SimpleLexer(Token.CommandLine), highlighters=[SelectionHighlighter()], input_processors=[ BeforeInput(self._before_prompt_command_tokens), AppendAutoSuggestion(), ]) ), filter=waits_for_prompt, ), ])), Float(xcursor=True, ycursor=True, content=CompletionsMenu(max_height=12)), ] )
def __call__(self, *a, **kw): return DEFAULT_COMMAND == "" # TODO fix this somehow input_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=Always()), HasFocus(SEARCH_BUFFER)), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & HasDefaultScope()), ] # pylint: disable=too-few-public-methods class ShowDefault(Filter): """ toggle on and off seeing the default """ def __call__(self, *a, **kw): return get_show_default() # pylint: disable=too-few-public-methods class ShowSymbol(Filter): """ toggle showing the symbols """ def __call__(self, *a, **kw):
def create_prompt_layout(self, message='', lexer=None, is_password=False, reserve_space_for_menu=8, get_prompt_tokens=None, get_bottom_toolbar_tokens=None, display_completions_in_columns=False, extra_input_processors=None, multiline=False, wrap_lines=True): """Create a Container instance for a prompt. Parameters ---------- message : Text to be used as prompt. lexer : ~prompt_toolkit.layout.lexers.Lexer to be used for the highlighting. is_password : bool or ~prompt_toolkit.filters.CLIFilter. When True, display input as '*'. reserve_space_for_menu : Space to be reserved for the menu. When >0, make sure that a minimal height is allocated in the terminal, in order to display the completion menu. get_prompt_tokens : An optional callable that returns the tokens to be shown in the menu. (To be used instead of a `message`.) get_bottom_toolbar_tokens : An optional callable that returns the tokens for a toolbar at the bottom. display_completions_in_columns : `bool` or :class:`~prompt_toolkit.filters.CLIFilter`. Display the completions in multiple columns. multiline : `bool` or :class:`~prompt_toolkit.filters.CLIFilter`. When True, prefer a layout that is more adapted for multiline input. Text after newlines is automatically indented, and search/arg input is shown below the input, instead of replacing the prompt. wrap_lines : `bool` or :class:`~prompt_toolkit.filters.CLIFilter`. When True (the default), automatically wrap long lines instead of scrolling horizontally. Notes ----- This method was forked from the mainline prompt-toolkit repo. Copyright (c) 2014, Jonathan Slenders, All rights reserved. WARNING; This method is due for removal once prompt-toolkit >v0.54 is released. """ assert isinstance(message, str) 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 (message and get_prompt_tokens) display_completions_in_columns = to_cli_filter( display_completions_in_columns) multiline = to_cli_filter(multiline) if get_prompt_tokens is None: get_prompt_tokens = lambda _: [(Token.Prompt, message)] get_prompt_tokens_1, get_prompt_tokens_2 = _split_multiline_prompt( get_prompt_tokens) # `lexer` is supposed to be a `Lexer` instance. But if a Pygments lexer # class is given, turn it into a PygmentsLexer. (Important for # backwards-compatibility.) try: if issubclass(lexer, pygments.lexer.Lexer): lexer = PygmentsLexer(lexer) except TypeError: # Happens when lexer is `None` or an instance of something else. pass # Create highlighters and processors list. if ConditionalHighlighter is None: highlighters = None highlighters_kwargs = {} else: highlighters = [ ConditionalHighlighter( # 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.) SearchHighlighter(preview_search=True), HasFocus(SEARCH_BUFFER)), SelectionHighlighter() ] highlighters_kwargs = {'highlighters': highlighters} input_processors = [ ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & ~IsDone()), ConditionalProcessor(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( ConditionalProcessor(DefaultPrompt(get_prompt_tokens), ~multiline)) # Create bottom toolbar. if get_bottom_toolbar_tokens: toolbars = [ ConditionalContainer( Window(TokenListControl(get_bottom_toolbar_tokens, default_char=Char( ' ', Token.Toolbar)), height=LayoutDimension.exact(1)), filter=~IsDone() & RendererHeightIsKnown()) ] else: toolbars = [] def get_height(cli): # 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 reserve_space_for_menu and not cli.is_done: return LayoutDimension(min=reserve_space_for_menu) else: return LayoutDimension() # Create and return Container instance. return HSplit([ ConditionalContainer( Window(TokenListControl(get_prompt_tokens_1), dont_extend_height=True), filter=multiline, ), VSplit([ # In multiline mode, the prompt is displayed in a left pane. ConditionalContainer( Window( TokenListControl(get_prompt_tokens_2), dont_extend_width=True, ), filter=multiline, ), # The main input, with completion menus floating on top of it. FloatContainer( Window( BufferControl( input_processors=input_processors, lexer=lexer, wrap_lines=wrap_lines, # Enable preview_search, we want to have immediate feedback # in reverse-i-search mode. preview_search=True, **highlighters_kwargs), get_height=get_height, ), [ Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=HasFocus(DEFAULT_BUFFER) & ~display_completions_in_columns)), Float(xcursor=True, ycursor=True, content=MultiColumnCompletionsMenu( extra_filter=HasFocus(DEFAULT_BUFFER) & display_completions_in_columns, show_meta=True)) ]), ]), ValidationToolbar(), SystemToolbar(), # In multiline mode, we use two toolbars for 'arg' and 'search'. ConditionalContainer(ArgToolbar(), multiline), ConditionalContainer(SearchToolbar(), multiline), ] + toolbars)
def _create_layout(self, message='', lexer=None, is_password=False, reserve_space_for_menu=8, get_prompt_tokens=None, get_continuation_tokens=None, get_rprompt_tokens=None, get_bottom_toolbar_tokens=None, get_url_tokens=None, display_completions_in_columns=False, extra_input_processors=None, multiline=False, wrap_lines=True): """ Create a :class:`.Container` instance for a prompt. :param message: Text to be used as prompt. :param lexer: :class:`~prompt_toolkit.layout.lexers.Lexer` to be used for the highlighting. :param is_password: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`. When True, display input as '*'. :param reserve_space_for_menu: Space to be reserved for the menu. When >0, make sure that a minimal height is allocated in the terminal, in order to display the completion menu. :param get_prompt_tokens: An optional callable that returns the tokens to be shown in the menu. (To be used instead of a `message`.) :param get_continuation_tokens: An optional callable that takes a CommandLineInterface and width as input and returns a list of (Token, text) tuples to be used for the continuation. :param get_bottom_toolbar_tokens: An optional callable that returns the tokens for a toolbar at the bottom. :param display_completions_in_columns: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`. Display the completions in multiple columns. :param multiline: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`. When True, prefer a layout that is more adapted for multiline input. Text after newlines is automatically indented, and search/arg input is shown below the input, instead of replacing the prompt. :param wrap_lines: `bool` or :class:`~prompt_toolkit.filters.CLIFilter`. When True (the default), automatically wrap long lines instead of scrolling horizontally. """ 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 get_rprompt_tokens is None or callable(get_rprompt_tokens) assert not (message and get_prompt_tokens) display_completions_in_columns = to_cli_filter( display_completions_in_columns) multiline = to_cli_filter(multiline) if get_prompt_tokens is None: get_prompt_tokens = lambda _: [(Token.Prompt, message)] has_before_tokens, get_prompt_tokens_1, get_prompt_tokens_2 = \ _split_multiline_prompt(get_prompt_tokens) # `lexer` is supposed to be a `Lexer` instance. But if a Pygments lexer # class is given, turn it into a PygmentsLexer. (Important for # backwards-compatibility.) try: if pygments_Lexer and issubclass(lexer, pygments_Lexer): lexer = PygmentsLexer(lexer, sync_from_start=True) except TypeError: # Happens when lexer is `None` or an instance of something else. pass # Create processors list. input_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), HasFocus(SEARCH_BUFFER)), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & ~IsDone()), ConditionalProcessor(PasswordProcessor(), is_password), DisplayMultipleCursors(DEFAULT_BUFFER), ] 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( ConditionalProcessor(DefaultPrompt(get_prompt_tokens_2), ~multiline)) # Create bottom toolbar. if get_bottom_toolbar_tokens: toolbars = [ ConditionalContainer(VSplit([ Window(TokenListControl(get_url_tokens, default_char=Char( ' ', Token.Toolbar)), height=LayoutDimension.exact(1)), Window(TokenListControl(get_bottom_toolbar_tokens, default_char=Char( ' ', Token.Toolbar), align_right=True), height=LayoutDimension.exact(1)) ]), filter=~IsDone() & RendererHeightIsKnown()) ] else: toolbars = [] def get_height(cli): # 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 reserve_space_for_menu and not cli.is_done: buff = cli.current_buffer # Reserve the space, either when there are completions, or when # `complete_while_typing` is true and we expect completions very # soon. if buff.complete_while_typing( ) or buff.complete_state is not None: return LayoutDimension(min=reserve_space_for_menu) return LayoutDimension() # Create and return Container instance. return HSplit([ # The main input, with completion menus floating on top of it. FloatContainer( HSplit([ ConditionalContainer( Window(TokenListControl(get_prompt_tokens_1), dont_extend_height=True), Condition(has_before_tokens)), Window( 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=get_height, left_margins=[ # In multiline mode, use the window margin to display # the prompt and continuation tokens. ConditionalMargin(PromptMargin( get_prompt_tokens_2, get_continuation_tokens), filter=multiline) ], wrap_lines=wrap_lines, ), ]), [ # Completion menus. Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=HasFocus(DEFAULT_BUFFER) & ~display_completions_in_columns)), Float(xcursor=True, ycursor=True, content=MultiColumnCompletionsMenu( extra_filter=HasFocus(DEFAULT_BUFFER) & display_completions_in_columns, show_meta=True)), # The right prompt. Float(right=0, top=0, hide_when_covering_content=True, content=_RPrompt(get_rprompt_tokens)), ]), ValidationToolbar(), SystemToolbar(), # In multiline mode, we use two toolbars for 'arg' and 'search'. ConditionalContainer(ArgToolbar(), multiline), ConditionalContainer(SearchToolbar(), multiline), ] + toolbars)
def __init__(self, my_app: "sqlApp") -> None: self.my_app = my_app self.search_field = SearchToolbar() history_file = config_location() + 'history' ensure_dir_exists(history_file) hist = ThreadedHistory(FileHistory(expanduser(history_file))) self.input_buffer = Buffer( name="defaultbuffer", tempfile_suffix=".py", multiline=MultilineFilter(self.my_app), history=hist, completer=DynamicCompleter( lambda: ThreadedCompleter(self.my_app.completer)), # lambda: self.my_app.completer), auto_suggest=ThreadedAutoSuggest(AutoSuggestFromHistory()), complete_while_typing=Condition( lambda: self.my_app.active_conn is not None)) main_win_control = BufferControl( buffer=self.input_buffer, lexer=PygmentsLexer(SqlLexer), search_buffer_control=self.search_field.control, include_default_input_processors=False, input_processors=[AppendAutoSuggestion()], preview_search=True) self.main_win = Window( main_win_control, height=( lambda: (None if get_app().is_done else (Dimension(min=self.my_app.min_num_menu_lines) if not self.my_app.show_preview else Dimension( min=self.my_app.min_num_menu_lines, preferred=180)))), get_line_prefix=partial(sql_line_prefix, my_app=self.my_app), scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4)) self.lprompt = login_prompt(self.my_app) self.preview = preview_element(self.my_app) self.disconnect_dialog = disconnect_dialog(self.my_app) container = HSplit([ VSplit([ FloatContainer( content=HSplit([ self.main_win, self.search_field, ]), floats=[ Float( bottom=1, left=1, right=0, content=sql_sidebar_help(self.my_app), ), Float(content=self.lprompt), Float(content=self.preview, ), Float(content=self.disconnect_dialog, ), Float(left=2, bottom=1, content=exit_confirmation(self.my_app)), Float(xcursor=True, ycursor=True, transparent=True, content=CompletionsMenu(scroll_offset=1, max_height=16, extra_filter=has_focus( self.input_buffer))) ]), ConditionalContainer( content=sql_sidebar(self.my_app), filter=ShowSidebar(self.my_app) & ~is_done, ) ]), VSplit([ status_bar(self.my_app), show_sidebar_button_info(self.my_app) ]) ]) def accept(buff): app = get_app() app.exit(result=["non-preview", buff.text]) app.pre_run_callables.append(buff.reset) return True self.input_buffer.accept_handler = accept self.layout = Layout(container, focused_element=self.main_win)
def browse(): """ A browser for the bibmanager database. """ # Content of the text buffer: bibs = bm.load() keys = [bib.key for bib in bibs] all_compact_text = "\n".join(keys) all_expanded_text = "\n\n".join(bib.meta() + bib.content for bib in bibs) # A list object, since I want this to be a global variable selected_content = [None] lex_style = style_from_pygments_cls( pygments.styles.get_style_by_name(cm.get('style'))) custom_style = Style.from_dict({ "status": "reverse", "status.position": "#aaaa00", "status.key": "#ffaa00", "shadow": "bg:#440044", "not-searching": "#888888", }) style = merge_styles([lex_style, custom_style]) def get_menubar_text(): return [ ("class:status", " ("), ("class:status.key", "enter"), ("class:status", ")select entry ("), ("class:status.key", "e"), ("class:status", ")xpand entry ("), ("class:status.key", "f"), ("class:status", ")ind ("), ("class:status.key", "s"), ("class:status", ")ave ("), ("class:status.key", "h"), ("class:status", ")elp ("), ("class:status.key", "q"), ("class:status", ")uit"), ] def get_menubar_right_text(): """Get index of entry under cursor.""" key = get_current_key(text_field.buffer.document, keys) return f" {keys.index(key) + 1} " def get_infobar_text(): """Get author-year-title of entry under cursor.""" key = get_current_key(text_field.buffer.document, keys) bib = bibs[keys.index(key)] year = '' if bib.year is None else bib.year title = 'NO_TITLE' if bib.title is None else bib.title return f"{bib.get_authors('ushort')}{year}: {title}" search_buffer = Buffer(completer=WordCompleter(keys), complete_while_typing=False, multiline=False) literal_search_field = SearchToolbar( search_buffer=search_buffer, forward_search_prompt="Text search: ", backward_search_prompt="Text search backward: ", ignore_case=False) # Entry search bar: authors_list = [bib.authors for bib in bibs] firsts = sorted( set([ u.get_authors([authors[0]], format='ushort') for authors in authors_list if authors is not None ])) firsts = [ '^{' + first + '}' if ' ' in first else '^' + first for first in firsts ] lasts = sorted( set([ u.get_authors([author], format='ushort') for authors in authors_list if authors is not None for author in authors ])) lasts = ['{' + last + '}' if ' ' in last else last for last in lasts] bibkeys = [bib.key for bib in bibs] bibcodes = [bib.bibcode for bib in bibs if bib.bibcode is not None] bibyears = sorted( set([str(bib.year) for bib in bibs if bib.year is not None])) titles = [bib.title for bib in bibs] tags = sorted( set( itertools.chain( *[bib.tags for bib in bibs if bib.tags is not None]))) key_words = { 'author:"^"': firsts, 'author:""': lasts, 'year:': bibyears, 'title:""': titles, 'key:': bibkeys, 'bibcode:': bibcodes, 'tags:': tags, } completer = u.DynamicKeywordCompleter(key_words) suggester = u.DynamicKeywordSuggester() auto_suggest_bindings = load_auto_suggest_bindings() # Searcher: entry_search_buffer = Buffer( completer=completer, complete_while_typing=False, auto_suggest=suggester, ) def get_line_prefix(lineno, wrap_count): return FormattedText([ ('bold', 'Entry search: '), ]) entry_search_field = Window(BufferControl( buffer=entry_search_buffer, input_processors=[AppendAutoSuggestion()], ), get_line_prefix=get_line_prefix, height=1) # Wrap in conditional container to display it only when focused: entry_search_focus = Condition( lambda: get_app().layout.current_window == entry_search_field) entry_search_container = ConditionalContainer( content=entry_search_field, filter=entry_search_focus, ) text_field = TextArea( text=all_compact_text, lexer=PygmentsLexer(BibTeXLexer), scrollbar=True, line_numbers=False, read_only=True, search_field=literal_search_field, input_processors=[HighlightEntryProcessor()], ) text_field.buffer.name = 'text_area_buffer' text_field.is_expanded = False text_field.compact_text = all_compact_text text_field.expanded_text = all_expanded_text # Shortcut to HighlightEntryProcessor: for processor in text_field.control.input_processors: if processor.__class__.__name__ == 'HighlightEntryProcessor': text_field.bm_processor = processor # Do not highlight searched text: sp = text_field.control.default_input_processors[0] sp._classname = ' ' sp._classname_current = ' ' menu_bar = VSplit( [ Window(FormattedTextControl(get_menubar_text), style="class:status"), Window(FormattedTextControl(get_menubar_right_text), style="class:status.right", width=9, align=WindowAlign.RIGHT), ], height=1, ) info_bar = ConditionalContainer( content=Window( content=FormattedTextControl(get_infobar_text), height=D.exact(1), style="class:status", ), filter=~entry_search_focus, ) body = HSplit([ menu_bar, text_field, literal_search_field, entry_search_container, info_bar, ]) root_container = FloatContainer( content=body, floats=[ Float( xcursor=True, ycursor=True, content=CompletionsMenu(max_height=16, scroll_offset=1), ), ], ) # Key bindings: bindings = KeyBindings() text_focus = Condition( lambda: get_app().layout.current_window == text_field.window) dialog_focus = Condition( lambda: hasattr(get_app().layout.current_window, 'dialog')) @bindings.add("q", filter=text_focus) def _quit(event): event.app.exit() # Navigation: @bindings.add("g", filter=text_focus) def _go_to_first_line(event): event.current_buffer.cursor_position = 0 @bindings.add("G", filter=text_focus) def _go_to_last_line(event) -> None: event.current_buffer.cursor_position = len(event.current_buffer.text) @bindings.add("d", filter=text_focus) def _scroll_down(event): scroll_half_page_down(event) @bindings.add("u", filter=text_focus) def _scroll_up(event): scroll_half_page_up(event) @bindings.add("n", filter=text_focus) def _find_next(event): search_state = event.app.current_search_state event.current_buffer.apply_search(search_state, include_current_position=False, count=event.arg) @bindings.add("N", filter=text_focus) def _find_previous(event): search_state = event.app.current_search_state event.current_buffer.apply_search(~search_state, include_current_position=False, count=event.arg) @bindings.add("h", filter=text_focus) def _show_help(event): show_message("Shortcuts", help_message) @bindings.add("f", filter=text_focus) def _start_literal_search(event): search.start_search(direction=search.SearchDirection.FORWARD) # TBD: Remove 't' binding no before 17/12/2022 @bindings.add("t", filter=text_focus) @bindings.add("k", filter=text_focus) def _start_entry_search(event): text_field.current_key = get_current_key(event.current_buffer.document, keys) event.app.layout.focus(entry_search_field) @bindings.add("b", filter=text_focus) def _open_in_browser(event): key = get_current_key(event.current_buffer.document, keys) bib = bm.find(key=key, bibs=bibs) if bib.adsurl is not None: webbrowser.open(bib.adsurl, new=2) else: show_message("Message", f"Entry '{key}' does not have an ADS url.") @bindings.add("c-c", filter=dialog_focus) def _close_dialog(event): get_app().layout.current_window.dialog.future.set_result(None) @bindings.add("s", filter=text_focus) def _save_selected_to_file(event): selected = text_field.bm_processor.selected_entries if len(selected) == 0: show_message("Message", "Nothing to save.") return async def coroutine(): dialog = TextInputDialog( title="Save to File", label_text="\nEnter a file path or leave blank to quit " "and print to screen:\n(press Control-c to cancel)\n", completer=PathCompleter(), ) path = await show_dialog_as_float(dialog) content = '\n\n'.join(bibs[keys.index(key)].content for key in selected) if path == "": selected_content[0] = content # The program termination is in TextInputDialog() since I # need to close this coroutine first. return if path is not None: try: with open(path, "w") as f: f.write(content) except IOError as e: show_message("Error", str(e)) ensure_future(coroutine()) @bindings.add("enter", filter=text_focus) def _toggle_selected_entry(event): "Select/deselect entry pointed by the cursor." key = get_current_key(event.current_buffer.document, keys) text_field.bm_processor.toggle_selected_entry(key) @bindings.add("enter", filter=entry_search_focus) def _select_entries(event): "Parse the input tag text and send focus back to main text." # Reset tag text to '': doc = event.current_buffer.document start_pos = doc.cursor_position + doc.get_start_of_line_position() event.current_buffer.cursor_position = start_pos event.current_buffer.delete(doc.get_end_of_line_position() - doc.get_start_of_line_position()) # Catch text and parse search text: matches = u.parse_search(doc.current_line) if len(matches) == 0: text_field.compact_text = all_compact_text[:] text_field.expanded_text = all_expanded_text[:] search_buffer.completer.words = keys else: text_field.compact_text = "\n".join([bib.key for bib in matches]) text_field.expanded_text = "\n\n".join(bib.meta() + bib.content for bib in matches) search_buffer.completer.words = [bib.key for bib in matches] # Return focus to main text: event.app.layout.focus(text_field.window) # Update main text with selected tag: buffer = event.current_buffer text_field.text = text_field.compact_text if text_field.current_key in search_buffer.completer.words: buffer_position = text_field.text.index(text_field.current_key) else: buffer_position = 0 buffer.cursor_position = buffer_position text_field.is_expanded = False # TBD: Remove 'T' binding no before 17/12/2022 @bindings.add("T", filter=text_focus) @bindings.add("K", filter=text_focus) def _deselect_tags(event): buffer = event.current_buffer key = get_current_key(buffer.document, keys) text_field.compact_text = all_compact_text[:] text_field.expanded_text = all_expanded_text[:] search_buffer.completer.words = keys # Update main text: text_field.text = text_field.compact_text buffer.cursor_position = buffer.text.index(key) text_field.is_expanded = False @bindings.add("e", filter=text_focus) def _expand_collapse_entry(event): "Expand/collapse current entry." doc = event.current_buffer.document key, start_end, is_expanded = get_current_key(doc, keys, get_start_end=True, get_expanded=True) bib = bm.find(key=key, bibs=bibs) if is_expanded: # Remove blank lines around if surrounded by keys: start_row, _ = doc._find_line_start_index(start_end[0]) if start_row > 0 and doc.lines[start_row - 2] in keys: start_end[0] -= 1 end_row, _ = doc._find_line_start_index(start_end[1]) if end_row < doc.line_count - 1 and doc.lines[end_row + 2] in keys: start_end[1] += 1 event.app.clipboard.set_text(bib.key) else: expanded_content = bib.meta() + bib.content row = doc.cursor_position_row # Add blank lines around if surrounded by keys: if row > 0 and doc.lines[row - 1] != '': expanded_content = '\n' + expanded_content if row < doc.line_count - 1 and doc.lines[row + 1] != '': expanded_content = expanded_content + '\n' event.app.clipboard.set_text(expanded_content) text_field.read_only = False event.current_buffer.cursor_position = start_end[0] event.current_buffer.delete(count=start_end[1] - start_end[0]) event.current_buffer.paste_clipboard_data( event.app.clipboard.get_data(), count=event.arg, paste_mode=PasteMode.VI_BEFORE) text_field.read_only = True if is_expanded: event.current_buffer.cursor_position = start_end[0] @bindings.add("E", filter=text_focus) def _expand_collapse_all(event): "Expand/collapse all entries." buffer = event.current_buffer key = get_current_key(buffer.document, keys) if text_field.is_expanded: text_field.text = text_field.compact_text else: text_field.text = text_field.expanded_text buffer.cursor_position = buffer.text.index(key) text_field.is_expanded = not text_field.is_expanded @bindings.add("o", filter=text_focus) def _open_pdf(event): buffer = event.current_buffer key = get_current_key(buffer.document, keys) bib = bm.find(key=key, bibs=bibs) has_pdf = bib.pdf is not None has_bibcode = bib.bibcode is not None is_missing = has_pdf and not os.path.exists(f'{u.BM_PDF()}{bib.pdf}') if not has_pdf and not has_bibcode: show_message("Message", f"BibTeX entry '{key}' does not have a PDF.") return if has_pdf and not is_missing: pm.open(key=key) return if has_pdf and is_missing and not has_bibcode: show_message( "Message", f"BibTeX entry has a PDF file: {bib.pdf}, but the file " "could not be found.") return # Need to fetch before opening: async def coroutine(): dialog = MessageDialog( "PDF file not found", "Fetch from ADS?\n(might take a few seconds ...)", asking=True) fetch = await show_dialog_as_float(dialog) if fetch: with io.StringIO() as buf, redirect_stdout(buf): fetched = pm.fetch(bib.bibcode, replace=True) fetch_output = buf.getvalue() if fetched is None: show_message("PDF fetch failed", fetch_output) else: show_message("PDF fetch succeeded.", fetch_output) pm.open(key=key) ensure_future(coroutine()) key_bindings = merge_key_bindings([ auto_suggest_bindings, bindings, ]) application = Application( layout=Layout(root_container, focused_element=text_field), key_bindings=key_bindings, enable_page_navigation_bindings=True, style=style, full_screen=True, ) application.run() if selected_content[0] is not None: tokens = list(pygments.lex(selected_content[0], lexer=BibTeXLexer())) print_formatted_text( PygmentsTokens(tokens), end="", style=lex_style, #output=create_output(sys.stdout), )
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, max_line_count=1000, initial_text="", align=WindowAlign.LEFT): assert isinstance(text, six.text_type) assert search_field is None or isinstance(search_field, SearchToolbar) 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.max_line_count = max_line_count self.buffer = CustomBuffer( 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: 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, align=align) self.log_lines: Deque[str] = deque() self.log(initial_text)
"Press '/' to start searching.")]) output_field = TextArea(style='class:output-field', scrollbar=True, line_numbers=True, text=help_text, search_field=search_field, lexer=PygmentsLexer(JsonLexer)) input_field = UrlArea(height=3, prompt='>>> ', style='class:input-field', completer=http_completer, accept_handler=True) input_field.control.input_processors.append(AppendAutoSuggestion()) container = HSplit([ Label('Request URL'), HSplit([Frame(input_field), CompletionsToolbar()]), HSplit([output_field, search_field]) ]) root_container = VSplit([ Window(width=10, content=BufferControl(buffer=buffer)), Window(width=1, char='|'), container, ArgToolbar(), SystemToolbar(), ValidationToolbar(), ])
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 __init__(self, multiline: FilterOrBool = True, password: FilterOrBool = False, focusable: FilterOrBool = True, focus_on_click: 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, key_bindings: KeyBindings = None, **kwargs) -> None: super().__init__(multiline=multiline, password=password, focusable=focusable, focus_on_click=focus_on_click, width=width, height=height, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, line_numbers=line_numbers, get_line_prefix=get_line_prefix, scrollbar=scrollbar, style=style, search_field=search_field, preview_search=preview_search, prompt=prompt, input_processors=input_processors, **kwargs) 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 = [] 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, key_bindings=key_bindings ) if multiline: right_margins = [ScrollbarMargin(display_arrows=True)] \ if scrollbar else [] left_margins = [NumberedMargin()] if line_numbers else [] 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 __init__(self, my_app: "sqlApp"): self.my_app = my_app help_text = """ Press Enter in the input box to page through the table. Alternatively, enter a filtering SQL statement and then press Enter to page through the results. """ self.formatter = TabularOutputFormatter() self.completer = PreviewCompleter( my_app=self.my_app, completer=MssqlCompleter( smart_completion=True, get_conn=lambda: self.my_app.selected_object.conn)) history_file = config_location() + 'preview_history' ensure_dir_exists(history_file) hist = PreviewHistory(my_app=self.my_app, filename=expanduser(history_file)) self.input_buffer = PreviewBuffer( name="previewbuffer", tempfile_suffix=".sql", history=ThreadedHistory(hist), auto_suggest=ThreadedAutoSuggest( PreviewSuggestFromHistory(my_app)), completer=ThreadedCompleter(self.completer), # history = hist, # auto_suggest = PreviewSuggestFromHistory(my_app), # completer = self.completer, complete_while_typing=Condition( lambda: self.my_app.selected_object is not None and self.my_app .selected_object.conn.connected()), multiline=False) input_control = BufferControl( buffer=self.input_buffer, include_default_input_processors=False, input_processors=[AppendAutoSuggestion()], preview_search=False) self.input_window = Window(input_control) search_buffer = Buffer(name="previewsearchbuffer") self.search_field = SearchToolbar(search_buffer) self.output_field = TextArea( style="class:preview-output-field", text=help_text, height=D(preferred=50), search_field=self.search_field, wrap_lines=False, focusable=True, read_only=True, preview_search=True, input_processors=[ ConditionalProcessor( processor=HighlightIncrementalSearchProcessor(), filter=has_focus("previewsearchbuffer") | has_focus(self.search_field.control), ), HighlightSelectionProcessor(), ]) def refresh_results(window_height) -> bool: """ This method gets called when the app restarts after exiting for execution of preview query. It populates the output buffer with results from the fetch/query. """ sql_conn = self.my_app.selected_object.conn if sql_conn.execution_status == executionStatus.FAIL: # Let's display the error message to the user output = sql_conn.execution_err else: crsr = sql_conn.cursor if crsr.description: cols = [col.name for col in crsr.description] else: cols = [] if len(cols): res = sql_conn.fetch_from_cache(size=window_height - 4, wait=True) output = self.formatter.format_output(res, cols, format_name="psql") output = "\n".join(output) else: output = "No rows returned\n" # Add text to output buffer. self.output_field.buffer.set_document( Document(text=output, cursor_position=0), True) return True def accept(buff: Buffer) -> bool: """ This method gets called when the user presses enter/return in the filter box. It is interpreted as either 'execute query' or 'fetch next page of results' if filter query hasn't changed. """ obj = self.my_app.selected_object sql_conn = obj.conn identifier = object_to_identifier(obj) query = sql_conn.preview_query( name=identifier, obj_type=obj.otype, filter_query=buff.text, limit=self.my_app.preview_limit_rows) if query is None: return True func = partial(refresh_results, window_height=self.output_field.window.render_info. window_height) if sql_conn.query != query: # Exit the app to execute the query self.my_app.application.exit(result=["preview", query]) self.my_app.application.pre_run_callables.append(func) else: # No need to exit let's just go and fetch func() return True # Keep filter text def cancel_handler() -> None: sql_conn = self.my_app.selected_object.conn sql_conn.close_cursor() self.input_buffer.text = "" self.output_field.buffer.set_document( Document(text=help_text, cursor_position=0), True) self.my_app.show_preview = False self.my_app.show_sidebar = True self.my_app.application.layout.focus(self.input_buffer) self.my_app.application.layout.focus("sidebarbuffer") return None self.input_buffer.accept_handler = accept self.cancel_button = Button(text="Done", handler=cancel_handler)
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
return DEFAULT_COMMAND == "" # TODO fix this somehow input_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=Always()), HasFocus(SEARCH_BUFFER)), HighlightSelectionProcessor(), ConditionalProcessor( AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & HasDefaultScope()), ] # pylint: disable=too-few-public-methods class ShowDefault(Filter): """ toggle on and off seeing the default """ def __init__(self, shell_ctx): self.shell_ctx = shell_ctx def __call__(self, *a, **kw): return self.shell_ctx.is_showing_default # pylint: disable=too-few-public-methods class ShowSymbol(Filter):
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_default_layout(app, message='', lexer=None, is_password=False, reserve_space_for_menu=False, get_prompt_tokens=None, get_bottom_toolbar_tokens=None, display_completions_in_columns=False, extra_input_processors=None, multiline=False): """ Generate default layout. Returns a ``Layout`` instance. :param message: Text to be used as prompt. :param lexer: Lexer to be used for the highlighting. :param is_password: `bool` or `CLIFilter`. When True, display input as '*'. :param reserve_space_for_menu: When True, make sure that a minimal height is allocated in the terminal, in order to display the completion menu. :param get_prompt_tokens: An optional callable that returns the tokens to be shown in the menu. (To be used instead of a `message`.) :param get_bottom_toolbar_tokens: An optional callable that returns the tokens for a toolbar at the bottom. :param display_completions_in_columns: `bool` or `CLIFilter`. Display the completions in multiple columns. :param multiline: `bool` or `CLIFilter`. When True, prefer a layout that is more adapted for multiline input. Text after newlines is automatically indented, and search/arg input is shown below the input, instead of replacing the prompt. """ assert isinstance(message, text_type) 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 (message and get_prompt_tokens) display_completions_in_columns = to_cli_filter( display_completions_in_columns) multiline = to_cli_filter(multiline) if get_prompt_tokens is None: get_prompt_tokens = lambda _: [(Token.Prompt, message)] get_prompt_tokens_1, get_prompt_tokens_2 = _split_multiline_prompt( get_prompt_tokens) # `lexer` is supposed to be a `Lexer` instance. But if a Pygments lexer # class is given, turn it into a PygmentsLexer. (Important for # backwards-compatibility.) try: if issubclass(lexer, Lexer): lexer = PygmentsLexer(lexer) except TypeError: # Happens when lexer is `None` or an instance of something else. pass # Create processors list. # (DefaultPrompt should always be at the end.) input_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=Always()), HasFocus(SEARCH_BUFFER)), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & ~IsDone()), ConditionalProcessor(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.) input_processors.append( ConditionalProcessor(DefaultPrompt(get_prompt_tokens), ~multiline)) # Create bottom toolbar. if get_bottom_toolbar_tokens: toolbars = [ ConditionalContainer(Window(TokenListControl( get_bottom_toolbar_tokens, default_char=Char(' ', Token.Toolbar)), height=LayoutDimension.exact(1)), filter=~IsDone() & RendererHeightIsKnown()) ] else: toolbars = [] def get_height(cli): # 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 reserve_space_for_menu and not cli.is_done: return LayoutDimension(min=8) else: return LayoutDimension() def separator(): return ConditionalContainer(content=Window( height=LayoutDimension.exact(1), content=FillControl(u'\u2500', token=Token.Separator)), filter=HasDocumentation(app) & ~IsDone()) # Create and return Layout instance. return HSplit([ ConditionalContainer( Window(TokenListControl(get_prompt_tokens_1), dont_extend_height=True), filter=multiline, ), VSplit([ # In multiline mode, the prompt is displayed in a left pane. ConditionalContainer( Window( TokenListControl(get_prompt_tokens_2), dont_extend_width=True, ), filter=multiline, ), # The main input, with completion menus floating on top of it. FloatContainer( Window( BufferControl( input_processors=input_processors, lexer=lexer, # Enable preview_search, we want to have immediate # feedback in reverse-i-search mode. preview_search=Always(), focus_on_click=True, ), get_height=get_height, ), [ Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=(HasFocus(DEFAULT_BUFFER) & ~display_completions_in_columns ))), # noqa E501 Float(xcursor=True, ycursor=True, content=MultiColumnCompletionsMenu( extra_filter=(HasFocus(DEFAULT_BUFFER) & display_completions_in_columns), show_meta=Always())) ]), ]), separator(), ConditionalContainer( content=Window(BufferControl( focus_on_click=True, buffer_name=u'clidocs', ), height=LayoutDimension(max=15)), filter=HasDocumentation(app) & ~IsDone(), ), separator(), ValidationToolbar(), SystemToolbar(), # In multiline mode, we use two toolbars for 'arg' and 'search'. ConditionalContainer(ArgToolbar(), multiline), ConditionalContainer(SearchToolbar(), multiline), ] + toolbars)
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 [])), # 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, ignore_case=dyncond('search_ignore_case')) search_buffer_control = SearchBufferControl( buffer=search_buffer, input_processors=[ ReverseSearchProcessor(), ShowArg(), ], 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 _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, 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, ]) return Layout(layout, default_buffer_window)
# from azclishell.az_lexer import ExampleLexer, ToolbarLexer MAX_COMPLETION = 16 # TODO fix this somehow input_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=Always()), HasFocus(SEARCH_BUFFER)), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER)), ] # SHOW_DEFAULT = False class ShowDefault(Filter): """ toggle on and off seeing the default """ def __call__(self, *a, **kw): return get_show_default() class ShowSymbol(Filter): """ toggle showing the symbols """ def __call__(self, *a, **kw): return get_symbols()