diff options
author | shadchin <[email protected]> | 2022-02-10 16:44:39 +0300 |
---|---|---|
committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:44:39 +0300 |
commit | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch) | |
tree | 64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/prompt-toolkit/py3/prompt_toolkit/output | |
parent | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff) |
Restoring authorship annotation for <[email protected]>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py3/prompt_toolkit/output')
8 files changed, 2021 insertions, 2021 deletions
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/__init__.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/__init__.py index ccfebf6ac43..7b90b476bd3 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/__init__.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/__init__.py @@ -1,13 +1,13 @@ -from .base import DummyOutput, Output -from .color_depth import ColorDepth -from .defaults import create_output - -__all__ = [ - # Base. - "Output", - "DummyOutput", - # Color depth. - "ColorDepth", - # Defaults. - "create_output", -] +from .base import DummyOutput, Output +from .color_depth import ColorDepth +from .defaults import create_output + +__all__ = [ + # Base. + "Output", + "DummyOutput", + # Color depth. + "ColorDepth", + # Defaults. + "create_output", +] diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/base.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/base.py index ee2c42457e1..c78677bc8b2 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/base.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/base.py @@ -1,146 +1,146 @@ -""" -Interface for an output. -""" -from abc import ABCMeta, abstractmethod -from typing import Optional, TextIO - +""" +Interface for an output. +""" +from abc import ABCMeta, abstractmethod +from typing import Optional, TextIO + from prompt_toolkit.cursor_shapes import CursorShape -from prompt_toolkit.data_structures import Size -from prompt_toolkit.styles import Attrs - -from .color_depth import ColorDepth - -__all__ = [ - "Output", - "DummyOutput", -] - - -class Output(metaclass=ABCMeta): - """ - Base class defining the output interface for a - :class:`~prompt_toolkit.renderer.Renderer`. - - Actual implementations are - :class:`~prompt_toolkit.output.vt100.Vt100_Output` and - :class:`~prompt_toolkit.output.win32.Win32Output`. - """ - - stdout: Optional[TextIO] = None - - @abstractmethod - def fileno(self) -> int: - "Return the file descriptor to which we can write for the output." - - @abstractmethod - def encoding(self) -> str: - """ - Return the encoding for this output, e.g. 'utf-8'. - (This is used mainly to know which characters are supported by the - output the data, so that the UI can provide alternatives, when - required.) - """ - - @abstractmethod - def write(self, data: str) -> None: - "Write text (Terminal escape sequences will be removed/escaped.)" - - @abstractmethod - def write_raw(self, data: str) -> None: - "Write text." - - @abstractmethod - def set_title(self, title: str) -> None: - "Set terminal title." - - @abstractmethod - def clear_title(self) -> None: - "Clear title again. (or restore previous title.)" - - @abstractmethod - def flush(self) -> None: - "Write to output stream and flush." - - @abstractmethod - def erase_screen(self) -> None: - """ - Erases the screen with the background colour and moves the cursor to - home. - """ - - @abstractmethod - def enter_alternate_screen(self) -> None: - "Go to the alternate screen buffer. (For full screen applications)." - - @abstractmethod - def quit_alternate_screen(self) -> None: - "Leave the alternate screen buffer." - - @abstractmethod - def enable_mouse_support(self) -> None: - "Enable mouse." - - @abstractmethod - def disable_mouse_support(self) -> None: - "Disable mouse." - - @abstractmethod - def erase_end_of_line(self) -> None: - """ - Erases from the current cursor position to the end of the current line. - """ - - @abstractmethod - def erase_down(self) -> None: - """ - Erases the screen from the current line down to the bottom of the - screen. - """ - - @abstractmethod - def reset_attributes(self) -> None: - "Reset color and styling attributes." - - @abstractmethod - def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: - "Set new color and styling attributes." - - @abstractmethod - def disable_autowrap(self) -> None: - "Disable auto line wrapping." - - @abstractmethod - def enable_autowrap(self) -> None: - "Enable auto line wrapping." - - @abstractmethod - def cursor_goto(self, row: int = 0, column: int = 0) -> None: - "Move cursor position." - - @abstractmethod - def cursor_up(self, amount: int) -> None: - "Move cursor `amount` place up." - - @abstractmethod - def cursor_down(self, amount: int) -> None: - "Move cursor `amount` place down." - - @abstractmethod - def cursor_forward(self, amount: int) -> None: - "Move cursor `amount` place forward." - - @abstractmethod - def cursor_backward(self, amount: int) -> None: - "Move cursor `amount` place backward." - - @abstractmethod - def hide_cursor(self) -> None: - "Hide cursor." - - @abstractmethod - def show_cursor(self) -> None: - "Show cursor." - +from prompt_toolkit.data_structures import Size +from prompt_toolkit.styles import Attrs + +from .color_depth import ColorDepth + +__all__ = [ + "Output", + "DummyOutput", +] + + +class Output(metaclass=ABCMeta): + """ + Base class defining the output interface for a + :class:`~prompt_toolkit.renderer.Renderer`. + + Actual implementations are + :class:`~prompt_toolkit.output.vt100.Vt100_Output` and + :class:`~prompt_toolkit.output.win32.Win32Output`. + """ + + stdout: Optional[TextIO] = None + + @abstractmethod + def fileno(self) -> int: + "Return the file descriptor to which we can write for the output." + + @abstractmethod + def encoding(self) -> str: + """ + Return the encoding for this output, e.g. 'utf-8'. + (This is used mainly to know which characters are supported by the + output the data, so that the UI can provide alternatives, when + required.) + """ + + @abstractmethod + def write(self, data: str) -> None: + "Write text (Terminal escape sequences will be removed/escaped.)" + + @abstractmethod + def write_raw(self, data: str) -> None: + "Write text." + + @abstractmethod + def set_title(self, title: str) -> None: + "Set terminal title." + + @abstractmethod + def clear_title(self) -> None: + "Clear title again. (or restore previous title.)" + + @abstractmethod + def flush(self) -> None: + "Write to output stream and flush." + + @abstractmethod + def erase_screen(self) -> None: + """ + Erases the screen with the background colour and moves the cursor to + home. + """ + + @abstractmethod + def enter_alternate_screen(self) -> None: + "Go to the alternate screen buffer. (For full screen applications)." + + @abstractmethod + def quit_alternate_screen(self) -> None: + "Leave the alternate screen buffer." + + @abstractmethod + def enable_mouse_support(self) -> None: + "Enable mouse." + + @abstractmethod + def disable_mouse_support(self) -> None: + "Disable mouse." + + @abstractmethod + def erase_end_of_line(self) -> None: + """ + Erases from the current cursor position to the end of the current line. + """ + + @abstractmethod + def erase_down(self) -> None: + """ + Erases the screen from the current line down to the bottom of the + screen. + """ + + @abstractmethod + def reset_attributes(self) -> None: + "Reset color and styling attributes." + + @abstractmethod + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + "Set new color and styling attributes." + + @abstractmethod + def disable_autowrap(self) -> None: + "Disable auto line wrapping." + + @abstractmethod + def enable_autowrap(self) -> None: + "Enable auto line wrapping." + + @abstractmethod + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + "Move cursor position." + + @abstractmethod + def cursor_up(self, amount: int) -> None: + "Move cursor `amount` place up." + + @abstractmethod + def cursor_down(self, amount: int) -> None: + "Move cursor `amount` place down." + + @abstractmethod + def cursor_forward(self, amount: int) -> None: + "Move cursor `amount` place forward." + + @abstractmethod + def cursor_backward(self, amount: int) -> None: + "Move cursor `amount` place backward." + + @abstractmethod + def hide_cursor(self) -> None: + "Hide cursor." + + @abstractmethod + def show_cursor(self) -> None: + "Show cursor." + @abstractmethod def set_cursor_shape(self, cursor_shape: CursorShape) -> None: "Set cursor shape to block, beam or underline." @@ -149,181 +149,181 @@ class Output(metaclass=ABCMeta): def reset_cursor_shape(self) -> None: "Reset cursor shape." - def ask_for_cpr(self) -> None: - """ - Asks for a cursor position report (CPR). - (VT100 only.) - """ - - @property - def responds_to_cpr(self) -> bool: - """ - `True` if the `Application` can expect to receive a CPR response after - calling `ask_for_cpr` (this will come back through the corresponding - `Input`). - - This is used to determine the amount of available rows we have below - the cursor position. In the first place, we have this so that the drop - down autocompletion menus are sized according to the available space. - - On Windows, we don't need this, there we have - `get_rows_below_cursor_position`. - """ - return False - - @abstractmethod - def get_size(self) -> Size: - "Return the size of the output window." - - def bell(self) -> None: - "Sound bell." - - def enable_bracketed_paste(self) -> None: - "For vt100 only." - - def disable_bracketed_paste(self) -> None: - "For vt100 only." - - def reset_cursor_key_mode(self) -> None: - """ - For vt100 only. - Put the terminal in normal cursor mode (instead of application mode). - - See: https://vt100.net/docs/vt100-ug/chapter3.html - """ - - def scroll_buffer_to_prompt(self) -> None: - "For Win32 only." - - def get_rows_below_cursor_position(self) -> int: - "For Windows only." - raise NotImplementedError - - @abstractmethod - def get_default_color_depth(self) -> ColorDepth: - """ - Get default color depth for this output. - - This value will be used if no color depth was explicitely passed to the - `Application`. - - .. note:: - - If the `$PROMPT_TOOLKIT_COLOR_DEPTH` environment variable has been - set, then `outputs.defaults.create_output` will pass this value to - the implementation as the default_color_depth, which is returned - here. (This is not used when the output corresponds to a - prompt_toolkit SSH/Telnet session.) - """ - - -class DummyOutput(Output): - """ - For testing. An output class that doesn't render anything. - """ - - def fileno(self) -> int: - "There is no sensible default for fileno()." - raise NotImplementedError - - def encoding(self) -> str: - return "utf-8" - - def write(self, data: str) -> None: - pass - - def write_raw(self, data: str) -> None: - pass - - def set_title(self, title: str) -> None: - pass - - def clear_title(self) -> None: - pass - - def flush(self) -> None: - pass - - def erase_screen(self) -> None: - pass - - def enter_alternate_screen(self) -> None: - pass - - def quit_alternate_screen(self) -> None: - pass - - def enable_mouse_support(self) -> None: - pass - - def disable_mouse_support(self) -> None: - pass - - def erase_end_of_line(self) -> None: - pass - - def erase_down(self) -> None: - pass - - def reset_attributes(self) -> None: - pass - - def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: - pass - - def disable_autowrap(self) -> None: - pass - - def enable_autowrap(self) -> None: - pass - - def cursor_goto(self, row: int = 0, column: int = 0) -> None: - pass - - def cursor_up(self, amount: int) -> None: - pass - - def cursor_down(self, amount: int) -> None: - pass - - def cursor_forward(self, amount: int) -> None: - pass - - def cursor_backward(self, amount: int) -> None: - pass - - def hide_cursor(self) -> None: - pass - - def show_cursor(self) -> None: - pass - + def ask_for_cpr(self) -> None: + """ + Asks for a cursor position report (CPR). + (VT100 only.) + """ + + @property + def responds_to_cpr(self) -> bool: + """ + `True` if the `Application` can expect to receive a CPR response after + calling `ask_for_cpr` (this will come back through the corresponding + `Input`). + + This is used to determine the amount of available rows we have below + the cursor position. In the first place, we have this so that the drop + down autocompletion menus are sized according to the available space. + + On Windows, we don't need this, there we have + `get_rows_below_cursor_position`. + """ + return False + + @abstractmethod + def get_size(self) -> Size: + "Return the size of the output window." + + def bell(self) -> None: + "Sound bell." + + def enable_bracketed_paste(self) -> None: + "For vt100 only." + + def disable_bracketed_paste(self) -> None: + "For vt100 only." + + def reset_cursor_key_mode(self) -> None: + """ + For vt100 only. + Put the terminal in normal cursor mode (instead of application mode). + + See: https://vt100.net/docs/vt100-ug/chapter3.html + """ + + def scroll_buffer_to_prompt(self) -> None: + "For Win32 only." + + def get_rows_below_cursor_position(self) -> int: + "For Windows only." + raise NotImplementedError + + @abstractmethod + def get_default_color_depth(self) -> ColorDepth: + """ + Get default color depth for this output. + + This value will be used if no color depth was explicitely passed to the + `Application`. + + .. note:: + + If the `$PROMPT_TOOLKIT_COLOR_DEPTH` environment variable has been + set, then `outputs.defaults.create_output` will pass this value to + the implementation as the default_color_depth, which is returned + here. (This is not used when the output corresponds to a + prompt_toolkit SSH/Telnet session.) + """ + + +class DummyOutput(Output): + """ + For testing. An output class that doesn't render anything. + """ + + def fileno(self) -> int: + "There is no sensible default for fileno()." + raise NotImplementedError + + def encoding(self) -> str: + return "utf-8" + + def write(self, data: str) -> None: + pass + + def write_raw(self, data: str) -> None: + pass + + def set_title(self, title: str) -> None: + pass + + def clear_title(self) -> None: + pass + + def flush(self) -> None: + pass + + def erase_screen(self) -> None: + pass + + def enter_alternate_screen(self) -> None: + pass + + def quit_alternate_screen(self) -> None: + pass + + def enable_mouse_support(self) -> None: + pass + + def disable_mouse_support(self) -> None: + pass + + def erase_end_of_line(self) -> None: + pass + + def erase_down(self) -> None: + pass + + def reset_attributes(self) -> None: + pass + + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + pass + + def disable_autowrap(self) -> None: + pass + + def enable_autowrap(self) -> None: + pass + + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + pass + + def cursor_up(self, amount: int) -> None: + pass + + def cursor_down(self, amount: int) -> None: + pass + + def cursor_forward(self, amount: int) -> None: + pass + + def cursor_backward(self, amount: int) -> None: + pass + + def hide_cursor(self) -> None: + pass + + def show_cursor(self) -> None: + pass + def set_cursor_shape(self, cursor_shape: CursorShape) -> None: pass def reset_cursor_shape(self) -> None: pass - def ask_for_cpr(self) -> None: - pass - - def bell(self) -> None: - pass - - def enable_bracketed_paste(self) -> None: - pass - - def disable_bracketed_paste(self) -> None: - pass - - def scroll_buffer_to_prompt(self) -> None: - pass - - def get_size(self) -> Size: - return Size(rows=40, columns=80) - - def get_rows_below_cursor_position(self) -> int: - return 40 - - def get_default_color_depth(self) -> ColorDepth: - return ColorDepth.DEPTH_1_BIT + def ask_for_cpr(self) -> None: + pass + + def bell(self) -> None: + pass + + def enable_bracketed_paste(self) -> None: + pass + + def disable_bracketed_paste(self) -> None: + pass + + def scroll_buffer_to_prompt(self) -> None: + pass + + def get_size(self) -> Size: + return Size(rows=40, columns=80) + + def get_rows_below_cursor_position(self) -> int: + return 40 + + def get_default_color_depth(self) -> ColorDepth: + return ColorDepth.DEPTH_1_BIT diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/color_depth.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/color_depth.py index 9756c206af4..a6166bacafb 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/color_depth.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/color_depth.py @@ -1,58 +1,58 @@ -import os -from enum import Enum -from typing import Optional - -__all__ = [ - "ColorDepth", -] - - -class ColorDepth(str, Enum): - """ - Possible color depth values for the output. - """ - - value: str - - #: One color only. - DEPTH_1_BIT = "DEPTH_1_BIT" - - #: ANSI Colors. - DEPTH_4_BIT = "DEPTH_4_BIT" - - #: The default. - DEPTH_8_BIT = "DEPTH_8_BIT" - - #: 24 bit True color. - DEPTH_24_BIT = "DEPTH_24_BIT" - - # Aliases. - MONOCHROME = DEPTH_1_BIT - ANSI_COLORS_ONLY = DEPTH_4_BIT - DEFAULT = DEPTH_8_BIT - TRUE_COLOR = DEPTH_24_BIT - - @classmethod - def from_env(cls) -> Optional["ColorDepth"]: - """ - Return the color depth if the $PROMPT_TOOLKIT_COLOR_DEPTH environment - variable has been set. - - This is a way to enforce a certain color depth in all prompt_toolkit - applications. - """ - # Check the `PROMPT_TOOLKIT_COLOR_DEPTH` environment variable. - all_values = [i.value for i in ColorDepth] - if os.environ.get("PROMPT_TOOLKIT_COLOR_DEPTH") in all_values: - return cls(os.environ["PROMPT_TOOLKIT_COLOR_DEPTH"]) - - return None - - @classmethod - def default(cls) -> "ColorDepth": - """ - Return the default color depth for the default output. - """ - from .defaults import create_output - - return create_output().get_default_color_depth() +import os +from enum import Enum +from typing import Optional + +__all__ = [ + "ColorDepth", +] + + +class ColorDepth(str, Enum): + """ + Possible color depth values for the output. + """ + + value: str + + #: One color only. + DEPTH_1_BIT = "DEPTH_1_BIT" + + #: ANSI Colors. + DEPTH_4_BIT = "DEPTH_4_BIT" + + #: The default. + DEPTH_8_BIT = "DEPTH_8_BIT" + + #: 24 bit True color. + DEPTH_24_BIT = "DEPTH_24_BIT" + + # Aliases. + MONOCHROME = DEPTH_1_BIT + ANSI_COLORS_ONLY = DEPTH_4_BIT + DEFAULT = DEPTH_8_BIT + TRUE_COLOR = DEPTH_24_BIT + + @classmethod + def from_env(cls) -> Optional["ColorDepth"]: + """ + Return the color depth if the $PROMPT_TOOLKIT_COLOR_DEPTH environment + variable has been set. + + This is a way to enforce a certain color depth in all prompt_toolkit + applications. + """ + # Check the `PROMPT_TOOLKIT_COLOR_DEPTH` environment variable. + all_values = [i.value for i in ColorDepth] + if os.environ.get("PROMPT_TOOLKIT_COLOR_DEPTH") in all_values: + return cls(os.environ["PROMPT_TOOLKIT_COLOR_DEPTH"]) + + return None + + @classmethod + def default(cls) -> "ColorDepth": + """ + Return the default color depth for the default output. + """ + from .defaults import create_output + + return create_output().get_default_color_depth() diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py index f1af406b8ca..ee1ac41d4d7 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py @@ -1,59 +1,59 @@ -from typing import Any, Optional, TextIO - -from prompt_toolkit.data_structures import Size - -from .base import Output -from .color_depth import ColorDepth -from .vt100 import Vt100_Output -from .win32 import Win32Output - -__all__ = [ - "ConEmuOutput", -] - - -class ConEmuOutput: - """ - ConEmu (Windows) output abstraction. - - ConEmu is a Windows console application, but it also supports ANSI escape - sequences. This output class is actually a proxy to both `Win32Output` and - `Vt100_Output`. It uses `Win32Output` for console sizing and scrolling, but - all cursor movements and scrolling happens through the `Vt100_Output`. - - This way, we can have 256 colors in ConEmu and Cmder. Rendering will be - even a little faster as well. - - http://conemu.github.io/ - http://gooseberrycreative.com/cmder/ - """ - - def __init__( - self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None - ) -> None: - self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth) - self.vt100_output = Vt100_Output( - stdout, lambda: Size(0, 0), default_color_depth=default_color_depth - ) - - @property - def responds_to_cpr(self) -> bool: - return False # We don't need this on Windows. - - def __getattr__(self, name: str) -> Any: - if name in ( - "get_size", - "get_rows_below_cursor_position", - "enable_mouse_support", - "disable_mouse_support", - "scroll_buffer_to_prompt", - "get_win32_screen_buffer_info", - "enable_bracketed_paste", - "disable_bracketed_paste", - ): - return getattr(self.win32_output, name) - else: - return getattr(self.vt100_output, name) - - -Output.register(ConEmuOutput) +from typing import Any, Optional, TextIO + +from prompt_toolkit.data_structures import Size + +from .base import Output +from .color_depth import ColorDepth +from .vt100 import Vt100_Output +from .win32 import Win32Output + +__all__ = [ + "ConEmuOutput", +] + + +class ConEmuOutput: + """ + ConEmu (Windows) output abstraction. + + ConEmu is a Windows console application, but it also supports ANSI escape + sequences. This output class is actually a proxy to both `Win32Output` and + `Vt100_Output`. It uses `Win32Output` for console sizing and scrolling, but + all cursor movements and scrolling happens through the `Vt100_Output`. + + This way, we can have 256 colors in ConEmu and Cmder. Rendering will be + even a little faster as well. + + http://conemu.github.io/ + http://gooseberrycreative.com/cmder/ + """ + + def __init__( + self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None + ) -> None: + self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth) + self.vt100_output = Vt100_Output( + stdout, lambda: Size(0, 0), default_color_depth=default_color_depth + ) + + @property + def responds_to_cpr(self) -> bool: + return False # We don't need this on Windows. + + def __getattr__(self, name: str) -> Any: + if name in ( + "get_size", + "get_rows_below_cursor_position", + "enable_mouse_support", + "disable_mouse_support", + "scroll_buffer_to_prompt", + "get_win32_screen_buffer_info", + "enable_bracketed_paste", + "disable_bracketed_paste", + ): + return getattr(self.win32_output, name) + else: + return getattr(self.vt100_output, name) + + +Output.register(ConEmuOutput) diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py index e2a1ef95e99..bd4bf950c43 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py @@ -1,31 +1,31 @@ -import sys -from typing import Optional, TextIO, cast - -from prompt_toolkit.utils import ( - get_bell_environment_variable, - get_term_environment_variable, - is_conemu_ansi, - is_windows, -) - +import sys +from typing import Optional, TextIO, cast + +from prompt_toolkit.utils import ( + get_bell_environment_variable, + get_term_environment_variable, + is_conemu_ansi, + is_windows, +) + from .base import DummyOutput, Output -from .color_depth import ColorDepth +from .color_depth import ColorDepth from .plain_text import PlainTextOutput - -__all__ = [ - "create_output", -] - - -def create_output( + +__all__ = [ + "create_output", +] + + +def create_output( stdout: Optional[TextIO] = None, always_prefer_tty: bool = False -) -> Output: - """ - Return an :class:`~prompt_toolkit.output.Output` instance for the command - line. - - :param stdout: The stdout object - :param always_prefer_tty: When set, look for `sys.stderr` if `sys.stdout` +) -> Output: + """ + Return an :class:`~prompt_toolkit.output.Output` instance for the command + line. + + :param stdout: The stdout object + :param always_prefer_tty: When set, look for `sys.stderr` if `sys.stdout` is not a TTY. Useful if `sys.stdout` is redirected to a file, but we still want user input and output on the terminal. @@ -33,26 +33,26 @@ def create_output( it's redirected to a file), then a `PlainTextOutput` will be returned. That way, tools like `print_formatted_text` will write plain text into that file. - """ - # Consider TERM, PROMPT_TOOLKIT_BELL, and PROMPT_TOOLKIT_COLOR_DEPTH - # environment variables. Notice that PROMPT_TOOLKIT_COLOR_DEPTH value is - # the default that's used if the Application doesn't override it. - term_from_env = get_term_environment_variable() - bell_from_env = get_bell_environment_variable() - color_depth_from_env = ColorDepth.from_env() - - if stdout is None: - # By default, render to stdout. If the output is piped somewhere else, - # render to stderr. - stdout = sys.stdout - - if always_prefer_tty: - for io in [sys.stdout, sys.stderr]: + """ + # Consider TERM, PROMPT_TOOLKIT_BELL, and PROMPT_TOOLKIT_COLOR_DEPTH + # environment variables. Notice that PROMPT_TOOLKIT_COLOR_DEPTH value is + # the default that's used if the Application doesn't override it. + term_from_env = get_term_environment_variable() + bell_from_env = get_bell_environment_variable() + color_depth_from_env = ColorDepth.from_env() + + if stdout is None: + # By default, render to stdout. If the output is piped somewhere else, + # render to stderr. + stdout = sys.stdout + + if always_prefer_tty: + for io in [sys.stdout, sys.stderr]: if io is not None and io.isatty(): # (This is `None` when using `pythonw.exe` on Windows.) - stdout = io - break - + stdout = io + break + # If the output is still `None`, use a DummyOutput. # This happens for instance on Windows, when running the application under # `pythonw.exe`. In that case, there won't be a terminal Window, and @@ -60,42 +60,42 @@ def create_output( if stdout is None: return DummyOutput() - # If the patch_stdout context manager has been used, then sys.stdout is - # replaced by this proxy. For prompt_toolkit applications, we want to use - # the real stdout. - from prompt_toolkit.patch_stdout import StdoutProxy - - while isinstance(stdout, StdoutProxy): - stdout = stdout.original_stdout - - if is_windows(): - from .conemu import ConEmuOutput - from .win32 import Win32Output - from .windows10 import Windows10_Output, is_win_vt100_enabled - - if is_win_vt100_enabled(): - return cast( - Output, - Windows10_Output(stdout, default_color_depth=color_depth_from_env), - ) - if is_conemu_ansi(): - return cast( - Output, ConEmuOutput(stdout, default_color_depth=color_depth_from_env) - ) - else: - return Win32Output(stdout, default_color_depth=color_depth_from_env) - else: - from .vt100 import Vt100_Output - + # If the patch_stdout context manager has been used, then sys.stdout is + # replaced by this proxy. For prompt_toolkit applications, we want to use + # the real stdout. + from prompt_toolkit.patch_stdout import StdoutProxy + + while isinstance(stdout, StdoutProxy): + stdout = stdout.original_stdout + + if is_windows(): + from .conemu import ConEmuOutput + from .win32 import Win32Output + from .windows10 import Windows10_Output, is_win_vt100_enabled + + if is_win_vt100_enabled(): + return cast( + Output, + Windows10_Output(stdout, default_color_depth=color_depth_from_env), + ) + if is_conemu_ansi(): + return cast( + Output, ConEmuOutput(stdout, default_color_depth=color_depth_from_env) + ) + else: + return Win32Output(stdout, default_color_depth=color_depth_from_env) + else: + from .vt100 import Vt100_Output + # Stdout is not a TTY? Render as plain text. # This is mostly useful if stdout is redirected to a file, and # `print_formatted_text` is used. if not stdout.isatty(): return PlainTextOutput(stdout) - return Vt100_Output.from_pty( - stdout, - term=term_from_env, - default_color_depth=color_depth_from_env, - enable_bell=bell_from_env, - ) + return Vt100_Output.from_pty( + stdout, + term=term_from_env, + default_color_depth=color_depth_from_env, + enable_bell=bell_from_env, + ) diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/vt100.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/vt100.py index fe7f85dae7a..05862672862 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/vt100.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/vt100.py @@ -1,669 +1,669 @@ -""" -Output for vt100 terminals. - -A lot of thanks, regarding outputting of colors, goes to the Pygments project: -(We don't rely on Pygments anymore, because many things are very custom, and -everything has been highly optimized.) -http://pygments.org/ -""" -import io -import os -import sys -from typing import ( - Callable, - Dict, - Hashable, - Iterable, - List, - Optional, - Sequence, - Set, - TextIO, - Tuple, -) - +""" +Output for vt100 terminals. + +A lot of thanks, regarding outputting of colors, goes to the Pygments project: +(We don't rely on Pygments anymore, because many things are very custom, and +everything has been highly optimized.) +http://pygments.org/ +""" +import io +import os +import sys +from typing import ( + Callable, + Dict, + Hashable, + Iterable, + List, + Optional, + Sequence, + Set, + TextIO, + Tuple, +) + from prompt_toolkit.cursor_shapes import CursorShape -from prompt_toolkit.data_structures import Size -from prompt_toolkit.output import Output -from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs -from prompt_toolkit.utils import is_dumb_terminal - -from .color_depth import ColorDepth +from prompt_toolkit.data_structures import Size +from prompt_toolkit.output import Output +from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs +from prompt_toolkit.utils import is_dumb_terminal + +from .color_depth import ColorDepth from .flush_stdout import flush_stdout - -__all__ = [ - "Vt100_Output", -] - - -FG_ANSI_COLORS = { - "ansidefault": 39, - # Low intensity. - "ansiblack": 30, - "ansired": 31, - "ansigreen": 32, - "ansiyellow": 33, - "ansiblue": 34, - "ansimagenta": 35, - "ansicyan": 36, - "ansigray": 37, - # High intensity. - "ansibrightblack": 90, - "ansibrightred": 91, - "ansibrightgreen": 92, - "ansibrightyellow": 93, - "ansibrightblue": 94, - "ansibrightmagenta": 95, - "ansibrightcyan": 96, - "ansiwhite": 97, -} - -BG_ANSI_COLORS = { - "ansidefault": 49, - # Low intensity. - "ansiblack": 40, - "ansired": 41, - "ansigreen": 42, - "ansiyellow": 43, - "ansiblue": 44, - "ansimagenta": 45, - "ansicyan": 46, - "ansigray": 47, - # High intensity. - "ansibrightblack": 100, - "ansibrightred": 101, - "ansibrightgreen": 102, - "ansibrightyellow": 103, - "ansibrightblue": 104, - "ansibrightmagenta": 105, - "ansibrightcyan": 106, - "ansiwhite": 107, -} - - -ANSI_COLORS_TO_RGB = { - "ansidefault": ( - 0x00, - 0x00, - 0x00, - ), # Don't use, 'default' doesn't really have a value. - "ansiblack": (0x00, 0x00, 0x00), - "ansigray": (0xE5, 0xE5, 0xE5), - "ansibrightblack": (0x7F, 0x7F, 0x7F), - "ansiwhite": (0xFF, 0xFF, 0xFF), - # Low intensity. - "ansired": (0xCD, 0x00, 0x00), - "ansigreen": (0x00, 0xCD, 0x00), - "ansiyellow": (0xCD, 0xCD, 0x00), - "ansiblue": (0x00, 0x00, 0xCD), - "ansimagenta": (0xCD, 0x00, 0xCD), - "ansicyan": (0x00, 0xCD, 0xCD), - # High intensity. - "ansibrightred": (0xFF, 0x00, 0x00), - "ansibrightgreen": (0x00, 0xFF, 0x00), - "ansibrightyellow": (0xFF, 0xFF, 0x00), - "ansibrightblue": (0x00, 0x00, 0xFF), - "ansibrightmagenta": (0xFF, 0x00, 0xFF), - "ansibrightcyan": (0x00, 0xFF, 0xFF), -} - - -assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) -assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) -assert set(ANSI_COLORS_TO_RGB) == set(ANSI_COLOR_NAMES) - - -def _get_closest_ansi_color(r: int, g: int, b: int, exclude: Sequence[str] = ()) -> str: - """ - Find closest ANSI color. Return it by name. - - :param r: Red (Between 0 and 255.) - :param g: Green (Between 0 and 255.) - :param b: Blue (Between 0 and 255.) - :param exclude: A tuple of color names to exclude. (E.g. ``('ansired', )``.) - """ - exclude = list(exclude) - - # When we have a bit of saturation, avoid the gray-like colors, otherwise, - # too often the distance to the gray color is less. - saturation = abs(r - g) + abs(g - b) + abs(b - r) # Between 0..510 - - if saturation > 30: - exclude.extend(["ansilightgray", "ansidarkgray", "ansiwhite", "ansiblack"]) - - # Take the closest color. - # (Thanks to Pygments for this part.) - distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) - match = "ansidefault" - - for name, (r2, g2, b2) in ANSI_COLORS_TO_RGB.items(): - if name != "ansidefault" and name not in exclude: - d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 - - if d < distance: - match = name - distance = d - - return match - - -_ColorCodeAndName = Tuple[int, str] - - -class _16ColorCache: - """ - Cache which maps (r, g, b) tuples to 16 ansi colors. - - :param bg: Cache for background colors, instead of foreground. - """ - - def __init__(self, bg: bool = False) -> None: - self.bg = bg - self._cache: Dict[Hashable, _ColorCodeAndName] = {} - - def get_code( - self, value: Tuple[int, int, int], exclude: Sequence[str] = () - ) -> _ColorCodeAndName: - """ - Return a (ansi_code, ansi_name) tuple. (E.g. ``(44, 'ansiblue')``.) for - a given (r,g,b) value. - """ - key: Hashable = (value, tuple(exclude)) - cache = self._cache - - if key not in cache: - cache[key] = self._get(value, exclude) - - return cache[key] - - def _get( - self, value: Tuple[int, int, int], exclude: Sequence[str] = () - ) -> _ColorCodeAndName: - - r, g, b = value - match = _get_closest_ansi_color(r, g, b, exclude=exclude) - - # Turn color name into code. - if self.bg: - code = BG_ANSI_COLORS[match] - else: - code = FG_ANSI_COLORS[match] - - return code, match - - -class _256ColorCache(Dict[Tuple[int, int, int], int]): - """ - Cache which maps (r, g, b) tuples to 256 colors. - """ - - def __init__(self) -> None: - # Build color table. - colors: List[Tuple[int, int, int]] = [] - - # colors 0..15: 16 basic colors - colors.append((0x00, 0x00, 0x00)) # 0 - colors.append((0xCD, 0x00, 0x00)) # 1 - colors.append((0x00, 0xCD, 0x00)) # 2 - colors.append((0xCD, 0xCD, 0x00)) # 3 - colors.append((0x00, 0x00, 0xEE)) # 4 - colors.append((0xCD, 0x00, 0xCD)) # 5 - colors.append((0x00, 0xCD, 0xCD)) # 6 - colors.append((0xE5, 0xE5, 0xE5)) # 7 - colors.append((0x7F, 0x7F, 0x7F)) # 8 - colors.append((0xFF, 0x00, 0x00)) # 9 - colors.append((0x00, 0xFF, 0x00)) # 10 - colors.append((0xFF, 0xFF, 0x00)) # 11 - colors.append((0x5C, 0x5C, 0xFF)) # 12 - colors.append((0xFF, 0x00, 0xFF)) # 13 - colors.append((0x00, 0xFF, 0xFF)) # 14 - colors.append((0xFF, 0xFF, 0xFF)) # 15 - - # colors 16..232: the 6x6x6 color cube - valuerange = (0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF) - - for i in range(217): - r = valuerange[(i // 36) % 6] - g = valuerange[(i // 6) % 6] - b = valuerange[i % 6] - colors.append((r, g, b)) - - # colors 233..253: grayscale - for i in range(1, 22): - v = 8 + i * 10 - colors.append((v, v, v)) - - self.colors = colors - - def __missing__(self, value: Tuple[int, int, int]) -> int: - r, g, b = value - - # Find closest color. - # (Thanks to Pygments for this!) - distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) - match = 0 - - for i, (r2, g2, b2) in enumerate(self.colors): - if i >= 16: # XXX: We ignore the 16 ANSI colors when mapping RGB - # to the 256 colors, because these highly depend on - # the color scheme of the terminal. - d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 - - if d < distance: - match = i - distance = d - - # Turn color name into code. - self[value] = match - return match - - -_16_fg_colors = _16ColorCache(bg=False) -_16_bg_colors = _16ColorCache(bg=True) -_256_colors = _256ColorCache() - - -class _EscapeCodeCache(Dict[Attrs, str]): - """ - Cache for VT100 escape codes. It maps - (fgcolor, bgcolor, bold, underline, strike, reverse) tuples to VT100 - escape sequences. - - :param true_color: When True, use 24bit colors instead of 256 colors. - """ - - def __init__(self, color_depth: ColorDepth) -> None: - self.color_depth = color_depth - - def __missing__(self, attrs: Attrs) -> str: - ( - fgcolor, - bgcolor, - bold, - underline, - strike, - italic, - blink, - reverse, - hidden, - ) = attrs - parts: List[str] = [] - - parts.extend(self._colors_to_code(fgcolor or "", bgcolor or "")) - - if bold: - parts.append("1") - if italic: - parts.append("3") - if blink: - parts.append("5") - if underline: - parts.append("4") - if reverse: - parts.append("7") - if hidden: - parts.append("8") - if strike: - parts.append("9") - - if parts: - result = "\x1b[0;" + ";".join(parts) + "m" - else: - result = "\x1b[0m" - - self[attrs] = result - return result - - def _color_name_to_rgb(self, color: str) -> Tuple[int, int, int]: - "Turn 'ffffff', into (0xff, 0xff, 0xff)." - try: - rgb = int(color, 16) - except ValueError: - raise - else: - r = (rgb >> 16) & 0xFF - g = (rgb >> 8) & 0xFF - b = rgb & 0xFF - return r, g, b - - def _colors_to_code(self, fg_color: str, bg_color: str) -> Iterable[str]: - """ - Return a tuple with the vt100 values that represent this color. - """ - # When requesting ANSI colors only, and both fg/bg color were converted - # to ANSI, ensure that the foreground and background color are not the - # same. (Unless they were explicitly defined to be the same color.) - fg_ansi = "" - - def get(color: str, bg: bool) -> List[int]: - nonlocal fg_ansi - - table = BG_ANSI_COLORS if bg else FG_ANSI_COLORS - - if not color or self.color_depth == ColorDepth.DEPTH_1_BIT: - return [] - - # 16 ANSI colors. (Given by name.) - elif color in table: - return [table[color]] - - # RGB colors. (Defined as 'ffffff'.) - else: - try: - rgb = self._color_name_to_rgb(color) - except ValueError: - return [] - - # When only 16 colors are supported, use that. - if self.color_depth == ColorDepth.DEPTH_4_BIT: - if bg: # Background. - if fg_color != bg_color: - exclude = [fg_ansi] - else: - exclude = [] - code, name = _16_bg_colors.get_code(rgb, exclude=exclude) - return [code] - else: # Foreground. - code, name = _16_fg_colors.get_code(rgb) - fg_ansi = name - return [code] - - # True colors. (Only when this feature is enabled.) - elif self.color_depth == ColorDepth.DEPTH_24_BIT: - r, g, b = rgb - return [(48 if bg else 38), 2, r, g, b] - - # 256 RGB colors. - else: - return [(48 if bg else 38), 5, _256_colors[rgb]] - - result: List[int] = [] - result.extend(get(fg_color, False)) - result.extend(get(bg_color, True)) - - return map(str, result) - - -def _get_size(fileno: int) -> Tuple[int, int]: - """ - Get the size of this pseudo terminal. - - :param fileno: stdout.fileno() - :returns: A (rows, cols) tuple. - """ - size = os.get_terminal_size(fileno) - return size.lines, size.columns - - -class Vt100_Output(Output): - """ - :param get_size: A callable which returns the `Size` of the output terminal. - :param stdout: Any object with has a `write` and `flush` method + an 'encoding' property. - :param term: The terminal environment variable. (xterm, xterm-256color, linux, ...) - :param write_binary: Encode the output before writing it. If `True` (the - default), the `stdout` object is supposed to expose an `encoding` attribute. - """ - - # For the error messages. Only display "Output is not a terminal" once per - # file descriptor. - _fds_not_a_terminal: Set[int] = set() - - def __init__( - self, - stdout: TextIO, - get_size: Callable[[], Size], - term: Optional[str] = None, - write_binary: bool = True, - default_color_depth: Optional[ColorDepth] = None, - enable_bell: bool = True, - ) -> None: - - assert all(hasattr(stdout, a) for a in ("write", "flush")) - - if write_binary: - assert hasattr(stdout, "encoding") - - self._buffer: List[str] = [] - self.stdout: TextIO = stdout - self.write_binary = write_binary - self.default_color_depth = default_color_depth - self._get_size = get_size - self.term = term - self.enable_bell = enable_bell - - # Cache for escape codes. - self._escape_code_caches: Dict[ColorDepth, _EscapeCodeCache] = { - ColorDepth.DEPTH_1_BIT: _EscapeCodeCache(ColorDepth.DEPTH_1_BIT), - ColorDepth.DEPTH_4_BIT: _EscapeCodeCache(ColorDepth.DEPTH_4_BIT), - ColorDepth.DEPTH_8_BIT: _EscapeCodeCache(ColorDepth.DEPTH_8_BIT), - ColorDepth.DEPTH_24_BIT: _EscapeCodeCache(ColorDepth.DEPTH_24_BIT), - } - + +__all__ = [ + "Vt100_Output", +] + + +FG_ANSI_COLORS = { + "ansidefault": 39, + # Low intensity. + "ansiblack": 30, + "ansired": 31, + "ansigreen": 32, + "ansiyellow": 33, + "ansiblue": 34, + "ansimagenta": 35, + "ansicyan": 36, + "ansigray": 37, + # High intensity. + "ansibrightblack": 90, + "ansibrightred": 91, + "ansibrightgreen": 92, + "ansibrightyellow": 93, + "ansibrightblue": 94, + "ansibrightmagenta": 95, + "ansibrightcyan": 96, + "ansiwhite": 97, +} + +BG_ANSI_COLORS = { + "ansidefault": 49, + # Low intensity. + "ansiblack": 40, + "ansired": 41, + "ansigreen": 42, + "ansiyellow": 43, + "ansiblue": 44, + "ansimagenta": 45, + "ansicyan": 46, + "ansigray": 47, + # High intensity. + "ansibrightblack": 100, + "ansibrightred": 101, + "ansibrightgreen": 102, + "ansibrightyellow": 103, + "ansibrightblue": 104, + "ansibrightmagenta": 105, + "ansibrightcyan": 106, + "ansiwhite": 107, +} + + +ANSI_COLORS_TO_RGB = { + "ansidefault": ( + 0x00, + 0x00, + 0x00, + ), # Don't use, 'default' doesn't really have a value. + "ansiblack": (0x00, 0x00, 0x00), + "ansigray": (0xE5, 0xE5, 0xE5), + "ansibrightblack": (0x7F, 0x7F, 0x7F), + "ansiwhite": (0xFF, 0xFF, 0xFF), + # Low intensity. + "ansired": (0xCD, 0x00, 0x00), + "ansigreen": (0x00, 0xCD, 0x00), + "ansiyellow": (0xCD, 0xCD, 0x00), + "ansiblue": (0x00, 0x00, 0xCD), + "ansimagenta": (0xCD, 0x00, 0xCD), + "ansicyan": (0x00, 0xCD, 0xCD), + # High intensity. + "ansibrightred": (0xFF, 0x00, 0x00), + "ansibrightgreen": (0x00, 0xFF, 0x00), + "ansibrightyellow": (0xFF, 0xFF, 0x00), + "ansibrightblue": (0x00, 0x00, 0xFF), + "ansibrightmagenta": (0xFF, 0x00, 0xFF), + "ansibrightcyan": (0x00, 0xFF, 0xFF), +} + + +assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) +assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) +assert set(ANSI_COLORS_TO_RGB) == set(ANSI_COLOR_NAMES) + + +def _get_closest_ansi_color(r: int, g: int, b: int, exclude: Sequence[str] = ()) -> str: + """ + Find closest ANSI color. Return it by name. + + :param r: Red (Between 0 and 255.) + :param g: Green (Between 0 and 255.) + :param b: Blue (Between 0 and 255.) + :param exclude: A tuple of color names to exclude. (E.g. ``('ansired', )``.) + """ + exclude = list(exclude) + + # When we have a bit of saturation, avoid the gray-like colors, otherwise, + # too often the distance to the gray color is less. + saturation = abs(r - g) + abs(g - b) + abs(b - r) # Between 0..510 + + if saturation > 30: + exclude.extend(["ansilightgray", "ansidarkgray", "ansiwhite", "ansiblack"]) + + # Take the closest color. + # (Thanks to Pygments for this part.) + distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) + match = "ansidefault" + + for name, (r2, g2, b2) in ANSI_COLORS_TO_RGB.items(): + if name != "ansidefault" and name not in exclude: + d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 + + if d < distance: + match = name + distance = d + + return match + + +_ColorCodeAndName = Tuple[int, str] + + +class _16ColorCache: + """ + Cache which maps (r, g, b) tuples to 16 ansi colors. + + :param bg: Cache for background colors, instead of foreground. + """ + + def __init__(self, bg: bool = False) -> None: + self.bg = bg + self._cache: Dict[Hashable, _ColorCodeAndName] = {} + + def get_code( + self, value: Tuple[int, int, int], exclude: Sequence[str] = () + ) -> _ColorCodeAndName: + """ + Return a (ansi_code, ansi_name) tuple. (E.g. ``(44, 'ansiblue')``.) for + a given (r,g,b) value. + """ + key: Hashable = (value, tuple(exclude)) + cache = self._cache + + if key not in cache: + cache[key] = self._get(value, exclude) + + return cache[key] + + def _get( + self, value: Tuple[int, int, int], exclude: Sequence[str] = () + ) -> _ColorCodeAndName: + + r, g, b = value + match = _get_closest_ansi_color(r, g, b, exclude=exclude) + + # Turn color name into code. + if self.bg: + code = BG_ANSI_COLORS[match] + else: + code = FG_ANSI_COLORS[match] + + return code, match + + +class _256ColorCache(Dict[Tuple[int, int, int], int]): + """ + Cache which maps (r, g, b) tuples to 256 colors. + """ + + def __init__(self) -> None: + # Build color table. + colors: List[Tuple[int, int, int]] = [] + + # colors 0..15: 16 basic colors + colors.append((0x00, 0x00, 0x00)) # 0 + colors.append((0xCD, 0x00, 0x00)) # 1 + colors.append((0x00, 0xCD, 0x00)) # 2 + colors.append((0xCD, 0xCD, 0x00)) # 3 + colors.append((0x00, 0x00, 0xEE)) # 4 + colors.append((0xCD, 0x00, 0xCD)) # 5 + colors.append((0x00, 0xCD, 0xCD)) # 6 + colors.append((0xE5, 0xE5, 0xE5)) # 7 + colors.append((0x7F, 0x7F, 0x7F)) # 8 + colors.append((0xFF, 0x00, 0x00)) # 9 + colors.append((0x00, 0xFF, 0x00)) # 10 + colors.append((0xFF, 0xFF, 0x00)) # 11 + colors.append((0x5C, 0x5C, 0xFF)) # 12 + colors.append((0xFF, 0x00, 0xFF)) # 13 + colors.append((0x00, 0xFF, 0xFF)) # 14 + colors.append((0xFF, 0xFF, 0xFF)) # 15 + + # colors 16..232: the 6x6x6 color cube + valuerange = (0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF) + + for i in range(217): + r = valuerange[(i // 36) % 6] + g = valuerange[(i // 6) % 6] + b = valuerange[i % 6] + colors.append((r, g, b)) + + # colors 233..253: grayscale + for i in range(1, 22): + v = 8 + i * 10 + colors.append((v, v, v)) + + self.colors = colors + + def __missing__(self, value: Tuple[int, int, int]) -> int: + r, g, b = value + + # Find closest color. + # (Thanks to Pygments for this!) + distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) + match = 0 + + for i, (r2, g2, b2) in enumerate(self.colors): + if i >= 16: # XXX: We ignore the 16 ANSI colors when mapping RGB + # to the 256 colors, because these highly depend on + # the color scheme of the terminal. + d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 + + if d < distance: + match = i + distance = d + + # Turn color name into code. + self[value] = match + return match + + +_16_fg_colors = _16ColorCache(bg=False) +_16_bg_colors = _16ColorCache(bg=True) +_256_colors = _256ColorCache() + + +class _EscapeCodeCache(Dict[Attrs, str]): + """ + Cache for VT100 escape codes. It maps + (fgcolor, bgcolor, bold, underline, strike, reverse) tuples to VT100 + escape sequences. + + :param true_color: When True, use 24bit colors instead of 256 colors. + """ + + def __init__(self, color_depth: ColorDepth) -> None: + self.color_depth = color_depth + + def __missing__(self, attrs: Attrs) -> str: + ( + fgcolor, + bgcolor, + bold, + underline, + strike, + italic, + blink, + reverse, + hidden, + ) = attrs + parts: List[str] = [] + + parts.extend(self._colors_to_code(fgcolor or "", bgcolor or "")) + + if bold: + parts.append("1") + if italic: + parts.append("3") + if blink: + parts.append("5") + if underline: + parts.append("4") + if reverse: + parts.append("7") + if hidden: + parts.append("8") + if strike: + parts.append("9") + + if parts: + result = "\x1b[0;" + ";".join(parts) + "m" + else: + result = "\x1b[0m" + + self[attrs] = result + return result + + def _color_name_to_rgb(self, color: str) -> Tuple[int, int, int]: + "Turn 'ffffff', into (0xff, 0xff, 0xff)." + try: + rgb = int(color, 16) + except ValueError: + raise + else: + r = (rgb >> 16) & 0xFF + g = (rgb >> 8) & 0xFF + b = rgb & 0xFF + return r, g, b + + def _colors_to_code(self, fg_color: str, bg_color: str) -> Iterable[str]: + """ + Return a tuple with the vt100 values that represent this color. + """ + # When requesting ANSI colors only, and both fg/bg color were converted + # to ANSI, ensure that the foreground and background color are not the + # same. (Unless they were explicitly defined to be the same color.) + fg_ansi = "" + + def get(color: str, bg: bool) -> List[int]: + nonlocal fg_ansi + + table = BG_ANSI_COLORS if bg else FG_ANSI_COLORS + + if not color or self.color_depth == ColorDepth.DEPTH_1_BIT: + return [] + + # 16 ANSI colors. (Given by name.) + elif color in table: + return [table[color]] + + # RGB colors. (Defined as 'ffffff'.) + else: + try: + rgb = self._color_name_to_rgb(color) + except ValueError: + return [] + + # When only 16 colors are supported, use that. + if self.color_depth == ColorDepth.DEPTH_4_BIT: + if bg: # Background. + if fg_color != bg_color: + exclude = [fg_ansi] + else: + exclude = [] + code, name = _16_bg_colors.get_code(rgb, exclude=exclude) + return [code] + else: # Foreground. + code, name = _16_fg_colors.get_code(rgb) + fg_ansi = name + return [code] + + # True colors. (Only when this feature is enabled.) + elif self.color_depth == ColorDepth.DEPTH_24_BIT: + r, g, b = rgb + return [(48 if bg else 38), 2, r, g, b] + + # 256 RGB colors. + else: + return [(48 if bg else 38), 5, _256_colors[rgb]] + + result: List[int] = [] + result.extend(get(fg_color, False)) + result.extend(get(bg_color, True)) + + return map(str, result) + + +def _get_size(fileno: int) -> Tuple[int, int]: + """ + Get the size of this pseudo terminal. + + :param fileno: stdout.fileno() + :returns: A (rows, cols) tuple. + """ + size = os.get_terminal_size(fileno) + return size.lines, size.columns + + +class Vt100_Output(Output): + """ + :param get_size: A callable which returns the `Size` of the output terminal. + :param stdout: Any object with has a `write` and `flush` method + an 'encoding' property. + :param term: The terminal environment variable. (xterm, xterm-256color, linux, ...) + :param write_binary: Encode the output before writing it. If `True` (the + default), the `stdout` object is supposed to expose an `encoding` attribute. + """ + + # For the error messages. Only display "Output is not a terminal" once per + # file descriptor. + _fds_not_a_terminal: Set[int] = set() + + def __init__( + self, + stdout: TextIO, + get_size: Callable[[], Size], + term: Optional[str] = None, + write_binary: bool = True, + default_color_depth: Optional[ColorDepth] = None, + enable_bell: bool = True, + ) -> None: + + assert all(hasattr(stdout, a) for a in ("write", "flush")) + + if write_binary: + assert hasattr(stdout, "encoding") + + self._buffer: List[str] = [] + self.stdout: TextIO = stdout + self.write_binary = write_binary + self.default_color_depth = default_color_depth + self._get_size = get_size + self.term = term + self.enable_bell = enable_bell + + # Cache for escape codes. + self._escape_code_caches: Dict[ColorDepth, _EscapeCodeCache] = { + ColorDepth.DEPTH_1_BIT: _EscapeCodeCache(ColorDepth.DEPTH_1_BIT), + ColorDepth.DEPTH_4_BIT: _EscapeCodeCache(ColorDepth.DEPTH_4_BIT), + ColorDepth.DEPTH_8_BIT: _EscapeCodeCache(ColorDepth.DEPTH_8_BIT), + ColorDepth.DEPTH_24_BIT: _EscapeCodeCache(ColorDepth.DEPTH_24_BIT), + } + # Keep track of whether the cursor shape was ever changed. # (We don't restore the cursor shape if it was never changed - by # default, we don't change them.) self._cursor_shape_changed = False - @classmethod - def from_pty( - cls, - stdout: TextIO, - term: Optional[str] = None, - default_color_depth: Optional[ColorDepth] = None, - enable_bell: bool = True, - ) -> "Vt100_Output": - """ - Create an Output class from a pseudo terminal. - (This will take the dimensions by reading the pseudo - terminal attributes.) - """ - fd: Optional[int] - # Normally, this requires a real TTY device, but people instantiate - # this class often during unit tests as well. For convenience, we print - # an error message, use standard dimensions, and go on. - try: - fd = stdout.fileno() - except io.UnsupportedOperation: - fd = None - - if not stdout.isatty() and (fd is None or fd not in cls._fds_not_a_terminal): - msg = "Warning: Output is not a terminal (fd=%r).\n" - sys.stderr.write(msg % fd) - sys.stderr.flush() - if fd is not None: - cls._fds_not_a_terminal.add(fd) - - def get_size() -> Size: - # If terminal (incorrectly) reports its size as 0, pick a - # reasonable default. See - # https://github.com/ipython/ipython/issues/10071 - rows, columns = (None, None) - - # It is possible that `stdout` is no longer a TTY device at this - # point. In that case we get an `OSError` in the ioctl call in - # `get_size`. See: - # https://github.com/prompt-toolkit/python-prompt-toolkit/pull/1021 - try: - rows, columns = _get_size(stdout.fileno()) - except OSError: - pass - return Size(rows=rows or 24, columns=columns or 80) - - return cls( - stdout, - get_size, - term=term, - default_color_depth=default_color_depth, - enable_bell=enable_bell, - ) - - def get_size(self) -> Size: - return self._get_size() - - def fileno(self) -> int: - "Return file descriptor." - return self.stdout.fileno() - - def encoding(self) -> str: - "Return encoding used for stdout." - return self.stdout.encoding - - def write_raw(self, data: str) -> None: - """ - Write raw data to output. - """ - self._buffer.append(data) - - def write(self, data: str) -> None: - """ - Write text to output. - (Removes vt100 escape codes. -- used for safely writing text.) - """ - self._buffer.append(data.replace("\x1b", "?")) - - def set_title(self, title: str) -> None: - """ - Set terminal title. - """ - if self.term not in ( - "linux", - "eterm-color", - ): # Not supported by the Linux console. - self.write_raw( - "\x1b]2;%s\x07" % title.replace("\x1b", "").replace("\x07", "") - ) - - def clear_title(self) -> None: - self.set_title("") - - def erase_screen(self) -> None: - """ - Erases the screen with the background colour and moves the cursor to - home. - """ - self.write_raw("\x1b[2J") - - def enter_alternate_screen(self) -> None: - self.write_raw("\x1b[?1049h\x1b[H") - - def quit_alternate_screen(self) -> None: - self.write_raw("\x1b[?1049l") - - def enable_mouse_support(self) -> None: - self.write_raw("\x1b[?1000h") - - # Enable mouse-drag support. - self.write_raw("\x1b[?1003h") - - # Enable urxvt Mouse mode. (For terminals that understand this.) - self.write_raw("\x1b[?1015h") - - # Also enable Xterm SGR mouse mode. (For terminals that understand this.) - self.write_raw("\x1b[?1006h") - - # Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr - # extensions. - - def disable_mouse_support(self) -> None: - self.write_raw("\x1b[?1000l") - self.write_raw("\x1b[?1015l") - self.write_raw("\x1b[?1006l") - self.write_raw("\x1b[?1003l") - - def erase_end_of_line(self) -> None: - """ - Erases from the current cursor position to the end of the current line. - """ - self.write_raw("\x1b[K") - - def erase_down(self) -> None: - """ - Erases the screen from the current line down to the bottom of the - screen. - """ - self.write_raw("\x1b[J") - - def reset_attributes(self) -> None: - self.write_raw("\x1b[0m") - - def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: - """ - Create new style and output. - - :param attrs: `Attrs` instance. - """ - # Get current depth. - escape_code_cache = self._escape_code_caches[color_depth] - - # Write escape character. - self.write_raw(escape_code_cache[attrs]) - - def disable_autowrap(self) -> None: - self.write_raw("\x1b[?7l") - - def enable_autowrap(self) -> None: - self.write_raw("\x1b[?7h") - - def enable_bracketed_paste(self) -> None: - self.write_raw("\x1b[?2004h") - - def disable_bracketed_paste(self) -> None: - self.write_raw("\x1b[?2004l") - - def reset_cursor_key_mode(self) -> None: - """ - For vt100 only. - Put the terminal in cursor mode (instead of application mode). - """ - # Put the terminal in cursor mode. (Instead of application mode.) - self.write_raw("\x1b[?1l") - - def cursor_goto(self, row: int = 0, column: int = 0) -> None: - """ - Move cursor position. - """ - self.write_raw("\x1b[%i;%iH" % (row, column)) - - def cursor_up(self, amount: int) -> None: - if amount == 0: - pass - elif amount == 1: - self.write_raw("\x1b[A") - else: - self.write_raw("\x1b[%iA" % amount) - - def cursor_down(self, amount: int) -> None: - if amount == 0: - pass - elif amount == 1: - # Note: Not the same as '\n', '\n' can cause the window content to - # scroll. - self.write_raw("\x1b[B") - else: - self.write_raw("\x1b[%iB" % amount) - - def cursor_forward(self, amount: int) -> None: - if amount == 0: - pass - elif amount == 1: - self.write_raw("\x1b[C") - else: - self.write_raw("\x1b[%iC" % amount) - - def cursor_backward(self, amount: int) -> None: - if amount == 0: - pass - elif amount == 1: - self.write_raw("\b") # '\x1b[D' - else: - self.write_raw("\x1b[%iD" % amount) - - def hide_cursor(self) -> None: - self.write_raw("\x1b[?25l") - - def show_cursor(self) -> None: - self.write_raw("\x1b[?12l\x1b[?25h") # Stop blinking cursor and show. - + @classmethod + def from_pty( + cls, + stdout: TextIO, + term: Optional[str] = None, + default_color_depth: Optional[ColorDepth] = None, + enable_bell: bool = True, + ) -> "Vt100_Output": + """ + Create an Output class from a pseudo terminal. + (This will take the dimensions by reading the pseudo + terminal attributes.) + """ + fd: Optional[int] + # Normally, this requires a real TTY device, but people instantiate + # this class often during unit tests as well. For convenience, we print + # an error message, use standard dimensions, and go on. + try: + fd = stdout.fileno() + except io.UnsupportedOperation: + fd = None + + if not stdout.isatty() and (fd is None or fd not in cls._fds_not_a_terminal): + msg = "Warning: Output is not a terminal (fd=%r).\n" + sys.stderr.write(msg % fd) + sys.stderr.flush() + if fd is not None: + cls._fds_not_a_terminal.add(fd) + + def get_size() -> Size: + # If terminal (incorrectly) reports its size as 0, pick a + # reasonable default. See + # https://github.com/ipython/ipython/issues/10071 + rows, columns = (None, None) + + # It is possible that `stdout` is no longer a TTY device at this + # point. In that case we get an `OSError` in the ioctl call in + # `get_size`. See: + # https://github.com/prompt-toolkit/python-prompt-toolkit/pull/1021 + try: + rows, columns = _get_size(stdout.fileno()) + except OSError: + pass + return Size(rows=rows or 24, columns=columns or 80) + + return cls( + stdout, + get_size, + term=term, + default_color_depth=default_color_depth, + enable_bell=enable_bell, + ) + + def get_size(self) -> Size: + return self._get_size() + + def fileno(self) -> int: + "Return file descriptor." + return self.stdout.fileno() + + def encoding(self) -> str: + "Return encoding used for stdout." + return self.stdout.encoding + + def write_raw(self, data: str) -> None: + """ + Write raw data to output. + """ + self._buffer.append(data) + + def write(self, data: str) -> None: + """ + Write text to output. + (Removes vt100 escape codes. -- used for safely writing text.) + """ + self._buffer.append(data.replace("\x1b", "?")) + + def set_title(self, title: str) -> None: + """ + Set terminal title. + """ + if self.term not in ( + "linux", + "eterm-color", + ): # Not supported by the Linux console. + self.write_raw( + "\x1b]2;%s\x07" % title.replace("\x1b", "").replace("\x07", "") + ) + + def clear_title(self) -> None: + self.set_title("") + + def erase_screen(self) -> None: + """ + Erases the screen with the background colour and moves the cursor to + home. + """ + self.write_raw("\x1b[2J") + + def enter_alternate_screen(self) -> None: + self.write_raw("\x1b[?1049h\x1b[H") + + def quit_alternate_screen(self) -> None: + self.write_raw("\x1b[?1049l") + + def enable_mouse_support(self) -> None: + self.write_raw("\x1b[?1000h") + + # Enable mouse-drag support. + self.write_raw("\x1b[?1003h") + + # Enable urxvt Mouse mode. (For terminals that understand this.) + self.write_raw("\x1b[?1015h") + + # Also enable Xterm SGR mouse mode. (For terminals that understand this.) + self.write_raw("\x1b[?1006h") + + # Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr + # extensions. + + def disable_mouse_support(self) -> None: + self.write_raw("\x1b[?1000l") + self.write_raw("\x1b[?1015l") + self.write_raw("\x1b[?1006l") + self.write_raw("\x1b[?1003l") + + def erase_end_of_line(self) -> None: + """ + Erases from the current cursor position to the end of the current line. + """ + self.write_raw("\x1b[K") + + def erase_down(self) -> None: + """ + Erases the screen from the current line down to the bottom of the + screen. + """ + self.write_raw("\x1b[J") + + def reset_attributes(self) -> None: + self.write_raw("\x1b[0m") + + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + """ + Create new style and output. + + :param attrs: `Attrs` instance. + """ + # Get current depth. + escape_code_cache = self._escape_code_caches[color_depth] + + # Write escape character. + self.write_raw(escape_code_cache[attrs]) + + def disable_autowrap(self) -> None: + self.write_raw("\x1b[?7l") + + def enable_autowrap(self) -> None: + self.write_raw("\x1b[?7h") + + def enable_bracketed_paste(self) -> None: + self.write_raw("\x1b[?2004h") + + def disable_bracketed_paste(self) -> None: + self.write_raw("\x1b[?2004l") + + def reset_cursor_key_mode(self) -> None: + """ + For vt100 only. + Put the terminal in cursor mode (instead of application mode). + """ + # Put the terminal in cursor mode. (Instead of application mode.) + self.write_raw("\x1b[?1l") + + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + """ + Move cursor position. + """ + self.write_raw("\x1b[%i;%iH" % (row, column)) + + def cursor_up(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + self.write_raw("\x1b[A") + else: + self.write_raw("\x1b[%iA" % amount) + + def cursor_down(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + # Note: Not the same as '\n', '\n' can cause the window content to + # scroll. + self.write_raw("\x1b[B") + else: + self.write_raw("\x1b[%iB" % amount) + + def cursor_forward(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + self.write_raw("\x1b[C") + else: + self.write_raw("\x1b[%iC" % amount) + + def cursor_backward(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + self.write_raw("\b") # '\x1b[D' + else: + self.write_raw("\x1b[%iD" % amount) + + def hide_cursor(self) -> None: + self.write_raw("\x1b[?25l") + + def show_cursor(self) -> None: + self.write_raw("\x1b[?12l\x1b[?25h") # Stop blinking cursor and show. + def set_cursor_shape(self, cursor_shape: CursorShape) -> None: if cursor_shape == CursorShape._NEVER_CHANGE: return @@ -689,65 +689,65 @@ class Vt100_Output(Output): # Reset cursor shape. self.write_raw("\x1b[0 q") - def flush(self) -> None: - """ - Write to output stream and flush. - """ - if not self._buffer: - return - - data = "".join(self._buffer) - self._buffer = [] - + def flush(self) -> None: + """ + Write to output stream and flush. + """ + if not self._buffer: + return + + data = "".join(self._buffer) + self._buffer = [] + flush_stdout(self.stdout, data, write_binary=self.write_binary) - - def ask_for_cpr(self) -> None: - """ - Asks for a cursor position report (CPR). - """ - self.write_raw("\x1b[6n") - self.flush() - - @property - def responds_to_cpr(self) -> bool: - # When the input is a tty, we assume that CPR is supported. - # It's not when the input is piped from Pexpect. - if os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1": - return False - - if is_dumb_terminal(self.term): - return False - try: - return self.stdout.isatty() - except ValueError: - return False # ValueError: I/O operation on closed file - - def bell(self) -> None: - "Sound bell." - if self.enable_bell: - self.write_raw("\a") - self.flush() - - def get_default_color_depth(self) -> ColorDepth: - """ - Return the default color depth for a vt100 terminal, according to the - our term value. - - We prefer 256 colors almost always, because this is what most terminals - support these days, and is a good default. - """ - if self.default_color_depth is not None: - return self.default_color_depth - - term = self.term - - if term is None: - return ColorDepth.DEFAULT - - if is_dumb_terminal(term): - return ColorDepth.DEPTH_1_BIT - - if term in ("linux", "eterm-color"): - return ColorDepth.DEPTH_4_BIT - - return ColorDepth.DEFAULT + + def ask_for_cpr(self) -> None: + """ + Asks for a cursor position report (CPR). + """ + self.write_raw("\x1b[6n") + self.flush() + + @property + def responds_to_cpr(self) -> bool: + # When the input is a tty, we assume that CPR is supported. + # It's not when the input is piped from Pexpect. + if os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1": + return False + + if is_dumb_terminal(self.term): + return False + try: + return self.stdout.isatty() + except ValueError: + return False # ValueError: I/O operation on closed file + + def bell(self) -> None: + "Sound bell." + if self.enable_bell: + self.write_raw("\a") + self.flush() + + def get_default_color_depth(self) -> ColorDepth: + """ + Return the default color depth for a vt100 terminal, according to the + our term value. + + We prefer 256 colors almost always, because this is what most terminals + support these days, and is a good default. + """ + if self.default_color_depth is not None: + return self.default_color_depth + + term = self.term + + if term is None: + return ColorDepth.DEFAULT + + if is_dumb_terminal(term): + return ColorDepth.DEPTH_1_BIT + + if term in ("linux", "eterm-color"): + return ColorDepth.DEPTH_4_BIT + + return ColorDepth.DEFAULT diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py index ca55bfdc6f6..abfd61774be 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py @@ -1,685 +1,685 @@ -import os +import os from ctypes import ArgumentError, byref, c_char, c_long, c_uint, c_ulong, pointer - -from ..utils import SPHINX_AUTODOC_RUNNING - -# Do not import win32-specific stuff when generating documentation. -# Otherwise RTD would be unable to generate docs for this module. -if not SPHINX_AUTODOC_RUNNING: - from ctypes import windll - -from ctypes.wintypes import DWORD, HANDLE -from typing import Callable, Dict, List, Optional, TextIO, Tuple, Type, TypeVar, Union - + +from ..utils import SPHINX_AUTODOC_RUNNING + +# Do not import win32-specific stuff when generating documentation. +# Otherwise RTD would be unable to generate docs for this module. +if not SPHINX_AUTODOC_RUNNING: + from ctypes import windll + +from ctypes.wintypes import DWORD, HANDLE +from typing import Callable, Dict, List, Optional, TextIO, Tuple, Type, TypeVar, Union + from prompt_toolkit.cursor_shapes import CursorShape -from prompt_toolkit.data_structures import Size -from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs -from prompt_toolkit.utils import get_cwidth -from prompt_toolkit.win32_types import ( - CONSOLE_SCREEN_BUFFER_INFO, - COORD, - SMALL_RECT, - STD_INPUT_HANDLE, - STD_OUTPUT_HANDLE, -) - -from .base import Output -from .color_depth import ColorDepth - -__all__ = [ - "Win32Output", -] - - -def _coord_byval(coord: COORD) -> c_long: - """ - Turns a COORD object into a c_long. - This will cause it to be passed by value instead of by reference. (That is what I think at least.) - - When running ``ptipython`` is run (only with IPython), we often got the following error:: - - Error in 'SetConsoleCursorPosition'. - ArgumentError("argument 2: <class 'TypeError'>: wrong type",) - argument 2: <class 'TypeError'>: wrong type - - It was solved by turning ``COORD`` parameters into a ``c_long`` like this. - - More info: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx - """ - return c_long(coord.Y * 0x10000 | coord.X & 0xFFFF) - - -#: If True: write the output of the renderer also to the following file. This -#: is very useful for debugging. (e.g.: to see that we don't write more bytes -#: than required.) -_DEBUG_RENDER_OUTPUT = False -_DEBUG_RENDER_OUTPUT_FILENAME = r"prompt-toolkit-windows-output.log" - - -class NoConsoleScreenBufferError(Exception): - """ - Raised when the application is not running inside a Windows Console, but - the user tries to instantiate Win32Output. - """ - - def __init__(self) -> None: - # Are we running in 'xterm' on Windows, like git-bash for instance? - xterm = "xterm" in os.environ.get("TERM", "") - - if xterm: - message = ( - "Found %s, while expecting a Windows console. " - 'Maybe try to run this program using "winpty" ' - "or run it in cmd.exe instead. Or otherwise, " - "in case of Cygwin, use the Python executable " - "that is compiled for Cygwin." % os.environ["TERM"] - ) - else: - message = "No Windows console found. Are you running cmd.exe?" - super().__init__(message) - - -_T = TypeVar("_T") - - -class Win32Output(Output): - """ - I/O abstraction for rendering to Windows consoles. - (cmd.exe and similar.) - """ - - def __init__( - self, - stdout: TextIO, - use_complete_width: bool = False, - default_color_depth: Optional[ColorDepth] = None, - ) -> None: - self.use_complete_width = use_complete_width - self.default_color_depth = default_color_depth - - self._buffer: List[str] = [] - self.stdout: TextIO = stdout - self.hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) - - self._in_alternate_screen = False - self._hidden = False - - self.color_lookup_table = ColorLookupTable() - - # Remember the default console colors. - info = self.get_win32_screen_buffer_info() - self.default_attrs = info.wAttributes if info else 15 - - if _DEBUG_RENDER_OUTPUT: - self.LOG = open(_DEBUG_RENDER_OUTPUT_FILENAME, "ab") - - def fileno(self) -> int: - "Return file descriptor." - return self.stdout.fileno() - - def encoding(self) -> str: - "Return encoding used for stdout." - return self.stdout.encoding - - def write(self, data: str) -> None: - if self._hidden: - data = " " * get_cwidth(data) - - self._buffer.append(data) - - def write_raw(self, data: str) -> None: - "For win32, there is no difference between write and write_raw." - self.write(data) - - def get_size(self) -> Size: - info = self.get_win32_screen_buffer_info() - - # We take the width of the *visible* region as the size. Not the width - # of the complete screen buffer. (Unless use_complete_width has been - # set.) - if self.use_complete_width: - width = info.dwSize.X - else: - width = info.srWindow.Right - info.srWindow.Left - - height = info.srWindow.Bottom - info.srWindow.Top + 1 - - # We avoid the right margin, windows will wrap otherwise. - maxwidth = info.dwSize.X - 1 - width = min(maxwidth, width) - - # Create `Size` object. - return Size(rows=height, columns=width) - - def _winapi(self, func: Callable[..., _T], *a: object, **kw: object) -> _T: - """ - Flush and call win API function. - """ - self.flush() - - if _DEBUG_RENDER_OUTPUT: - self.LOG.write(("%r" % func.__name__).encode("utf-8") + b"\n") - self.LOG.write( - b" " + ", ".join(["%r" % i for i in a]).encode("utf-8") + b"\n" - ) - self.LOG.write( - b" " - + ", ".join(["%r" % type(i) for i in a]).encode("utf-8") - + b"\n" - ) - self.LOG.flush() - - try: - return func(*a, **kw) - except ArgumentError as e: - if _DEBUG_RENDER_OUTPUT: - self.LOG.write( - (" Error in %r %r %s\n" % (func.__name__, e, e)).encode("utf-8") - ) - - raise - - def get_win32_screen_buffer_info(self) -> CONSOLE_SCREEN_BUFFER_INFO: - """ - Return Screen buffer info. - """ - # NOTE: We don't call the `GetConsoleScreenBufferInfo` API through - # `self._winapi`. Doing so causes Python to crash on certain 64bit - # Python versions. (Reproduced with 64bit Python 2.7.6, on Windows - # 10). It is not clear why. Possibly, it has to do with passing - # these objects as an argument, or through *args. - - # The Python documentation contains the following - possibly related - warning: - # ctypes does not support passing unions or structures with - # bit-fields to functions by value. While this may work on 32-bit - # x86, it's not guaranteed by the library to work in the general - # case. Unions and structures with bit-fields should always be - # passed to functions by pointer. - - # Also see: - # - https://github.com/ipython/ipython/issues/10070 - # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/406 - # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/86 - - self.flush() - sbinfo = CONSOLE_SCREEN_BUFFER_INFO() - success = windll.kernel32.GetConsoleScreenBufferInfo( - self.hconsole, byref(sbinfo) - ) - - # success = self._winapi(windll.kernel32.GetConsoleScreenBufferInfo, - # self.hconsole, byref(sbinfo)) - - if success: - return sbinfo - else: - raise NoConsoleScreenBufferError - - def set_title(self, title: str) -> None: - """ - Set terminal title. - """ - self._winapi(windll.kernel32.SetConsoleTitleW, title) - - def clear_title(self) -> None: - self._winapi(windll.kernel32.SetConsoleTitleW, "") - - def erase_screen(self) -> None: - start = COORD(0, 0) - sbinfo = self.get_win32_screen_buffer_info() - length = sbinfo.dwSize.X * sbinfo.dwSize.Y - - self.cursor_goto(row=0, column=0) - self._erase(start, length) - - def erase_down(self) -> None: - sbinfo = self.get_win32_screen_buffer_info() - size = sbinfo.dwSize - - start = sbinfo.dwCursorPosition - length = (size.X - size.X) + size.X * (size.Y - sbinfo.dwCursorPosition.Y) - - self._erase(start, length) - - def erase_end_of_line(self) -> None: - """""" - sbinfo = self.get_win32_screen_buffer_info() - start = sbinfo.dwCursorPosition - length = sbinfo.dwSize.X - sbinfo.dwCursorPosition.X - - self._erase(start, length) - - def _erase(self, start: COORD, length: int) -> None: - chars_written = c_ulong() - - self._winapi( - windll.kernel32.FillConsoleOutputCharacterA, - self.hconsole, - c_char(b" "), - DWORD(length), - _coord_byval(start), - byref(chars_written), - ) - - # Reset attributes. - sbinfo = self.get_win32_screen_buffer_info() - self._winapi( - windll.kernel32.FillConsoleOutputAttribute, - self.hconsole, - sbinfo.wAttributes, - length, - _coord_byval(start), - byref(chars_written), - ) - - def reset_attributes(self) -> None: - "Reset the console foreground/background color." - self._winapi( - windll.kernel32.SetConsoleTextAttribute, self.hconsole, self.default_attrs - ) - self._hidden = False - - def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: - ( - fgcolor, - bgcolor, - bold, - underline, - strike, - italic, - blink, - reverse, - hidden, - ) = attrs - self._hidden = bool(hidden) - - # Start from the default attributes. - win_attrs: int = self.default_attrs - - if color_depth != ColorDepth.DEPTH_1_BIT: - # Override the last four bits: foreground color. - if fgcolor: - win_attrs = win_attrs & ~0xF - win_attrs |= self.color_lookup_table.lookup_fg_color(fgcolor) - - # Override the next four bits: background color. - if bgcolor: - win_attrs = win_attrs & ~0xF0 - win_attrs |= self.color_lookup_table.lookup_bg_color(bgcolor) - - # Reverse: swap these four bits groups. - if reverse: - win_attrs = ( - (win_attrs & ~0xFF) - | ((win_attrs & 0xF) << 4) - | ((win_attrs & 0xF0) >> 4) - ) - - self._winapi(windll.kernel32.SetConsoleTextAttribute, self.hconsole, win_attrs) - - def disable_autowrap(self) -> None: - # Not supported by Windows. - pass - - def enable_autowrap(self) -> None: - # Not supported by Windows. - pass - - def cursor_goto(self, row: int = 0, column: int = 0) -> None: - pos = COORD(X=column, Y=row) - self._winapi( - windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) - ) - - def cursor_up(self, amount: int) -> None: - sr = self.get_win32_screen_buffer_info().dwCursorPosition - pos = COORD(X=sr.X, Y=sr.Y - amount) - self._winapi( - windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) - ) - - def cursor_down(self, amount: int) -> None: - self.cursor_up(-amount) - - def cursor_forward(self, amount: int) -> None: - sr = self.get_win32_screen_buffer_info().dwCursorPosition - # assert sr.X + amount >= 0, 'Negative cursor position: x=%r amount=%r' % (sr.X, amount) - - pos = COORD(X=max(0, sr.X + amount), Y=sr.Y) - self._winapi( - windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) - ) - - def cursor_backward(self, amount: int) -> None: - self.cursor_forward(-amount) - - def flush(self) -> None: - """ - Write to output stream and flush. - """ - if not self._buffer: - # Only flush stdout buffer. (It could be that Python still has - # something in its buffer. -- We want to be sure to print that in - # the correct color.) - self.stdout.flush() - return - - data = "".join(self._buffer) - - if _DEBUG_RENDER_OUTPUT: - self.LOG.write(("%r" % data).encode("utf-8") + b"\n") - self.LOG.flush() - - # Print characters one by one. This appears to be the best solution - # in oder to avoid traces of vertical lines when the completion - # menu disappears. - for b in data: - written = DWORD() - - retval = windll.kernel32.WriteConsoleW( - self.hconsole, b, 1, byref(written), None - ) - assert retval != 0 - - self._buffer = [] - - def get_rows_below_cursor_position(self) -> int: - info = self.get_win32_screen_buffer_info() - return info.srWindow.Bottom - info.dwCursorPosition.Y + 1 - - def scroll_buffer_to_prompt(self) -> None: - """ - To be called before drawing the prompt. This should scroll the console - to left, with the cursor at the bottom (if possible). - """ - # Get current window size - info = self.get_win32_screen_buffer_info() - sr = info.srWindow - cursor_pos = info.dwCursorPosition - - result = SMALL_RECT() - - # Scroll to the left. - result.Left = 0 - result.Right = sr.Right - sr.Left - - # Scroll vertical - win_height = sr.Bottom - sr.Top - if 0 < sr.Bottom - cursor_pos.Y < win_height - 1: - # no vertical scroll if cursor already on the screen - result.Bottom = sr.Bottom - else: - result.Bottom = max(win_height, cursor_pos.Y) - result.Top = result.Bottom - win_height - - # Scroll API - self._winapi( - windll.kernel32.SetConsoleWindowInfo, self.hconsole, True, byref(result) - ) - - def enter_alternate_screen(self) -> None: - """ - Go to alternate screen buffer. - """ - if not self._in_alternate_screen: - GENERIC_READ = 0x80000000 - GENERIC_WRITE = 0x40000000 - - # Create a new console buffer and activate that one. - handle = HANDLE( - self._winapi( - windll.kernel32.CreateConsoleScreenBuffer, - GENERIC_READ | GENERIC_WRITE, - DWORD(0), - None, - DWORD(1), - None, - ) - ) - - self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, handle) - self.hconsole = handle - self._in_alternate_screen = True - - def quit_alternate_screen(self) -> None: - """ - Make stdout again the active buffer. - """ - if self._in_alternate_screen: - stdout = HANDLE( - self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE) - ) - self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, stdout) - self._winapi(windll.kernel32.CloseHandle, self.hconsole) - self.hconsole = stdout - self._in_alternate_screen = False - - def enable_mouse_support(self) -> None: - ENABLE_MOUSE_INPUT = 0x10 - - # This `ENABLE_QUICK_EDIT_MODE` flag needs to be cleared for mouse - # support to work, but it's possible that it was already cleared - # before. - ENABLE_QUICK_EDIT_MODE = 0x0040 - - handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) - - original_mode = DWORD() - self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) - self._winapi( - windll.kernel32.SetConsoleMode, - handle, - (original_mode.value | ENABLE_MOUSE_INPUT) & ~ENABLE_QUICK_EDIT_MODE, - ) - - def disable_mouse_support(self) -> None: - ENABLE_MOUSE_INPUT = 0x10 - handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) - - original_mode = DWORD() - self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) - self._winapi( - windll.kernel32.SetConsoleMode, - handle, - original_mode.value & ~ENABLE_MOUSE_INPUT, - ) - - def hide_cursor(self) -> None: - pass - - def show_cursor(self) -> None: - pass - +from prompt_toolkit.data_structures import Size +from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs +from prompt_toolkit.utils import get_cwidth +from prompt_toolkit.win32_types import ( + CONSOLE_SCREEN_BUFFER_INFO, + COORD, + SMALL_RECT, + STD_INPUT_HANDLE, + STD_OUTPUT_HANDLE, +) + +from .base import Output +from .color_depth import ColorDepth + +__all__ = [ + "Win32Output", +] + + +def _coord_byval(coord: COORD) -> c_long: + """ + Turns a COORD object into a c_long. + This will cause it to be passed by value instead of by reference. (That is what I think at least.) + + When running ``ptipython`` is run (only with IPython), we often got the following error:: + + Error in 'SetConsoleCursorPosition'. + ArgumentError("argument 2: <class 'TypeError'>: wrong type",) + argument 2: <class 'TypeError'>: wrong type + + It was solved by turning ``COORD`` parameters into a ``c_long`` like this. + + More info: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx + """ + return c_long(coord.Y * 0x10000 | coord.X & 0xFFFF) + + +#: If True: write the output of the renderer also to the following file. This +#: is very useful for debugging. (e.g.: to see that we don't write more bytes +#: than required.) +_DEBUG_RENDER_OUTPUT = False +_DEBUG_RENDER_OUTPUT_FILENAME = r"prompt-toolkit-windows-output.log" + + +class NoConsoleScreenBufferError(Exception): + """ + Raised when the application is not running inside a Windows Console, but + the user tries to instantiate Win32Output. + """ + + def __init__(self) -> None: + # Are we running in 'xterm' on Windows, like git-bash for instance? + xterm = "xterm" in os.environ.get("TERM", "") + + if xterm: + message = ( + "Found %s, while expecting a Windows console. " + 'Maybe try to run this program using "winpty" ' + "or run it in cmd.exe instead. Or otherwise, " + "in case of Cygwin, use the Python executable " + "that is compiled for Cygwin." % os.environ["TERM"] + ) + else: + message = "No Windows console found. Are you running cmd.exe?" + super().__init__(message) + + +_T = TypeVar("_T") + + +class Win32Output(Output): + """ + I/O abstraction for rendering to Windows consoles. + (cmd.exe and similar.) + """ + + def __init__( + self, + stdout: TextIO, + use_complete_width: bool = False, + default_color_depth: Optional[ColorDepth] = None, + ) -> None: + self.use_complete_width = use_complete_width + self.default_color_depth = default_color_depth + + self._buffer: List[str] = [] + self.stdout: TextIO = stdout + self.hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) + + self._in_alternate_screen = False + self._hidden = False + + self.color_lookup_table = ColorLookupTable() + + # Remember the default console colors. + info = self.get_win32_screen_buffer_info() + self.default_attrs = info.wAttributes if info else 15 + + if _DEBUG_RENDER_OUTPUT: + self.LOG = open(_DEBUG_RENDER_OUTPUT_FILENAME, "ab") + + def fileno(self) -> int: + "Return file descriptor." + return self.stdout.fileno() + + def encoding(self) -> str: + "Return encoding used for stdout." + return self.stdout.encoding + + def write(self, data: str) -> None: + if self._hidden: + data = " " * get_cwidth(data) + + self._buffer.append(data) + + def write_raw(self, data: str) -> None: + "For win32, there is no difference between write and write_raw." + self.write(data) + + def get_size(self) -> Size: + info = self.get_win32_screen_buffer_info() + + # We take the width of the *visible* region as the size. Not the width + # of the complete screen buffer. (Unless use_complete_width has been + # set.) + if self.use_complete_width: + width = info.dwSize.X + else: + width = info.srWindow.Right - info.srWindow.Left + + height = info.srWindow.Bottom - info.srWindow.Top + 1 + + # We avoid the right margin, windows will wrap otherwise. + maxwidth = info.dwSize.X - 1 + width = min(maxwidth, width) + + # Create `Size` object. + return Size(rows=height, columns=width) + + def _winapi(self, func: Callable[..., _T], *a: object, **kw: object) -> _T: + """ + Flush and call win API function. + """ + self.flush() + + if _DEBUG_RENDER_OUTPUT: + self.LOG.write(("%r" % func.__name__).encode("utf-8") + b"\n") + self.LOG.write( + b" " + ", ".join(["%r" % i for i in a]).encode("utf-8") + b"\n" + ) + self.LOG.write( + b" " + + ", ".join(["%r" % type(i) for i in a]).encode("utf-8") + + b"\n" + ) + self.LOG.flush() + + try: + return func(*a, **kw) + except ArgumentError as e: + if _DEBUG_RENDER_OUTPUT: + self.LOG.write( + (" Error in %r %r %s\n" % (func.__name__, e, e)).encode("utf-8") + ) + + raise + + def get_win32_screen_buffer_info(self) -> CONSOLE_SCREEN_BUFFER_INFO: + """ + Return Screen buffer info. + """ + # NOTE: We don't call the `GetConsoleScreenBufferInfo` API through + # `self._winapi`. Doing so causes Python to crash on certain 64bit + # Python versions. (Reproduced with 64bit Python 2.7.6, on Windows + # 10). It is not clear why. Possibly, it has to do with passing + # these objects as an argument, or through *args. + + # The Python documentation contains the following - possibly related - warning: + # ctypes does not support passing unions or structures with + # bit-fields to functions by value. While this may work on 32-bit + # x86, it's not guaranteed by the library to work in the general + # case. Unions and structures with bit-fields should always be + # passed to functions by pointer. + + # Also see: + # - https://github.com/ipython/ipython/issues/10070 + # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/406 + # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/86 + + self.flush() + sbinfo = CONSOLE_SCREEN_BUFFER_INFO() + success = windll.kernel32.GetConsoleScreenBufferInfo( + self.hconsole, byref(sbinfo) + ) + + # success = self._winapi(windll.kernel32.GetConsoleScreenBufferInfo, + # self.hconsole, byref(sbinfo)) + + if success: + return sbinfo + else: + raise NoConsoleScreenBufferError + + def set_title(self, title: str) -> None: + """ + Set terminal title. + """ + self._winapi(windll.kernel32.SetConsoleTitleW, title) + + def clear_title(self) -> None: + self._winapi(windll.kernel32.SetConsoleTitleW, "") + + def erase_screen(self) -> None: + start = COORD(0, 0) + sbinfo = self.get_win32_screen_buffer_info() + length = sbinfo.dwSize.X * sbinfo.dwSize.Y + + self.cursor_goto(row=0, column=0) + self._erase(start, length) + + def erase_down(self) -> None: + sbinfo = self.get_win32_screen_buffer_info() + size = sbinfo.dwSize + + start = sbinfo.dwCursorPosition + length = (size.X - size.X) + size.X * (size.Y - sbinfo.dwCursorPosition.Y) + + self._erase(start, length) + + def erase_end_of_line(self) -> None: + """""" + sbinfo = self.get_win32_screen_buffer_info() + start = sbinfo.dwCursorPosition + length = sbinfo.dwSize.X - sbinfo.dwCursorPosition.X + + self._erase(start, length) + + def _erase(self, start: COORD, length: int) -> None: + chars_written = c_ulong() + + self._winapi( + windll.kernel32.FillConsoleOutputCharacterA, + self.hconsole, + c_char(b" "), + DWORD(length), + _coord_byval(start), + byref(chars_written), + ) + + # Reset attributes. + sbinfo = self.get_win32_screen_buffer_info() + self._winapi( + windll.kernel32.FillConsoleOutputAttribute, + self.hconsole, + sbinfo.wAttributes, + length, + _coord_byval(start), + byref(chars_written), + ) + + def reset_attributes(self) -> None: + "Reset the console foreground/background color." + self._winapi( + windll.kernel32.SetConsoleTextAttribute, self.hconsole, self.default_attrs + ) + self._hidden = False + + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + ( + fgcolor, + bgcolor, + bold, + underline, + strike, + italic, + blink, + reverse, + hidden, + ) = attrs + self._hidden = bool(hidden) + + # Start from the default attributes. + win_attrs: int = self.default_attrs + + if color_depth != ColorDepth.DEPTH_1_BIT: + # Override the last four bits: foreground color. + if fgcolor: + win_attrs = win_attrs & ~0xF + win_attrs |= self.color_lookup_table.lookup_fg_color(fgcolor) + + # Override the next four bits: background color. + if bgcolor: + win_attrs = win_attrs & ~0xF0 + win_attrs |= self.color_lookup_table.lookup_bg_color(bgcolor) + + # Reverse: swap these four bits groups. + if reverse: + win_attrs = ( + (win_attrs & ~0xFF) + | ((win_attrs & 0xF) << 4) + | ((win_attrs & 0xF0) >> 4) + ) + + self._winapi(windll.kernel32.SetConsoleTextAttribute, self.hconsole, win_attrs) + + def disable_autowrap(self) -> None: + # Not supported by Windows. + pass + + def enable_autowrap(self) -> None: + # Not supported by Windows. + pass + + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + pos = COORD(X=column, Y=row) + self._winapi( + windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) + ) + + def cursor_up(self, amount: int) -> None: + sr = self.get_win32_screen_buffer_info().dwCursorPosition + pos = COORD(X=sr.X, Y=sr.Y - amount) + self._winapi( + windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) + ) + + def cursor_down(self, amount: int) -> None: + self.cursor_up(-amount) + + def cursor_forward(self, amount: int) -> None: + sr = self.get_win32_screen_buffer_info().dwCursorPosition + # assert sr.X + amount >= 0, 'Negative cursor position: x=%r amount=%r' % (sr.X, amount) + + pos = COORD(X=max(0, sr.X + amount), Y=sr.Y) + self._winapi( + windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) + ) + + def cursor_backward(self, amount: int) -> None: + self.cursor_forward(-amount) + + def flush(self) -> None: + """ + Write to output stream and flush. + """ + if not self._buffer: + # Only flush stdout buffer. (It could be that Python still has + # something in its buffer. -- We want to be sure to print that in + # the correct color.) + self.stdout.flush() + return + + data = "".join(self._buffer) + + if _DEBUG_RENDER_OUTPUT: + self.LOG.write(("%r" % data).encode("utf-8") + b"\n") + self.LOG.flush() + + # Print characters one by one. This appears to be the best solution + # in oder to avoid traces of vertical lines when the completion + # menu disappears. + for b in data: + written = DWORD() + + retval = windll.kernel32.WriteConsoleW( + self.hconsole, b, 1, byref(written), None + ) + assert retval != 0 + + self._buffer = [] + + def get_rows_below_cursor_position(self) -> int: + info = self.get_win32_screen_buffer_info() + return info.srWindow.Bottom - info.dwCursorPosition.Y + 1 + + def scroll_buffer_to_prompt(self) -> None: + """ + To be called before drawing the prompt. This should scroll the console + to left, with the cursor at the bottom (if possible). + """ + # Get current window size + info = self.get_win32_screen_buffer_info() + sr = info.srWindow + cursor_pos = info.dwCursorPosition + + result = SMALL_RECT() + + # Scroll to the left. + result.Left = 0 + result.Right = sr.Right - sr.Left + + # Scroll vertical + win_height = sr.Bottom - sr.Top + if 0 < sr.Bottom - cursor_pos.Y < win_height - 1: + # no vertical scroll if cursor already on the screen + result.Bottom = sr.Bottom + else: + result.Bottom = max(win_height, cursor_pos.Y) + result.Top = result.Bottom - win_height + + # Scroll API + self._winapi( + windll.kernel32.SetConsoleWindowInfo, self.hconsole, True, byref(result) + ) + + def enter_alternate_screen(self) -> None: + """ + Go to alternate screen buffer. + """ + if not self._in_alternate_screen: + GENERIC_READ = 0x80000000 + GENERIC_WRITE = 0x40000000 + + # Create a new console buffer and activate that one. + handle = HANDLE( + self._winapi( + windll.kernel32.CreateConsoleScreenBuffer, + GENERIC_READ | GENERIC_WRITE, + DWORD(0), + None, + DWORD(1), + None, + ) + ) + + self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, handle) + self.hconsole = handle + self._in_alternate_screen = True + + def quit_alternate_screen(self) -> None: + """ + Make stdout again the active buffer. + """ + if self._in_alternate_screen: + stdout = HANDLE( + self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE) + ) + self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, stdout) + self._winapi(windll.kernel32.CloseHandle, self.hconsole) + self.hconsole = stdout + self._in_alternate_screen = False + + def enable_mouse_support(self) -> None: + ENABLE_MOUSE_INPUT = 0x10 + + # This `ENABLE_QUICK_EDIT_MODE` flag needs to be cleared for mouse + # support to work, but it's possible that it was already cleared + # before. + ENABLE_QUICK_EDIT_MODE = 0x0040 + + handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) + + original_mode = DWORD() + self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) + self._winapi( + windll.kernel32.SetConsoleMode, + handle, + (original_mode.value | ENABLE_MOUSE_INPUT) & ~ENABLE_QUICK_EDIT_MODE, + ) + + def disable_mouse_support(self) -> None: + ENABLE_MOUSE_INPUT = 0x10 + handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) + + original_mode = DWORD() + self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) + self._winapi( + windll.kernel32.SetConsoleMode, + handle, + original_mode.value & ~ENABLE_MOUSE_INPUT, + ) + + def hide_cursor(self) -> None: + pass + + def show_cursor(self) -> None: + pass + def set_cursor_shape(self, cursor_shape: CursorShape) -> None: pass def reset_cursor_shape(self) -> None: pass - @classmethod - def win32_refresh_window(cls) -> None: - """ - Call win32 API to refresh the whole Window. - - This is sometimes necessary when the application paints background - for completion menus. When the menu disappears, it leaves traces due - to a bug in the Windows Console. Sending a repaint request solves it. - """ - # Get console handle - handle = HANDLE(windll.kernel32.GetConsoleWindow()) - - RDW_INVALIDATE = 0x0001 - windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE)) - - def get_default_color_depth(self) -> ColorDepth: - """ - Return the default color depth for a windows terminal. - - Contrary to the Vt100 implementation, this doesn't depend on a $TERM - variable. - """ - if self.default_color_depth is not None: - return self.default_color_depth - - # For now, by default, always use 4 bit color on Windows 10 by default, - # even when vt100 escape sequences with - # ENABLE_VIRTUAL_TERMINAL_PROCESSING are supported. We don't have a - # reliable way yet to know whether our console supports true color or - # only 4-bit. - return ColorDepth.DEPTH_4_BIT - - -class FOREGROUND_COLOR: - BLACK = 0x0000 - BLUE = 0x0001 - GREEN = 0x0002 - CYAN = 0x0003 - RED = 0x0004 - MAGENTA = 0x0005 - YELLOW = 0x0006 - GRAY = 0x0007 - INTENSITY = 0x0008 # Foreground color is intensified. - - -class BACKGROUND_COLOR: - BLACK = 0x0000 - BLUE = 0x0010 - GREEN = 0x0020 - CYAN = 0x0030 - RED = 0x0040 - MAGENTA = 0x0050 - YELLOW = 0x0060 - GRAY = 0x0070 - INTENSITY = 0x0080 # Background color is intensified. - - -def _create_ansi_color_dict( - color_cls: Union[Type[FOREGROUND_COLOR], Type[BACKGROUND_COLOR]] -) -> Dict[str, int]: - "Create a table that maps the 16 named ansi colors to their Windows code." - return { - "ansidefault": color_cls.BLACK, - "ansiblack": color_cls.BLACK, - "ansigray": color_cls.GRAY, - "ansibrightblack": color_cls.BLACK | color_cls.INTENSITY, - "ansiwhite": color_cls.GRAY | color_cls.INTENSITY, - # Low intensity. - "ansired": color_cls.RED, - "ansigreen": color_cls.GREEN, - "ansiyellow": color_cls.YELLOW, - "ansiblue": color_cls.BLUE, - "ansimagenta": color_cls.MAGENTA, - "ansicyan": color_cls.CYAN, - # High intensity. - "ansibrightred": color_cls.RED | color_cls.INTENSITY, - "ansibrightgreen": color_cls.GREEN | color_cls.INTENSITY, - "ansibrightyellow": color_cls.YELLOW | color_cls.INTENSITY, - "ansibrightblue": color_cls.BLUE | color_cls.INTENSITY, - "ansibrightmagenta": color_cls.MAGENTA | color_cls.INTENSITY, - "ansibrightcyan": color_cls.CYAN | color_cls.INTENSITY, - } - - -FG_ANSI_COLORS = _create_ansi_color_dict(FOREGROUND_COLOR) -BG_ANSI_COLORS = _create_ansi_color_dict(BACKGROUND_COLOR) - -assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) -assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) - - -class ColorLookupTable: - """ - Inspired by pygments/formatters/terminal256.py - """ - - def __init__(self) -> None: - self._win32_colors = self._build_color_table() - - # Cache (map color string to foreground and background code). - self.best_match: Dict[str, Tuple[int, int]] = {} - - @staticmethod - def _build_color_table() -> List[Tuple[int, int, int, int, int]]: - """ - Build an RGB-to-256 color conversion table - """ - FG = FOREGROUND_COLOR - BG = BACKGROUND_COLOR - - return [ - (0x00, 0x00, 0x00, FG.BLACK, BG.BLACK), - (0x00, 0x00, 0xAA, FG.BLUE, BG.BLUE), - (0x00, 0xAA, 0x00, FG.GREEN, BG.GREEN), - (0x00, 0xAA, 0xAA, FG.CYAN, BG.CYAN), - (0xAA, 0x00, 0x00, FG.RED, BG.RED), - (0xAA, 0x00, 0xAA, FG.MAGENTA, BG.MAGENTA), - (0xAA, 0xAA, 0x00, FG.YELLOW, BG.YELLOW), - (0x88, 0x88, 0x88, FG.GRAY, BG.GRAY), - (0x44, 0x44, 0xFF, FG.BLUE | FG.INTENSITY, BG.BLUE | BG.INTENSITY), - (0x44, 0xFF, 0x44, FG.GREEN | FG.INTENSITY, BG.GREEN | BG.INTENSITY), - (0x44, 0xFF, 0xFF, FG.CYAN | FG.INTENSITY, BG.CYAN | BG.INTENSITY), - (0xFF, 0x44, 0x44, FG.RED | FG.INTENSITY, BG.RED | BG.INTENSITY), - (0xFF, 0x44, 0xFF, FG.MAGENTA | FG.INTENSITY, BG.MAGENTA | BG.INTENSITY), - (0xFF, 0xFF, 0x44, FG.YELLOW | FG.INTENSITY, BG.YELLOW | BG.INTENSITY), - (0x44, 0x44, 0x44, FG.BLACK | FG.INTENSITY, BG.BLACK | BG.INTENSITY), - (0xFF, 0xFF, 0xFF, FG.GRAY | FG.INTENSITY, BG.GRAY | BG.INTENSITY), - ] - - def _closest_color(self, r: int, g: int, b: int) -> Tuple[int, int]: - distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) - fg_match = 0 - bg_match = 0 - - for r_, g_, b_, fg_, bg_ in self._win32_colors: - rd = r - r_ - gd = g - g_ - bd = b - b_ - - d = rd * rd + gd * gd + bd * bd - - if d < distance: - fg_match = fg_ - bg_match = bg_ - distance = d - return fg_match, bg_match - - def _color_indexes(self, color: str) -> Tuple[int, int]: - indexes = self.best_match.get(color, None) - if indexes is None: - try: - rgb = int(str(color), 16) - except ValueError: - rgb = 0 - - r = (rgb >> 16) & 0xFF - g = (rgb >> 8) & 0xFF - b = rgb & 0xFF - indexes = self._closest_color(r, g, b) - self.best_match[color] = indexes - return indexes - - def lookup_fg_color(self, fg_color: str) -> int: - """ - Return the color for use in the - `windll.kernel32.SetConsoleTextAttribute` API call. - - :param fg_color: Foreground as text. E.g. 'ffffff' or 'red' - """ - # Foreground. - if fg_color in FG_ANSI_COLORS: - return FG_ANSI_COLORS[fg_color] - else: - return self._color_indexes(fg_color)[0] - - def lookup_bg_color(self, bg_color: str) -> int: - """ - Return the color for use in the - `windll.kernel32.SetConsoleTextAttribute` API call. - - :param bg_color: Background as text. E.g. 'ffffff' or 'red' - """ - # Background. - if bg_color in BG_ANSI_COLORS: - return BG_ANSI_COLORS[bg_color] - else: - return self._color_indexes(bg_color)[1] + @classmethod + def win32_refresh_window(cls) -> None: + """ + Call win32 API to refresh the whole Window. + + This is sometimes necessary when the application paints background + for completion menus. When the menu disappears, it leaves traces due + to a bug in the Windows Console. Sending a repaint request solves it. + """ + # Get console handle + handle = HANDLE(windll.kernel32.GetConsoleWindow()) + + RDW_INVALIDATE = 0x0001 + windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE)) + + def get_default_color_depth(self) -> ColorDepth: + """ + Return the default color depth for a windows terminal. + + Contrary to the Vt100 implementation, this doesn't depend on a $TERM + variable. + """ + if self.default_color_depth is not None: + return self.default_color_depth + + # For now, by default, always use 4 bit color on Windows 10 by default, + # even when vt100 escape sequences with + # ENABLE_VIRTUAL_TERMINAL_PROCESSING are supported. We don't have a + # reliable way yet to know whether our console supports true color or + # only 4-bit. + return ColorDepth.DEPTH_4_BIT + + +class FOREGROUND_COLOR: + BLACK = 0x0000 + BLUE = 0x0001 + GREEN = 0x0002 + CYAN = 0x0003 + RED = 0x0004 + MAGENTA = 0x0005 + YELLOW = 0x0006 + GRAY = 0x0007 + INTENSITY = 0x0008 # Foreground color is intensified. + + +class BACKGROUND_COLOR: + BLACK = 0x0000 + BLUE = 0x0010 + GREEN = 0x0020 + CYAN = 0x0030 + RED = 0x0040 + MAGENTA = 0x0050 + YELLOW = 0x0060 + GRAY = 0x0070 + INTENSITY = 0x0080 # Background color is intensified. + + +def _create_ansi_color_dict( + color_cls: Union[Type[FOREGROUND_COLOR], Type[BACKGROUND_COLOR]] +) -> Dict[str, int]: + "Create a table that maps the 16 named ansi colors to their Windows code." + return { + "ansidefault": color_cls.BLACK, + "ansiblack": color_cls.BLACK, + "ansigray": color_cls.GRAY, + "ansibrightblack": color_cls.BLACK | color_cls.INTENSITY, + "ansiwhite": color_cls.GRAY | color_cls.INTENSITY, + # Low intensity. + "ansired": color_cls.RED, + "ansigreen": color_cls.GREEN, + "ansiyellow": color_cls.YELLOW, + "ansiblue": color_cls.BLUE, + "ansimagenta": color_cls.MAGENTA, + "ansicyan": color_cls.CYAN, + # High intensity. + "ansibrightred": color_cls.RED | color_cls.INTENSITY, + "ansibrightgreen": color_cls.GREEN | color_cls.INTENSITY, + "ansibrightyellow": color_cls.YELLOW | color_cls.INTENSITY, + "ansibrightblue": color_cls.BLUE | color_cls.INTENSITY, + "ansibrightmagenta": color_cls.MAGENTA | color_cls.INTENSITY, + "ansibrightcyan": color_cls.CYAN | color_cls.INTENSITY, + } + + +FG_ANSI_COLORS = _create_ansi_color_dict(FOREGROUND_COLOR) +BG_ANSI_COLORS = _create_ansi_color_dict(BACKGROUND_COLOR) + +assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) +assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) + + +class ColorLookupTable: + """ + Inspired by pygments/formatters/terminal256.py + """ + + def __init__(self) -> None: + self._win32_colors = self._build_color_table() + + # Cache (map color string to foreground and background code). + self.best_match: Dict[str, Tuple[int, int]] = {} + + @staticmethod + def _build_color_table() -> List[Tuple[int, int, int, int, int]]: + """ + Build an RGB-to-256 color conversion table + """ + FG = FOREGROUND_COLOR + BG = BACKGROUND_COLOR + + return [ + (0x00, 0x00, 0x00, FG.BLACK, BG.BLACK), + (0x00, 0x00, 0xAA, FG.BLUE, BG.BLUE), + (0x00, 0xAA, 0x00, FG.GREEN, BG.GREEN), + (0x00, 0xAA, 0xAA, FG.CYAN, BG.CYAN), + (0xAA, 0x00, 0x00, FG.RED, BG.RED), + (0xAA, 0x00, 0xAA, FG.MAGENTA, BG.MAGENTA), + (0xAA, 0xAA, 0x00, FG.YELLOW, BG.YELLOW), + (0x88, 0x88, 0x88, FG.GRAY, BG.GRAY), + (0x44, 0x44, 0xFF, FG.BLUE | FG.INTENSITY, BG.BLUE | BG.INTENSITY), + (0x44, 0xFF, 0x44, FG.GREEN | FG.INTENSITY, BG.GREEN | BG.INTENSITY), + (0x44, 0xFF, 0xFF, FG.CYAN | FG.INTENSITY, BG.CYAN | BG.INTENSITY), + (0xFF, 0x44, 0x44, FG.RED | FG.INTENSITY, BG.RED | BG.INTENSITY), + (0xFF, 0x44, 0xFF, FG.MAGENTA | FG.INTENSITY, BG.MAGENTA | BG.INTENSITY), + (0xFF, 0xFF, 0x44, FG.YELLOW | FG.INTENSITY, BG.YELLOW | BG.INTENSITY), + (0x44, 0x44, 0x44, FG.BLACK | FG.INTENSITY, BG.BLACK | BG.INTENSITY), + (0xFF, 0xFF, 0xFF, FG.GRAY | FG.INTENSITY, BG.GRAY | BG.INTENSITY), + ] + + def _closest_color(self, r: int, g: int, b: int) -> Tuple[int, int]: + distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) + fg_match = 0 + bg_match = 0 + + for r_, g_, b_, fg_, bg_ in self._win32_colors: + rd = r - r_ + gd = g - g_ + bd = b - b_ + + d = rd * rd + gd * gd + bd * bd + + if d < distance: + fg_match = fg_ + bg_match = bg_ + distance = d + return fg_match, bg_match + + def _color_indexes(self, color: str) -> Tuple[int, int]: + indexes = self.best_match.get(color, None) + if indexes is None: + try: + rgb = int(str(color), 16) + except ValueError: + rgb = 0 + + r = (rgb >> 16) & 0xFF + g = (rgb >> 8) & 0xFF + b = rgb & 0xFF + indexes = self._closest_color(r, g, b) + self.best_match[color] = indexes + return indexes + + def lookup_fg_color(self, fg_color: str) -> int: + """ + Return the color for use in the + `windll.kernel32.SetConsoleTextAttribute` API call. + + :param fg_color: Foreground as text. E.g. 'ffffff' or 'red' + """ + # Foreground. + if fg_color in FG_ANSI_COLORS: + return FG_ANSI_COLORS[fg_color] + else: + return self._color_indexes(fg_color)[0] + + def lookup_bg_color(self, bg_color: str) -> int: + """ + Return the color for use in the + `windll.kernel32.SetConsoleTextAttribute` API call. + + :param bg_color: Background as text. E.g. 'ffffff' or 'red' + """ + # Background. + if bg_color in BG_ANSI_COLORS: + return BG_ANSI_COLORS[bg_color] + else: + return self._color_indexes(bg_color)[1] diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py index dc4f7cdf44e..933f54a28f2 100644 --- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py +++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py @@ -1,104 +1,104 @@ -from ctypes import byref, windll -from ctypes.wintypes import DWORD, HANDLE -from typing import Any, Optional, TextIO - -from prompt_toolkit.data_structures import Size -from prompt_toolkit.utils import is_windows -from prompt_toolkit.win32_types import STD_OUTPUT_HANDLE - -from .base import Output -from .color_depth import ColorDepth -from .vt100 import Vt100_Output -from .win32 import Win32Output - -__all__ = [ - "Windows10_Output", -] - -# See: https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms686033(v=vs.85).aspx -ENABLE_PROCESSED_INPUT = 0x0001 -ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 - - -class Windows10_Output: - """ - Windows 10 output abstraction. This enables and uses vt100 escape sequences. - """ - - def __init__( - self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None - ) -> None: - self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth) - self.vt100_output = Vt100_Output( - stdout, lambda: Size(0, 0), default_color_depth=default_color_depth - ) - self._hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) - - def flush(self) -> None: - """ - Write to output stream and flush. - """ - original_mode = DWORD(0) - - # Remember the previous console mode. - windll.kernel32.GetConsoleMode(self._hconsole, byref(original_mode)) - - # Enable processing of vt100 sequences. - windll.kernel32.SetConsoleMode( - self._hconsole, - DWORD(ENABLE_PROCESSED_INPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING), - ) - - try: - self.vt100_output.flush() - finally: - # Restore console mode. - windll.kernel32.SetConsoleMode(self._hconsole, original_mode) - - @property - def responds_to_cpr(self) -> bool: - return False # We don't need this on Windows. - - def __getattr__(self, name: str) -> Any: - if name in ( - "get_size", - "get_rows_below_cursor_position", - "enable_mouse_support", - "disable_mouse_support", - "scroll_buffer_to_prompt", - "get_win32_screen_buffer_info", - "enable_bracketed_paste", - "disable_bracketed_paste", - "get_default_color_depth", - ): - return getattr(self.win32_output, name) - else: - return getattr(self.vt100_output, name) - - -Output.register(Windows10_Output) - - -def is_win_vt100_enabled() -> bool: - """ - Returns True when we're running Windows and VT100 escape sequences are - supported. - """ - if not is_windows(): - return False - - hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) - - # Get original console mode. - original_mode = DWORD(0) - windll.kernel32.GetConsoleMode(hconsole, byref(original_mode)) - - try: - # Try to enable VT100 sequences. - result: int = windll.kernel32.SetConsoleMode( - hconsole, DWORD(ENABLE_PROCESSED_INPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING) - ) - - return result == 1 - finally: - windll.kernel32.SetConsoleMode(hconsole, original_mode) +from ctypes import byref, windll +from ctypes.wintypes import DWORD, HANDLE +from typing import Any, Optional, TextIO + +from prompt_toolkit.data_structures import Size +from prompt_toolkit.utils import is_windows +from prompt_toolkit.win32_types import STD_OUTPUT_HANDLE + +from .base import Output +from .color_depth import ColorDepth +from .vt100 import Vt100_Output +from .win32 import Win32Output + +__all__ = [ + "Windows10_Output", +] + +# See: https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms686033(v=vs.85).aspx +ENABLE_PROCESSED_INPUT = 0x0001 +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + + +class Windows10_Output: + """ + Windows 10 output abstraction. This enables and uses vt100 escape sequences. + """ + + def __init__( + self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None + ) -> None: + self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth) + self.vt100_output = Vt100_Output( + stdout, lambda: Size(0, 0), default_color_depth=default_color_depth + ) + self._hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) + + def flush(self) -> None: + """ + Write to output stream and flush. + """ + original_mode = DWORD(0) + + # Remember the previous console mode. + windll.kernel32.GetConsoleMode(self._hconsole, byref(original_mode)) + + # Enable processing of vt100 sequences. + windll.kernel32.SetConsoleMode( + self._hconsole, + DWORD(ENABLE_PROCESSED_INPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING), + ) + + try: + self.vt100_output.flush() + finally: + # Restore console mode. + windll.kernel32.SetConsoleMode(self._hconsole, original_mode) + + @property + def responds_to_cpr(self) -> bool: + return False # We don't need this on Windows. + + def __getattr__(self, name: str) -> Any: + if name in ( + "get_size", + "get_rows_below_cursor_position", + "enable_mouse_support", + "disable_mouse_support", + "scroll_buffer_to_prompt", + "get_win32_screen_buffer_info", + "enable_bracketed_paste", + "disable_bracketed_paste", + "get_default_color_depth", + ): + return getattr(self.win32_output, name) + else: + return getattr(self.vt100_output, name) + + +Output.register(Windows10_Output) + + +def is_win_vt100_enabled() -> bool: + """ + Returns True when we're running Windows and VT100 escape sequences are + supported. + """ + if not is_windows(): + return False + + hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) + + # Get original console mode. + original_mode = DWORD(0) + windll.kernel32.GetConsoleMode(hconsole, byref(original_mode)) + + try: + # Try to enable VT100 sequences. + result: int = windll.kernel32.SetConsoleMode( + hconsole, DWORD(ENABLE_PROCESSED_INPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING) + ) + + return result == 1 + finally: + windll.kernel32.SetConsoleMode(hconsole, original_mode) |