diff options
author | Ivan Blinkov <ivan@blinkov.ru> | 2022-02-10 16:47:11 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:11 +0300 |
commit | 5b283123c882433dafbaf6b338adeea16c1a0ea0 (patch) | |
tree | 339adc63bce23800021202ae4a8328a843dc447a /contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py | |
parent | 1aeb9a455974457866f78722ad98114bafc84e8a (diff) | |
download | ydb-5b283123c882433dafbaf6b338adeea16c1a0ea0.tar.gz |
Restoring authorship annotation for Ivan Blinkov <ivan@blinkov.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py')
-rw-r--r-- | contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py | 944 |
1 files changed, 472 insertions, 472 deletions
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py index 5c8b2b18d1..7a8fde55b3 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/renderer.py @@ -1,118 +1,118 @@ -""" -Renders the command line on the console. -(Redraws parts of the input line that were changed.) -""" -from __future__ import unicode_literals - +""" +Renders the command line on the console. +(Redraws parts of the input line that were changed.) +""" +from __future__ import unicode_literals + from prompt_toolkit.filters import to_cli_filter from prompt_toolkit.layout.mouse_handlers import MouseHandlers -from prompt_toolkit.layout.screen import Point, Screen, WritePosition -from prompt_toolkit.output import Output +from prompt_toolkit.layout.screen import Point, Screen, WritePosition +from prompt_toolkit.output import Output from prompt_toolkit.styles import Style from prompt_toolkit.token import Token -from prompt_toolkit.utils import is_windows - +from prompt_toolkit.utils import is_windows + from six.moves import range -__all__ = ( - 'Renderer', - 'print_tokens', -) - - +__all__ = ( + 'Renderer', + 'print_tokens', +) + + def _output_screen_diff(output, screen, current_pos, previous_screen=None, last_token=None, is_done=False, use_alternate_screen=False, attrs_for_token=None, size=None, previous_width=0): # XXX: drop is_done - """ - Render the diff between this screen and the previous screen. - - This takes two `Screen` instances. The one that represents the output like - it was during the last rendering and one that represents the current - output raster. Looking at these two `Screen` instances, this function will - render the difference by calling the appropriate methods of the `Output` - object that only paint the changes to the terminal. - - This is some performance-critical code which is heavily optimized. - Don't change things without profiling first. - - :param current_pos: Current cursor position. + """ + Render the diff between this screen and the previous screen. + + This takes two `Screen` instances. The one that represents the output like + it was during the last rendering and one that represents the current + output raster. Looking at these two `Screen` instances, this function will + render the difference by calling the appropriate methods of the `Output` + object that only paint the changes to the terminal. + + This is some performance-critical code which is heavily optimized. + Don't change things without profiling first. + + :param current_pos: Current cursor position. :param last_token: `Token` instance that represents the output attributes of - the last drawn character. (Color/attributes.) - :param attrs_for_token: :class:`._TokenToAttrsCache` instance. - :param width: The width of the terminal. - :param prevous_width: The width of the terminal during the last rendering. - """ - width, height = size.columns, size.rows - - #: Remember the last printed character. + the last drawn character. (Color/attributes.) + :param attrs_for_token: :class:`._TokenToAttrsCache` instance. + :param width: The width of the terminal. + :param prevous_width: The width of the terminal during the last rendering. + """ + width, height = size.columns, size.rows + + #: Remember the last printed character. last_token = [last_token] # nonlocal - - #: Variable for capturing the output. - write = output.write + + #: Variable for capturing the output. + write = output.write write_raw = output.write_raw - - # Create locals for the most used output methods. - # (Save expensive attribute lookups.) - _output_set_attributes = output.set_attributes - _output_reset_attributes = output.reset_attributes - _output_cursor_forward = output.cursor_forward - _output_cursor_up = output.cursor_up - _output_cursor_backward = output.cursor_backward - - # Hide cursor before rendering. (Avoid flickering.) - output.hide_cursor() - - def reset_attributes(): - " Wrapper around Output.reset_attributes. " - _output_reset_attributes() + + # Create locals for the most used output methods. + # (Save expensive attribute lookups.) + _output_set_attributes = output.set_attributes + _output_reset_attributes = output.reset_attributes + _output_cursor_forward = output.cursor_forward + _output_cursor_up = output.cursor_up + _output_cursor_backward = output.cursor_backward + + # Hide cursor before rendering. (Avoid flickering.) + output.hide_cursor() + + def reset_attributes(): + " Wrapper around Output.reset_attributes. " + _output_reset_attributes() last_token[0] = None # Forget last char after resetting attributes. - - def move_cursor(new): - " Move cursor to this `new` point. Returns the given Point. " - current_x, current_y = current_pos.x, current_pos.y - - if new.y > current_y: - # Use newlines instead of CURSOR_DOWN, because this meight add new lines. - # CURSOR_DOWN will never create new lines at the bottom. - # Also reset attributes, otherwise the newline could draw a - # background color. - reset_attributes() - write('\r\n' * (new.y - current_y)) - current_x = 0 - _output_cursor_forward(new.x) - return new - elif new.y < current_y: - _output_cursor_up(current_y - new.y) - - if current_x >= width - 1: - write('\r') - _output_cursor_forward(new.x) - elif new.x < current_x or current_x >= width - 1: - _output_cursor_backward(current_x - new.x) - elif new.x > current_x: - _output_cursor_forward(new.x - current_x) - - return new - - def output_char(char): - """ - Write the output of this character. - """ - # If the last printed character has the same token, it also has the - # same style, so we don't output it. + + def move_cursor(new): + " Move cursor to this `new` point. Returns the given Point. " + current_x, current_y = current_pos.x, current_pos.y + + if new.y > current_y: + # Use newlines instead of CURSOR_DOWN, because this meight add new lines. + # CURSOR_DOWN will never create new lines at the bottom. + # Also reset attributes, otherwise the newline could draw a + # background color. + reset_attributes() + write('\r\n' * (new.y - current_y)) + current_x = 0 + _output_cursor_forward(new.x) + return new + elif new.y < current_y: + _output_cursor_up(current_y - new.y) + + if current_x >= width - 1: + write('\r') + _output_cursor_forward(new.x) + elif new.x < current_x or current_x >= width - 1: + _output_cursor_backward(current_x - new.x) + elif new.x > current_x: + _output_cursor_forward(new.x - current_x) + + return new + + def output_char(char): + """ + Write the output of this character. + """ + # If the last printed character has the same token, it also has the + # same style, so we don't output it. the_last_token = last_token[0] if the_last_token and the_last_token == char.token: - write(char.char) - else: + write(char.char) + else: _output_set_attributes(attrs_for_token[char.token]) - write(char.char) + write(char.char) last_token[0] = char.token - + # Render for the first time: reset styling. - if not previous_screen: - reset_attributes() - + if not previous_screen: + reset_attributes() + # Disable autowrap. (When entering a the alternate screen, or anytime when # we have a prompt. - In the case of a REPL, like IPython, people can have # background threads, and it's hard for debugging if their output is not @@ -120,416 +120,416 @@ def _output_screen_diff(output, screen, current_pos, previous_screen=None, last_ if not previous_screen or not use_alternate_screen: output.disable_autowrap() - # When the previous screen has a different size, redraw everything anyway. - # Also when we are done. (We meight take up less rows, so clearing is important.) - if is_done or not previous_screen or previous_width != width: # XXX: also consider height?? - current_pos = move_cursor(Point(0, 0)) - reset_attributes() - output.erase_down() - - previous_screen = Screen() - - # Get height of the screen. - # (height changes as we loop over data_buffer, so remember the current value.) - # (Also make sure to clip the height to the size of the output.) - current_height = min(screen.height, height) - - # Loop over the rows. - row_count = min(max(screen.height, previous_screen.height), height) - c = 0 # Column counter. - + # When the previous screen has a different size, redraw everything anyway. + # Also when we are done. (We meight take up less rows, so clearing is important.) + if is_done or not previous_screen or previous_width != width: # XXX: also consider height?? + current_pos = move_cursor(Point(0, 0)) + reset_attributes() + output.erase_down() + + previous_screen = Screen() + + # Get height of the screen. + # (height changes as we loop over data_buffer, so remember the current value.) + # (Also make sure to clip the height to the size of the output.) + current_height = min(screen.height, height) + + # Loop over the rows. + row_count = min(max(screen.height, previous_screen.height), height) + c = 0 # Column counter. + for y in range(row_count): new_row = screen.data_buffer[y] previous_row = previous_screen.data_buffer[y] zero_width_escapes_row = screen.zero_width_escapes[y] - - new_max_line_len = min(width - 1, max(new_row.keys()) if new_row else 0) - previous_max_line_len = min(width - 1, max(previous_row.keys()) if previous_row else 0) - - # Loop over the columns. - c = 0 - while c < new_max_line_len + 1: - new_char = new_row[c] - old_char = previous_row[c] - char_width = (new_char.width or 1) - - # When the old and new character at this position are different, - # draw the output. (Because of the performance, we don't call - # `Char.__ne__`, but inline the same expression.) - if new_char.char != old_char.char or new_char.token != old_char.token: - current_pos = move_cursor(Point(y=y, x=c)) + + new_max_line_len = min(width - 1, max(new_row.keys()) if new_row else 0) + previous_max_line_len = min(width - 1, max(previous_row.keys()) if previous_row else 0) + + # Loop over the columns. + c = 0 + while c < new_max_line_len + 1: + new_char = new_row[c] + old_char = previous_row[c] + char_width = (new_char.width or 1) + + # When the old and new character at this position are different, + # draw the output. (Because of the performance, we don't call + # `Char.__ne__`, but inline the same expression.) + if new_char.char != old_char.char or new_char.token != old_char.token: + current_pos = move_cursor(Point(y=y, x=c)) # Send injected escape sequences to output. if c in zero_width_escapes_row: write_raw(zero_width_escapes_row[c]) - output_char(new_char) - current_pos = current_pos._replace(x=current_pos.x + char_width) - - c += char_width - - # If the new line is shorter, trim it. - if previous_screen and new_max_line_len < previous_max_line_len: - current_pos = move_cursor(Point(y=y, x=new_max_line_len+1)) - reset_attributes() - output.erase_end_of_line() - - # Correctly reserve vertical space as required by the layout. - # When this is a new screen (drawn for the first time), or for some reason - # higher than the previous one. Move the cursor once to the bottom of the - # output. That way, we're sure that the terminal scrolls up, even when the - # lower lines of the canvas just contain whitespace. - - # The most obvious reason that we actually want this behaviour is the avoid - # the artifact of the input scrolling when the completion menu is shown. - # (If the scrolling is actually wanted, the layout can still be build in a - # way to behave that way by setting a dynamic height.) - if current_height > previous_screen.height: - current_pos = move_cursor(Point(y=current_height - 1, x=0)) - - # Move cursor: - if is_done: - current_pos = move_cursor(Point(y=current_height, x=0)) - output.erase_down() - else: - current_pos = move_cursor(screen.cursor_position) - + output_char(new_char) + current_pos = current_pos._replace(x=current_pos.x + char_width) + + c += char_width + + # If the new line is shorter, trim it. + if previous_screen and new_max_line_len < previous_max_line_len: + current_pos = move_cursor(Point(y=y, x=new_max_line_len+1)) + reset_attributes() + output.erase_end_of_line() + + # Correctly reserve vertical space as required by the layout. + # When this is a new screen (drawn for the first time), or for some reason + # higher than the previous one. Move the cursor once to the bottom of the + # output. That way, we're sure that the terminal scrolls up, even when the + # lower lines of the canvas just contain whitespace. + + # The most obvious reason that we actually want this behaviour is the avoid + # the artifact of the input scrolling when the completion menu is shown. + # (If the scrolling is actually wanted, the layout can still be build in a + # way to behave that way by setting a dynamic height.) + if current_height > previous_screen.height: + current_pos = move_cursor(Point(y=current_height - 1, x=0)) + + # Move cursor: + if is_done: + current_pos = move_cursor(Point(y=current_height, x=0)) + output.erase_down() + else: + current_pos = move_cursor(screen.cursor_position) + if is_done or not use_alternate_screen: - output.enable_autowrap() - + output.enable_autowrap() + # Always reset the color attributes. This is important because a background # thread could print data to stdout and we want that to be displayed in the # default colors. (Also, if a background color has been set, many terminals # give weird artifacs on resize events.) reset_attributes() - - if screen.show_cursor or is_done: - output.show_cursor() - + + if screen.show_cursor or is_done: + output.show_cursor() + return current_pos, last_token[0] - - -class HeightIsUnknownError(Exception): - " Information unavailable. Did not yet receive the CPR response. " - - -class _TokenToAttrsCache(dict): - """ - A cache structure that maps Pygments Tokens to :class:`.Attr`. - (This is an important speed up.) - """ - def __init__(self, get_style_for_token): - self.get_style_for_token = get_style_for_token - - def __missing__(self, token): - try: - result = self.get_style_for_token(token) - except KeyError: - result = None - - self[token] = result - return result - - -class Renderer(object): - """ - Typical usage: - - :: - - output = Vt100_Output.from_pty(sys.stdout) - r = Renderer(style, output) - r.render(cli, layout=...) - """ - def __init__(self, style, output, use_alternate_screen=False, mouse_support=False): - assert isinstance(style, Style) - assert isinstance(output, Output) - - self.style = style - self.output = output - self.use_alternate_screen = use_alternate_screen - self.mouse_support = to_cli_filter(mouse_support) - - self._in_alternate_screen = False - self._mouse_support_enabled = False - self._bracketed_paste_enabled = False - + + +class HeightIsUnknownError(Exception): + " Information unavailable. Did not yet receive the CPR response. " + + +class _TokenToAttrsCache(dict): + """ + A cache structure that maps Pygments Tokens to :class:`.Attr`. + (This is an important speed up.) + """ + def __init__(self, get_style_for_token): + self.get_style_for_token = get_style_for_token + + def __missing__(self, token): + try: + result = self.get_style_for_token(token) + except KeyError: + result = None + + self[token] = result + return result + + +class Renderer(object): + """ + Typical usage: + + :: + + output = Vt100_Output.from_pty(sys.stdout) + r = Renderer(style, output) + r.render(cli, layout=...) + """ + def __init__(self, style, output, use_alternate_screen=False, mouse_support=False): + assert isinstance(style, Style) + assert isinstance(output, Output) + + self.style = style + self.output = output + self.use_alternate_screen = use_alternate_screen + self.mouse_support = to_cli_filter(mouse_support) + + self._in_alternate_screen = False + self._mouse_support_enabled = False + self._bracketed_paste_enabled = False + # Waiting for CPR flag. True when we send the request, but didn't got a # response. self.waiting_for_cpr = False - self.reset(_scroll=True) - + self.reset(_scroll=True) + def reset(self, _scroll=False, leave_alternate_screen=True): - # Reset position - self._cursor_pos = Point(x=0, y=0) - - # Remember the last screen instance between renderers. This way, - # we can create a `diff` between two screens and only output the - # difference. It's also to remember the last height. (To show for - # instance a toolbar at the bottom position.) - self._last_screen = None - self._last_size = None + # Reset position + self._cursor_pos = Point(x=0, y=0) + + # Remember the last screen instance between renderers. This way, + # we can create a `diff` between two screens and only output the + # difference. It's also to remember the last height. (To show for + # instance a toolbar at the bottom position.) + self._last_screen = None + self._last_size = None self._last_token = None - - # When the style hash changes, we have to do a full redraw as well as - # clear the `_attrs_for_token` dictionary. - self._last_style_hash = None - self._attrs_for_token = None - - # Default MouseHandlers. (Just empty.) - self.mouse_handlers = MouseHandlers() - - # Remember the last title. Only set the title when it changes. - self._last_title = None - - #: Space from the top of the layout, until the bottom of the terminal. - #: We don't know this until a `report_absolute_cursor_row` call. - self._min_available_height = 0 - - # In case of Windown, also make sure to scroll to the current cursor - # position. (Only when rendering the first time.) - if is_windows() and _scroll: - self.output.scroll_buffer_to_prompt() - - # Quit alternate screen. + + # When the style hash changes, we have to do a full redraw as well as + # clear the `_attrs_for_token` dictionary. + self._last_style_hash = None + self._attrs_for_token = None + + # Default MouseHandlers. (Just empty.) + self.mouse_handlers = MouseHandlers() + + # Remember the last title. Only set the title when it changes. + self._last_title = None + + #: Space from the top of the layout, until the bottom of the terminal. + #: We don't know this until a `report_absolute_cursor_row` call. + self._min_available_height = 0 + + # In case of Windown, also make sure to scroll to the current cursor + # position. (Only when rendering the first time.) + if is_windows() and _scroll: + self.output.scroll_buffer_to_prompt() + + # Quit alternate screen. if self._in_alternate_screen and leave_alternate_screen: - self.output.quit_alternate_screen() - self._in_alternate_screen = False - - # Disable mouse support. - if self._mouse_support_enabled: - self.output.disable_mouse_support() - self._mouse_support_enabled = False - - # Disable bracketed paste. - if self._bracketed_paste_enabled: - self.output.disable_bracketed_paste() - self._bracketed_paste_enabled = False - - # Flush output. `disable_mouse_support` needs to write to stdout. - self.output.flush() - - @property - def height_is_known(self): - """ - True when the height from the cursor until the bottom of the terminal - is known. (It's often nicer to draw bottom toolbars only if the height - is known, in order to avoid flickering when the CPR response arrives.) - """ - return self.use_alternate_screen or self._min_available_height > 0 or \ - is_windows() # On Windows, we don't have to wait for a CPR. - - @property - def rows_above_layout(self): - """ - Return the number of rows visible in the terminal above the layout. - """ - if self._in_alternate_screen: - return 0 - elif self._min_available_height > 0: - total_rows = self.output.get_size().rows - last_screen_height = self._last_screen.height if self._last_screen else 0 - return total_rows - max(self._min_available_height, last_screen_height) - else: - raise HeightIsUnknownError('Rows above layout is unknown.') - - def request_absolute_cursor_position(self): - """ - Get current cursor position. - For vt100: Do CPR request. (answer will arrive later.) - For win32: Do API call. (Answer comes immediately.) - """ - # Only do this request when the cursor is at the top row. (after a - # clear or reset). We will rely on that in `report_absolute_cursor_row`. - assert self._cursor_pos.y == 0 - - # For Win32, we have an API call to get the number of rows below the - # cursor. - if is_windows(): - self._min_available_height = self.output.get_rows_below_cursor_position() - else: - if self.use_alternate_screen: - self._min_available_height = self.output.get_size().rows - else: - # Asks for a cursor position report (CPR). + self.output.quit_alternate_screen() + self._in_alternate_screen = False + + # Disable mouse support. + if self._mouse_support_enabled: + self.output.disable_mouse_support() + self._mouse_support_enabled = False + + # Disable bracketed paste. + if self._bracketed_paste_enabled: + self.output.disable_bracketed_paste() + self._bracketed_paste_enabled = False + + # Flush output. `disable_mouse_support` needs to write to stdout. + self.output.flush() + + @property + def height_is_known(self): + """ + True when the height from the cursor until the bottom of the terminal + is known. (It's often nicer to draw bottom toolbars only if the height + is known, in order to avoid flickering when the CPR response arrives.) + """ + return self.use_alternate_screen or self._min_available_height > 0 or \ + is_windows() # On Windows, we don't have to wait for a CPR. + + @property + def rows_above_layout(self): + """ + Return the number of rows visible in the terminal above the layout. + """ + if self._in_alternate_screen: + return 0 + elif self._min_available_height > 0: + total_rows = self.output.get_size().rows + last_screen_height = self._last_screen.height if self._last_screen else 0 + return total_rows - max(self._min_available_height, last_screen_height) + else: + raise HeightIsUnknownError('Rows above layout is unknown.') + + def request_absolute_cursor_position(self): + """ + Get current cursor position. + For vt100: Do CPR request. (answer will arrive later.) + For win32: Do API call. (Answer comes immediately.) + """ + # Only do this request when the cursor is at the top row. (after a + # clear or reset). We will rely on that in `report_absolute_cursor_row`. + assert self._cursor_pos.y == 0 + + # For Win32, we have an API call to get the number of rows below the + # cursor. + if is_windows(): + self._min_available_height = self.output.get_rows_below_cursor_position() + else: + if self.use_alternate_screen: + self._min_available_height = self.output.get_size().rows + else: + # Asks for a cursor position report (CPR). self.waiting_for_cpr = True - self.output.ask_for_cpr() - - def report_absolute_cursor_row(self, row): - """ - To be called when we know the absolute cursor position. - (As an answer of a "Cursor Position Request" response.) - """ - # Calculate the amount of rows from the cursor position until the - # bottom of the terminal. - total_rows = self.output.get_size().rows - rows_below_cursor = total_rows - row + 1 - - # Set the - self._min_available_height = rows_below_cursor - + self.output.ask_for_cpr() + + def report_absolute_cursor_row(self, row): + """ + To be called when we know the absolute cursor position. + (As an answer of a "Cursor Position Request" response.) + """ + # Calculate the amount of rows from the cursor position until the + # bottom of the terminal. + total_rows = self.output.get_size().rows + rows_below_cursor = total_rows - row + 1 + + # Set the + self._min_available_height = rows_below_cursor + self.waiting_for_cpr = False - def render(self, cli, layout, is_done=False): - """ - Render the current interface to the output. - - :param is_done: When True, put the cursor at the end of the interface. We - won't print any changes to this part. - """ - output = self.output - - # Enter alternate screen. - if self.use_alternate_screen and not self._in_alternate_screen: - self._in_alternate_screen = True - output.enter_alternate_screen() - - # Enable bracketed paste. - if not self._bracketed_paste_enabled: - self.output.enable_bracketed_paste() - self._bracketed_paste_enabled = True - - # Enable/disable mouse support. - needs_mouse_support = self.mouse_support(cli) - - if needs_mouse_support and not self._mouse_support_enabled: - output.enable_mouse_support() - self._mouse_support_enabled = True - - elif not needs_mouse_support and self._mouse_support_enabled: - output.disable_mouse_support() - self._mouse_support_enabled = False - - # Create screen and write layout to it. - size = output.get_size() - screen = Screen() - screen.show_cursor = False # Hide cursor by default, unless one of the - # containers decides to display it. - mouse_handlers = MouseHandlers() - - if is_done: - height = 0 # When we are done, we don't necessary want to fill up until the bottom. - else: - height = self._last_screen.height if self._last_screen else 0 - height = max(self._min_available_height, height) - - # When te size changes, don't consider the previous screen. - if self._last_size != size: - self._last_screen = None - - # When we render using another style, do a full repaint. (Forget about - # the previous rendered screen.) - # (But note that we still use _last_screen to calculate the height.) - if self.style.invalidation_hash() != self._last_style_hash: - self._last_screen = None - self._attrs_for_token = None - if self._attrs_for_token is None: - self._attrs_for_token = _TokenToAttrsCache(self.style.get_attrs_for_token) - self._last_style_hash = self.style.invalidation_hash() - - layout.write_to_screen(cli, screen, mouse_handlers, WritePosition( - xpos=0, - ypos=0, - width=size.columns, - height=(size.rows if self.use_alternate_screen else height), - extended_height=size.rows, - )) - - # When grayed. Replace all tokens in the new screen. - if cli.is_aborting or cli.is_exiting: - screen.replace_all_tokens(Token.Aborted) - - # Process diff and write to output. + def render(self, cli, layout, is_done=False): + """ + Render the current interface to the output. + + :param is_done: When True, put the cursor at the end of the interface. We + won't print any changes to this part. + """ + output = self.output + + # Enter alternate screen. + if self.use_alternate_screen and not self._in_alternate_screen: + self._in_alternate_screen = True + output.enter_alternate_screen() + + # Enable bracketed paste. + if not self._bracketed_paste_enabled: + self.output.enable_bracketed_paste() + self._bracketed_paste_enabled = True + + # Enable/disable mouse support. + needs_mouse_support = self.mouse_support(cli) + + if needs_mouse_support and not self._mouse_support_enabled: + output.enable_mouse_support() + self._mouse_support_enabled = True + + elif not needs_mouse_support and self._mouse_support_enabled: + output.disable_mouse_support() + self._mouse_support_enabled = False + + # Create screen and write layout to it. + size = output.get_size() + screen = Screen() + screen.show_cursor = False # Hide cursor by default, unless one of the + # containers decides to display it. + mouse_handlers = MouseHandlers() + + if is_done: + height = 0 # When we are done, we don't necessary want to fill up until the bottom. + else: + height = self._last_screen.height if self._last_screen else 0 + height = max(self._min_available_height, height) + + # When te size changes, don't consider the previous screen. + if self._last_size != size: + self._last_screen = None + + # When we render using another style, do a full repaint. (Forget about + # the previous rendered screen.) + # (But note that we still use _last_screen to calculate the height.) + if self.style.invalidation_hash() != self._last_style_hash: + self._last_screen = None + self._attrs_for_token = None + if self._attrs_for_token is None: + self._attrs_for_token = _TokenToAttrsCache(self.style.get_attrs_for_token) + self._last_style_hash = self.style.invalidation_hash() + + layout.write_to_screen(cli, screen, mouse_handlers, WritePosition( + xpos=0, + ypos=0, + width=size.columns, + height=(size.rows if self.use_alternate_screen else height), + extended_height=size.rows, + )) + + # When grayed. Replace all tokens in the new screen. + if cli.is_aborting or cli.is_exiting: + screen.replace_all_tokens(Token.Aborted) + + # Process diff and write to output. self._cursor_pos, self._last_token = _output_screen_diff( - output, screen, self._cursor_pos, + output, screen, self._cursor_pos, self._last_screen, self._last_token, is_done, use_alternate_screen=self.use_alternate_screen, - attrs_for_token=self._attrs_for_token, - size=size, - previous_width=(self._last_size.columns if self._last_size else 0)) - self._last_screen = screen - self._last_size = size - self.mouse_handlers = mouse_handlers - - # Write title if it changed. - new_title = cli.terminal_title - - if new_title != self._last_title: - if new_title is None: - self.output.clear_title() - else: - self.output.set_title(new_title) - self._last_title = new_title - - output.flush() - + attrs_for_token=self._attrs_for_token, + size=size, + previous_width=(self._last_size.columns if self._last_size else 0)) + self._last_screen = screen + self._last_size = size + self.mouse_handlers = mouse_handlers + + # Write title if it changed. + new_title = cli.terminal_title + + if new_title != self._last_title: + if new_title is None: + self.output.clear_title() + else: + self.output.set_title(new_title) + self._last_title = new_title + + output.flush() + def erase(self, leave_alternate_screen=True, erase_title=True): - """ - Hide all output and put the cursor back at the first line. This is for - instance used for running a system command (while hiding the CLI) and - later resuming the same CLI.) + """ + Hide all output and put the cursor back at the first line. This is for + instance used for running a system command (while hiding the CLI) and + later resuming the same CLI.) :param leave_alternate_screen: When True, and when inside an alternate screen buffer, quit the alternate screen. :param erase_title: When True, clear the title from the title bar. - """ - output = self.output - - output.cursor_backward(self._cursor_pos.x) - output.cursor_up(self._cursor_pos.y) - output.erase_down() - output.reset_attributes() + """ + output = self.output + + output.cursor_backward(self._cursor_pos.x) + output.cursor_up(self._cursor_pos.y) + output.erase_down() + output.reset_attributes() output.enable_autowrap() - output.flush() - - # Erase title. + output.flush() + + # Erase title. if self._last_title and erase_title: - output.clear_title() - + output.clear_title() + self.reset(leave_alternate_screen=leave_alternate_screen) - - def clear(self): - """ - Clear screen and go to 0,0 - """ - # Erase current output first. - self.erase() - - # Send "Erase Screen" command and go to (0, 0). - output = self.output - - output.erase_screen() - output.cursor_goto(0, 0) - output.flush() - - self.request_absolute_cursor_position() - - -def print_tokens(output, tokens, style): - """ - Print a list of (Token, text) tuples in the given style to the output. - """ - assert isinstance(output, Output) - assert isinstance(style, Style) - - # Reset first. - output.reset_attributes() - output.enable_autowrap() - - # Print all (token, text) tuples. - attrs_for_token = _TokenToAttrsCache(style.get_attrs_for_token) - - for token, text in tokens: - attrs = attrs_for_token[token] - - if attrs: - output.set_attributes(attrs) - else: - output.reset_attributes() - - output.write(text) - - # Reset again. - output.reset_attributes() - output.flush() + + def clear(self): + """ + Clear screen and go to 0,0 + """ + # Erase current output first. + self.erase() + + # Send "Erase Screen" command and go to (0, 0). + output = self.output + + output.erase_screen() + output.cursor_goto(0, 0) + output.flush() + + self.request_absolute_cursor_position() + + +def print_tokens(output, tokens, style): + """ + Print a list of (Token, text) tuples in the given style to the output. + """ + assert isinstance(output, Output) + assert isinstance(style, Style) + + # Reset first. + output.reset_attributes() + output.enable_autowrap() + + # Print all (token, text) tuples. + attrs_for_token = _TokenToAttrsCache(style.get_attrs_for_token) + + for token, text in tokens: + attrs = attrs_for_token[token] + + if attrs: + output.set_attributes(attrs) + else: + output.reset_attributes() + + output.write(text) + + # Reset again. + output.reset_attributes() + output.flush() |