blob: f9d574c0a915759585cfcf86e5cb676df1322e0c [file] [log] [blame]
# Copyright 2021 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Text formatting functions."""
import re
from typing import Iterable
from prompt_toolkit.formatted_text import StyleAndTextTuples
from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple
_ANSI_SEQUENCE_REGEX = re.compile(r'\x1b[^m]*m')
def strip_ansi(text: str):
"""Strip out ANSI escape sequences."""
return _ANSI_SEQUENCE_REGEX.sub('', text)
def fill_character_width(line_fragments: StyleAndTextTuples,
fragment_width: int,
window_width: int,
remaining_width: int,
line_wrapping: bool,
horizontal_scroll_amount: int = 0,
add_cursor: bool = False) -> StyleAndTextTuples:
"""Fill line to the width of the window using spaces."""
if add_cursor:
# Add a cursor to this line by adding SetCursorPosition fragment.
line_fragments_remainder = line_fragments
line_fragments = [('[SetCursorPosition]', '')]
# Use extend to keep types happy.
line_fragments.extend(line_fragments_remainder)
# Calculate the number of spaces to add at the end.
empty_characters = window_width - fragment_width
# If wrapping is on, use remaining_width
if line_wrapping and (fragment_width > window_width):
empty_characters = remaining_width
empty_characters += horizontal_scroll_amount
if empty_characters > 0:
# Replace line ending tuple ('', '\n') with additional empty
# spaces to inherit the selected line background.
line_fragments[-1] = ('', ' ' * empty_characters + '\n')
return line_fragments
def flatten_formatted_text_tuples(
lines: Iterable[StyleAndTextTuples]) -> StyleAndTextTuples:
"""Flatten a list of lines of FormattedTextTuples
This function will also remove trailing newlines to avoid displaying extra
empty lines in prompt_toolkit containers.
"""
fragments: StyleAndTextTuples = []
# Return empty list if lines is empty.
if not lines:
return fragments
for line_fragments in lines:
# Append all FormattedText tuples for this line.
for fragment in line_fragments:
fragments.append(fragment)
# Strip off any trailing line breaks
last_fragment: OneStyleAndTextTuple = fragments[-1]
style = last_fragment[0]
text = last_fragment[1].rstrip('\n')
fragments[-1] = (style, text)
return fragments
def remove_formatting(formatted_text):
"""Throw away style info from prompt_toolkit formatted text tuples."""
return ''.join([formatted_tuple[1] for formatted_tuple in formatted_text]) # pylint: disable=not-an-iterable
def get_line_height(text_width, screen_width, prefix_width):
"""Calculates line height for a string with line wrapping enabled."""
if text_width == 0:
return 0
# If text will fit on the screen without wrapping.
if text_width <= screen_width:
return 1, screen_width - text_width
# Assume zero width prefix if it's >= width of the screen.
if prefix_width >= screen_width:
prefix_width = 0
# Start with height of 1 row.
total_height = 1
# One screen_width of characters (with no prefix) is displayed first.
remaining_width = text_width - screen_width
# While we have caracters remaining to be displayed
while remaining_width > 0:
# Add the new indentation prefix
remaining_width += prefix_width
# Display this line
remaining_width -= screen_width
# Add a line break
total_height += 1
# Remaining characters is what's left below zero.
return (total_height, abs(remaining_width))