mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-01 14:07:48 +00:00
first commit
This commit is contained in:
13
.venv/Lib/site-packages/prompt_toolkit/output/__init__.py
Normal file
13
.venv/Lib/site-packages/prompt_toolkit/output/__init__.py
Normal file
@ -0,0 +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",
|
||||
]
|
329
.venv/Lib/site-packages/prompt_toolkit/output/base.py
Normal file
329
.venv/Lib/site-packages/prompt_toolkit/output/base.py
Normal file
@ -0,0 +1,329 @@
|
||||
"""
|
||||
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."
|
||||
|
||||
@abstractmethod
|
||||
def set_cursor_shape(self, cursor_shape: CursorShape) -> None:
|
||||
"Set cursor shape to block, beam or underline."
|
||||
|
||||
@abstractmethod
|
||||
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 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
|
58
.venv/Lib/site-packages/prompt_toolkit/output/color_depth.py
Normal file
58
.venv/Lib/site-packages/prompt_toolkit/output/color_depth.py
Normal file
@ -0,0 +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()
|
63
.venv/Lib/site-packages/prompt_toolkit/output/conemu.py
Normal file
63
.venv/Lib/site-packages/prompt_toolkit/output/conemu.py
Normal file
@ -0,0 +1,63 @@
|
||||
import sys
|
||||
|
||||
assert sys.platform == "win32"
|
||||
|
||||
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)
|
100
.venv/Lib/site-packages/prompt_toolkit/output/defaults.py
Normal file
100
.venv/Lib/site-packages/prompt_toolkit/output/defaults.py
Normal file
@ -0,0 +1,100 @@
|
||||
import sys
|
||||
from typing import Optional, TextIO, cast
|
||||
|
||||
from prompt_toolkit.utils import (
|
||||
get_bell_environment_variable,
|
||||
get_term_environment_variable,
|
||||
is_conemu_ansi,
|
||||
)
|
||||
|
||||
from .base import DummyOutput, Output
|
||||
from .color_depth import ColorDepth
|
||||
from .plain_text import PlainTextOutput
|
||||
|
||||
__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`
|
||||
is not a TTY. Useful if `sys.stdout` is redirected to a file, but we
|
||||
still want user input and output on the terminal.
|
||||
|
||||
By default, this is `False`. If `sys.stdout` is not a terminal (maybe
|
||||
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]:
|
||||
if io is not None and io.isatty():
|
||||
# (This is `None` when using `pythonw.exe` on Windows.)
|
||||
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
|
||||
# stdin/stdout/stderr are `None`.
|
||||
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 sys.platform == "win32":
|
||||
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,
|
||||
)
|
@ -0,0 +1,84 @@
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from typing import IO, Iterator, TextIO, cast
|
||||
|
||||
__all__ = ["flush_stdout"]
|
||||
|
||||
|
||||
def flush_stdout(stdout: TextIO, data: str, write_binary: bool) -> None:
|
||||
try:
|
||||
# Ensure that `stdout` is made blocking when writing into it.
|
||||
# Otherwise, when uvloop is activated (which makes stdout
|
||||
# non-blocking), and we write big amounts of text, then we get a
|
||||
# `BlockingIOError` here.
|
||||
with _blocking_io(stdout):
|
||||
# (We try to encode ourself, because that way we can replace
|
||||
# characters that don't exist in the character set, avoiding
|
||||
# UnicodeEncodeError crashes. E.g. u'\xb7' does not appear in 'ascii'.)
|
||||
# My Arch Linux installation of july 2015 reported 'ANSI_X3.4-1968'
|
||||
# for sys.stdout.encoding in xterm.
|
||||
out: IO[bytes]
|
||||
if write_binary:
|
||||
if hasattr(stdout, "buffer"):
|
||||
out = stdout.buffer
|
||||
else:
|
||||
# IO[bytes] was given to begin with.
|
||||
# (Used in the unit tests, for instance.)
|
||||
out = cast(IO[bytes], stdout)
|
||||
out.write(data.encode(stdout.encoding or "utf-8", "replace"))
|
||||
else:
|
||||
stdout.write(data)
|
||||
|
||||
stdout.flush()
|
||||
except OSError as e:
|
||||
if e.args and e.args[0] == errno.EINTR:
|
||||
# Interrupted system call. Can happen in case of a window
|
||||
# resize signal. (Just ignore. The resize handler will render
|
||||
# again anyway.)
|
||||
pass
|
||||
elif e.args and e.args[0] == 0:
|
||||
# This can happen when there is a lot of output and the user
|
||||
# sends a KeyboardInterrupt by pressing Control-C. E.g. in
|
||||
# a Python REPL when we execute "while True: print('test')".
|
||||
# (The `ptpython` REPL uses this `Output` class instead of
|
||||
# `stdout` directly -- in order to be network transparent.)
|
||||
# So, just ignore.
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _blocking_io(io: IO[str]) -> Iterator[None]:
|
||||
"""
|
||||
Ensure that the FD for `io` is set to blocking in here.
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
# On Windows, the `os` module doesn't have a `get/set_blocking`
|
||||
# function.
|
||||
yield
|
||||
return
|
||||
|
||||
try:
|
||||
fd = io.fileno()
|
||||
blocking = os.get_blocking(fd)
|
||||
except: # noqa
|
||||
# Failed somewhere.
|
||||
# `get_blocking` can raise `OSError`.
|
||||
# The io object can raise `AttributeError` when no `fileno()` method is
|
||||
# present if we're not a real file object.
|
||||
blocking = True # Assume we're good, and don't do anything.
|
||||
|
||||
try:
|
||||
# Make blocking if we weren't blocking yet.
|
||||
if not blocking:
|
||||
os.set_blocking(fd, True)
|
||||
|
||||
yield
|
||||
|
||||
finally:
|
||||
# Restore original blocking mode.
|
||||
if not blocking:
|
||||
os.set_blocking(fd, blocking)
|
145
.venv/Lib/site-packages/prompt_toolkit/output/plain_text.py
Normal file
145
.venv/Lib/site-packages/prompt_toolkit/output/plain_text.py
Normal file
@ -0,0 +1,145 @@
|
||||
from typing import List, TextIO
|
||||
|
||||
from prompt_toolkit.cursor_shapes import CursorShape
|
||||
from prompt_toolkit.data_structures import Size
|
||||
from prompt_toolkit.styles import Attrs
|
||||
|
||||
from .base import Output
|
||||
from .color_depth import ColorDepth
|
||||
from .flush_stdout import flush_stdout
|
||||
|
||||
__all__ = ["PlainTextOutput"]
|
||||
|
||||
|
||||
class PlainTextOutput(Output):
|
||||
"""
|
||||
Output that won't include any ANSI escape sequences.
|
||||
|
||||
Useful when stdout is not a terminal. Maybe stdout is redirected to a file.
|
||||
In this case, if `print_formatted_text` is used, for instance, we don't
|
||||
want to include formatting.
|
||||
|
||||
(The code is mostly identical to `Vt100_Output`, but without the
|
||||
formatting.)
|
||||
"""
|
||||
|
||||
def __init__(self, stdout: TextIO, write_binary: bool = True) -> None:
|
||||
assert all(hasattr(stdout, a) for a in ("write", "flush"))
|
||||
|
||||
if write_binary:
|
||||
assert hasattr(stdout, "encoding")
|
||||
|
||||
self.stdout: TextIO = stdout
|
||||
self.write_binary = write_binary
|
||||
self._buffer: List[str] = []
|
||||
|
||||
def fileno(self) -> int:
|
||||
"There is no sensible default for fileno()."
|
||||
return self.stdout.fileno()
|
||||
|
||||
def encoding(self) -> str:
|
||||
return "utf-8"
|
||||
|
||||
def write(self, data: str) -> None:
|
||||
self._buffer.append(data)
|
||||
|
||||
def write_raw(self, data: str) -> None:
|
||||
self._buffer.append(data)
|
||||
|
||||
def set_title(self, title: str) -> None:
|
||||
pass
|
||||
|
||||
def clear_title(self) -> None:
|
||||
pass
|
||||
|
||||
def flush(self) -> None:
|
||||
if not self._buffer:
|
||||
return
|
||||
|
||||
data = "".join(self._buffer)
|
||||
self._buffer = []
|
||||
flush_stdout(self.stdout, data, write_binary=self.write_binary)
|
||||
|
||||
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:
|
||||
self._buffer.append("\n")
|
||||
|
||||
def cursor_forward(self, amount: int) -> None:
|
||||
self._buffer.append(" " * amount)
|
||||
|
||||
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 8
|
||||
|
||||
def get_default_color_depth(self) -> ColorDepth:
|
||||
return ColorDepth.DEPTH_1_BIT
|
753
.venv/Lib/site-packages/prompt_toolkit/output/vt100.py
Normal file
753
.venv/Lib/site-packages/prompt_toolkit/output/vt100.py
Normal file
@ -0,0 +1,753 @@
|
||||
"""
|
||||
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 .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),
|
||||
}
|
||||
|
||||
# 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.
|
||||
|
||||
def set_cursor_shape(self, cursor_shape: CursorShape) -> None:
|
||||
if cursor_shape == CursorShape._NEVER_CHANGE:
|
||||
return
|
||||
|
||||
self._cursor_shape_changed = True
|
||||
self.write_raw(
|
||||
{
|
||||
CursorShape.BLOCK: "\x1b[2 q",
|
||||
CursorShape.BEAM: "\x1b[6 q",
|
||||
CursorShape.UNDERLINE: "\x1b[4 q",
|
||||
CursorShape.BLINKING_BLOCK: "\x1b[1 q",
|
||||
CursorShape.BLINKING_BEAM: "\x1b[5 q",
|
||||
CursorShape.BLINKING_UNDERLINE: "\x1b[3 q",
|
||||
}.get(cursor_shape, "")
|
||||
)
|
||||
|
||||
def reset_cursor_shape(self) -> None:
|
||||
"Reset cursor shape."
|
||||
# (Only reset cursor shape, if we ever changed it.)
|
||||
if self._cursor_shape_changed:
|
||||
self._cursor_shape_changed = False
|
||||
|
||||
# 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 = []
|
||||
|
||||
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
|
687
.venv/Lib/site-packages/prompt_toolkit/output/win32.py
Normal file
687
.venv/Lib/site-packages/prompt_toolkit/output/win32.py
Normal file
@ -0,0 +1,687 @@
|
||||
import sys
|
||||
|
||||
assert sys.platform == "win32"
|
||||
|
||||
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 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((f" Error in {func.__name__!r} {e!r} {e}\n").encode())
|
||||
|
||||
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]
|
107
.venv/Lib/site-packages/prompt_toolkit/output/windows10.py
Normal file
107
.venv/Lib/site-packages/prompt_toolkit/output/windows10.py
Normal file
@ -0,0 +1,107 @@
|
||||
import sys
|
||||
|
||||
assert sys.platform == "win32"
|
||||
|
||||
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.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 sys.platform != "win32":
|
||||
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)
|
Reference in New Issue
Block a user