def __init__(self, start: str = '[', end: str = ']', sym_a: str = '=', sym_b: str = '>', sym_c: str = ' ', unknown: str = '#') -> None: assert len(sym_a) == 1 and get_cwidth(sym_a) == 1 assert len(sym_c) == 1 and get_cwidth(sym_c) == 1 self.start = start self.end = end self.sym_a = sym_a self.sym_b = sym_b self.sym_c = sym_c self.unknown = unknown
def format(self, progress_bar: 'ProgressBar', progress: 'ProgressBarCounter[object]', width: int) -> AnyFormattedText: # Subtract left, bar_b and right. width -= get_cwidth(self.start + self.sym_b + self.end) if progress.total: pb_a = int(progress.percentage * width / 100) bar_a = self.sym_a * pb_a bar_b = self.sym_b bar_c = self.sym_c * (width - pb_a) else: # Total is unknown. pb_a = int(time.time() * 20 % 100 * width / 100) bar_a = self.sym_c * pb_a bar_b = self.unknown bar_c = self.sym_c * (width - pb_a) return HTML(self.template).format( start=self.start, end=self.end, bar_a=bar_a, bar_b=bar_b, bar_c=bar_c)
def _trim_formatted_text( formatted_text: StyleAndTextTuples, max_width: int) -> Tuple[StyleAndTextTuples, int]: """ Trim the text to `max_width`, append dots when the text is too long. Returns (text, width) tuple. """ width = fragment_list_width(formatted_text) # When the text is too wide, trim it. if width > max_width: result = [] # Text fragments. remaining_width = max_width - 3 for style_and_ch in explode_text_fragments(formatted_text): ch_width = get_cwidth(style_and_ch[1]) if ch_width <= remaining_width: result.append(style_and_ch) remaining_width -= ch_width else: break result.append(('', '...')) return result, max_width - remaining_width else: return formatted_text, width
def _get_menu_width( self, max_width: int, complete_state: CompletionState) -> int: """ Return the width of the main column. """ return min(max_width, max(self.MIN_WIDTH, max(get_cwidth(c.display_text) for c in complete_state.completions) + 2))
def preferred_width(self, max_available_width: int) -> int: """ Return the preferred width for this control. That is the width of the longest line. """ text = fragment_list_to_text(self._get_formatted_text_cached()) line_lengths = [get_cwidth(l) for l in text.split('\n')] return max(line_lengths)
def fragment_list_width(fragments: StyleAndTextTuples) -> int: """ Return the character width of this text fragment list. (Take double width characters into account.) :param fragments: List of ``(style_str, text)`` or ``(style_str, text, mouse_handler)`` tuples. """ ZeroWidthEscape = '[ZeroWidthEscape]' return sum(get_cwidth(c) for item in fragments for c in item[1] if ZeroWidthEscape not in item[0])
def get_width() -> AnyDimension: if width is None: text_fragments = to_formatted_text(self.text) text = fragment_list_to_text(text_fragments) if text: longest_line = max( get_cwidth(line) for line in text.splitlines()) else: return D(preferred=0) return D(preferred=longest_line) else: return width
def preferred_width(self, max_available_width: int) -> Optional[int]: """ Report the width of the longest meta text as the preferred width of this control. It could be that we use less width, but this way, we're sure that the layout doesn't change when we select another completion (E.g. that completions are suddenly shown in more or fewer columns.) """ app = get_app() if app.current_buffer.complete_state: state = app.current_buffer.complete_state return 2 + max(get_cwidth(c.display_meta_text) for c in state.completions) else: return 0
def __init__(self, char: str = ' ', style: str = ''): # If this character has to be displayed otherwise, take that one. if char in self.display_mappings: if char == '\xa0': style += ' class:nbsp ' # Will be underlined. else: style += ' class:control-character ' char = self.display_mappings[char] self.char = char self.style = style # Calculate width. (We always need this, so better to store it directly # as a member for performance.) self.width = get_cwidth(char)
def display(page: int) -> None: # Display completions. page_completions = completions[page * completions_per_page:(page + 1) * completions_per_page] page_row_count = int( math.ceil(len(page_completions) / float(column_count))) page_columns = [ page_completions[i * page_row_count:(i + 1) * page_row_count] for i in range(column_count) ] result: StyleAndTextTuples = [] for r in range(page_row_count): for c in range(column_count): try: completion = page_columns[c][r] style = 'class:readline-like-completions.completion ' + ( completion.style or '') result.extend( to_formatted_text(completion.display, style=style)) # Add padding. padding = max_compl_width - get_cwidth( completion.display_text) result.append(( completion.style, ' ' * padding, )) except IndexError: pass result.append(('', '\n')) app.print_text( to_formatted_text(result, 'class:readline-like-completions'))
def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: " Width to report to the `Window`. " # Take the width from the first line. text = fragment_list_to_text(self.get_prompt()) return get_cwidth(text)
def _display_completions_like_readline( app: 'Application', completions: List[Completion]) -> 'asyncio.Task[None]': """ Display the list of completions in columns above the prompt. This will ask for a confirmation if there are too many completions to fit on a single page and provide a paginator to walk through them. """ from prompt_toolkit_dev.shortcuts.prompt import create_confirm_session from prompt_toolkit_dev.formatted_text import to_formatted_text # Get terminal dimensions. term_size = app.output.get_size() term_width = term_size.columns term_height = term_size.rows # Calculate amount of required columns/rows for displaying the # completions. (Keep in mind that completions are displayed # alphabetically column-wise.) max_compl_width = min( term_width, max(get_cwidth(c.display_text) for c in completions) + 1) column_count = max(1, term_width // max_compl_width) completions_per_page = column_count * (term_height - 1) page_count = int(math.ceil(len(completions) / float(completions_per_page))) # Note: math.ceil can return float on Python2. def display(page: int) -> None: # Display completions. page_completions = completions[page * completions_per_page:(page + 1) * completions_per_page] page_row_count = int( math.ceil(len(page_completions) / float(column_count))) page_columns = [ page_completions[i * page_row_count:(i + 1) * page_row_count] for i in range(column_count) ] result: StyleAndTextTuples = [] for r in range(page_row_count): for c in range(column_count): try: completion = page_columns[c][r] style = 'class:readline-like-completions.completion ' + ( completion.style or '') result.extend( to_formatted_text(completion.display, style=style)) # Add padding. padding = max_compl_width - get_cwidth( completion.display_text) result.append(( completion.style, ' ' * padding, )) except IndexError: pass result.append(('', '\n')) app.print_text( to_formatted_text(result, 'class:readline-like-completions')) # User interaction through an application generator function. async def run_compl() -> None: " Coroutine. " async with in_terminal(render_cli_done=True): if len(completions) > completions_per_page: # Ask confirmation if it doesn't fit on the screen. confirm = await create_confirm_session( 'Display all {} possibilities?'.format( len(completions)), ).prompt_async() if confirm: # Display pages. for page in range(page_count): display(page) if page != page_count - 1: # Display --MORE-- and go to the next page. show_more = await _create_more_session( '--MORE--').prompt_async() if not show_more: return else: app.output.flush() else: # Display all completions. display(0) return app.create_background_task(run_compl())
def get_height_for_line(self, lineno: int, width: int, get_line_prefix: Optional[GetLinePrefixCallable], slice_stop: Optional[int] = None) -> int: """ Return the height that a given line would need if it is rendered in a space with the given width (using line wrapping). :param get_line_prefix: None or a `Window.get_line_prefix` callable that returns the prefix to be inserted before this line. :param slice_stop: Wrap only "line[:slice_stop]" and return that partial result. This is needed for scrolling the window correctly when line wrapping. :returns: The computed height. """ # Instead of using `get_line_prefix` as key, we use render_counter # instead. This is more reliable, because this function could still be # the same, while the content would change over time. key = get_app().render_counter, lineno, width, slice_stop try: return self._line_heights_cache[key] except KeyError: if width == 0: height = 10**8 else: # Calculate line width first. line = fragment_list_to_text( self.get_line(lineno))[:slice_stop] text_width = get_cwidth(line) if get_line_prefix: # Add prefix width. text_width += fragment_list_width( to_formatted_text(get_line_prefix(lineno, 0))) # Slower path: compute path when there's a line prefix. height = 1 # Keep wrapping as long as the line doesn't fit. # Keep adding new prefixes for every wrapped line. while text_width > width: height += 1 text_width -= width fragments2 = to_formatted_text( get_line_prefix(lineno, height - 1)) prefix_width = get_cwidth( fragment_list_to_text(fragments2)) if prefix_width >= width: # Prefix doesn't fit. height = 10**8 break text_width += prefix_width else: # Fast path: compute height when there's no line prefix. try: quotient, remainder = divmod(text_width, width) except ZeroDivisionError: height = 10**8 else: if remainder: quotient += 1 # Like math.ceil. height = max(1, quotient) # Cache and return self._line_heights_cache[key] = height return height
def width(self) -> int: if self.children: return max(get_cwidth(c.text) for c in self.children) else: return 0
def _get_column_width(self, complete_state: CompletionState) -> int: """ Return the width of each column. """ return max(get_cwidth(c.display_text) for c in complete_state.completions) + 1
def meta_width(completion: Completion) -> int: return get_cwidth(completion.display_meta_text)