Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
64
venv/Lib/site-packages/prompt_toolkit/styles/__init__.py
Normal file
64
venv/Lib/site-packages/prompt_toolkit/styles/__init__.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
"""
|
||||
Styling for prompt_toolkit applications.
|
||||
"""
|
||||
from .base import (
|
||||
ANSI_COLOR_NAMES,
|
||||
DEFAULT_ATTRS,
|
||||
Attrs,
|
||||
BaseStyle,
|
||||
DummyStyle,
|
||||
DynamicStyle,
|
||||
)
|
||||
from .defaults import default_pygments_style, default_ui_style
|
||||
from .named_colors import NAMED_COLORS
|
||||
from .pygments import (
|
||||
pygments_token_to_classname,
|
||||
style_from_pygments_cls,
|
||||
style_from_pygments_dict,
|
||||
)
|
||||
from .style import Priority, Style, merge_styles, parse_color
|
||||
from .style_transformation import (
|
||||
AdjustBrightnessStyleTransformation,
|
||||
ConditionalStyleTransformation,
|
||||
DummyStyleTransformation,
|
||||
DynamicStyleTransformation,
|
||||
ReverseStyleTransformation,
|
||||
SetDefaultColorStyleTransformation,
|
||||
StyleTransformation,
|
||||
SwapLightAndDarkStyleTransformation,
|
||||
merge_style_transformations,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# Base.
|
||||
"Attrs",
|
||||
"DEFAULT_ATTRS",
|
||||
"ANSI_COLOR_NAMES",
|
||||
"BaseStyle",
|
||||
"DummyStyle",
|
||||
"DynamicStyle",
|
||||
# Defaults.
|
||||
"default_ui_style",
|
||||
"default_pygments_style",
|
||||
# Style.
|
||||
"Style",
|
||||
"Priority",
|
||||
"merge_styles",
|
||||
"parse_color",
|
||||
# Style transformation.
|
||||
"StyleTransformation",
|
||||
"SwapLightAndDarkStyleTransformation",
|
||||
"ReverseStyleTransformation",
|
||||
"SetDefaultColorStyleTransformation",
|
||||
"AdjustBrightnessStyleTransformation",
|
||||
"DummyStyleTransformation",
|
||||
"ConditionalStyleTransformation",
|
||||
"DynamicStyleTransformation",
|
||||
"merge_style_transformations",
|
||||
# Pygments.
|
||||
"style_from_pygments_cls",
|
||||
"style_from_pygments_dict",
|
||||
"pygments_token_to_classname",
|
||||
# Named colors.
|
||||
"NAMED_COLORS",
|
||||
]
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
181
venv/Lib/site-packages/prompt_toolkit/styles/base.py
Normal file
181
venv/Lib/site-packages/prompt_toolkit/styles/base.py
Normal file
|
@ -0,0 +1,181 @@
|
|||
"""
|
||||
The base classes for the styling.
|
||||
"""
|
||||
from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
from typing import Callable, Dict, Hashable, List, NamedTuple, Optional, Tuple
|
||||
|
||||
__all__ = [
|
||||
"Attrs",
|
||||
"DEFAULT_ATTRS",
|
||||
"ANSI_COLOR_NAMES",
|
||||
"ANSI_COLOR_NAMES_ALIASES",
|
||||
"BaseStyle",
|
||||
"DummyStyle",
|
||||
"DynamicStyle",
|
||||
]
|
||||
|
||||
|
||||
#: Style attributes.
|
||||
Attrs = NamedTuple(
|
||||
"Attrs",
|
||||
[
|
||||
("color", Optional[str]),
|
||||
("bgcolor", Optional[str]),
|
||||
("bold", Optional[bool]),
|
||||
("underline", Optional[bool]),
|
||||
("italic", Optional[bool]),
|
||||
("blink", Optional[bool]),
|
||||
("reverse", Optional[bool]),
|
||||
("hidden", Optional[bool]),
|
||||
],
|
||||
)
|
||||
|
||||
"""
|
||||
:param color: Hexadecimal string. E.g. '000000' or Ansi color name: e.g. 'ansiblue'
|
||||
:param bgcolor: Hexadecimal string. E.g. 'ffffff' or Ansi color name: e.g. 'ansired'
|
||||
:param bold: Boolean
|
||||
:param underline: Boolean
|
||||
:param italic: Boolean
|
||||
:param blink: Boolean
|
||||
:param reverse: Boolean
|
||||
:param hidden: Boolean
|
||||
"""
|
||||
|
||||
#: The default `Attrs`.
|
||||
DEFAULT_ATTRS = Attrs(
|
||||
color="",
|
||||
bgcolor="",
|
||||
bold=False,
|
||||
underline=False,
|
||||
italic=False,
|
||||
blink=False,
|
||||
reverse=False,
|
||||
hidden=False,
|
||||
)
|
||||
|
||||
|
||||
#: ``Attrs.bgcolor/fgcolor`` can be in either 'ffffff' format, or can be any of
|
||||
#: the following in case we want to take colors from the 8/16 color palette.
|
||||
#: Usually, in that case, the terminal application allows to configure the RGB
|
||||
#: values for these names.
|
||||
#: ISO 6429 colors
|
||||
ANSI_COLOR_NAMES = [
|
||||
"ansidefault",
|
||||
# Low intensity, dark. (One or two components 0x80, the other 0x00.)
|
||||
"ansiblack",
|
||||
"ansired",
|
||||
"ansigreen",
|
||||
"ansiyellow",
|
||||
"ansiblue",
|
||||
"ansimagenta",
|
||||
"ansicyan",
|
||||
"ansigray",
|
||||
# High intensity, bright. (One or two components 0xff, the other 0x00. Not supported everywhere.)
|
||||
"ansibrightblack",
|
||||
"ansibrightred",
|
||||
"ansibrightgreen",
|
||||
"ansibrightyellow",
|
||||
"ansibrightblue",
|
||||
"ansibrightmagenta",
|
||||
"ansibrightcyan",
|
||||
"ansiwhite",
|
||||
]
|
||||
|
||||
|
||||
# People don't use the same ANSI color names everywhere. In prompt_toolkit 1.0
|
||||
# we used some unconventional names (which were contributed like that to
|
||||
# Pygments). This is fixed now, but we still support the old names.
|
||||
|
||||
# The table below maps the old aliases to the current names.
|
||||
ANSI_COLOR_NAMES_ALIASES: Dict[str, str] = {
|
||||
"ansidarkgray": "ansibrightblack",
|
||||
"ansiteal": "ansicyan",
|
||||
"ansiturquoise": "ansibrightcyan",
|
||||
"ansibrown": "ansiyellow",
|
||||
"ansipurple": "ansimagenta",
|
||||
"ansifuchsia": "ansibrightmagenta",
|
||||
"ansilightgray": "ansigray",
|
||||
"ansidarkred": "ansired",
|
||||
"ansidarkgreen": "ansigreen",
|
||||
"ansidarkblue": "ansiblue",
|
||||
}
|
||||
assert set(ANSI_COLOR_NAMES_ALIASES.values()).issubset(set(ANSI_COLOR_NAMES))
|
||||
assert not (set(ANSI_COLOR_NAMES_ALIASES.keys()) & set(ANSI_COLOR_NAMES))
|
||||
|
||||
|
||||
class BaseStyle(metaclass=ABCMeta):
|
||||
"""
|
||||
Abstract base class for prompt_toolkit styles.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_attrs_for_style_str(
|
||||
self, style_str: str, default: Attrs = DEFAULT_ATTRS
|
||||
) -> Attrs:
|
||||
"""
|
||||
Return :class:`.Attrs` for the given style string.
|
||||
|
||||
:param style_str: The style string. This can contain inline styling as
|
||||
well as classnames (e.g. "class:title").
|
||||
:param default: `Attrs` to be used if no styling was defined.
|
||||
"""
|
||||
|
||||
@abstractproperty
|
||||
def style_rules(self) -> List[Tuple[str, str]]:
|
||||
"""
|
||||
The list of style rules, used to create this style.
|
||||
(Required for `DynamicStyle` and `_MergedStyle` to work.)
|
||||
"""
|
||||
return []
|
||||
|
||||
@abstractmethod
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
"""
|
||||
Invalidation hash for the style. When this changes over time, the
|
||||
renderer knows that something in the style changed, and that everything
|
||||
has to be redrawn.
|
||||
"""
|
||||
|
||||
|
||||
class DummyStyle(BaseStyle):
|
||||
"""
|
||||
A style that doesn't style anything.
|
||||
"""
|
||||
|
||||
def get_attrs_for_style_str(
|
||||
self, style_str: str, default: Attrs = DEFAULT_ATTRS
|
||||
) -> Attrs:
|
||||
return default
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return 1 # Always the same value.
|
||||
|
||||
@property
|
||||
def style_rules(self) -> List[Tuple[str, str]]:
|
||||
return []
|
||||
|
||||
|
||||
class DynamicStyle(BaseStyle):
|
||||
"""
|
||||
Style class that can dynamically returns an other Style.
|
||||
|
||||
:param get_style: Callable that returns a :class:`.Style` instance.
|
||||
"""
|
||||
|
||||
def __init__(self, get_style: Callable[[], Optional[BaseStyle]]):
|
||||
self.get_style = get_style
|
||||
self._dummy = DummyStyle()
|
||||
|
||||
def get_attrs_for_style_str(
|
||||
self, style_str: str, default: Attrs = DEFAULT_ATTRS
|
||||
) -> Attrs:
|
||||
style = self.get_style() or self._dummy
|
||||
|
||||
return style.get_attrs_for_style_str(style_str, default)
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return (self.get_style() or self._dummy).invalidation_hash()
|
||||
|
||||
@property
|
||||
def style_rules(self) -> List[Tuple[str, str]]:
|
||||
return (self.get_style() or self._dummy).style_rules
|
227
venv/Lib/site-packages/prompt_toolkit/styles/defaults.py
Normal file
227
venv/Lib/site-packages/prompt_toolkit/styles/defaults.py
Normal file
|
@ -0,0 +1,227 @@
|
|||
"""
|
||||
The default styling.
|
||||
"""
|
||||
from prompt_toolkit.cache import memoized
|
||||
|
||||
from .base import ANSI_COLOR_NAMES
|
||||
from .named_colors import NAMED_COLORS
|
||||
from .style import BaseStyle, Style, merge_styles
|
||||
|
||||
__all__ = [
|
||||
"default_ui_style",
|
||||
"default_pygments_style",
|
||||
]
|
||||
|
||||
#: Default styling. Mapping from classnames to their style definition.
|
||||
PROMPT_TOOLKIT_STYLE = [
|
||||
# Highlighting of search matches in document.
|
||||
("search", "bg:ansibrightyellow ansiblack"),
|
||||
("search.current", ""),
|
||||
# Incremental search.
|
||||
("incsearch", ""),
|
||||
("incsearch.current", "reverse"),
|
||||
# Highlighting of select text in document.
|
||||
("selected", "reverse"),
|
||||
("cursor-column", "bg:#dddddd"),
|
||||
("cursor-line", "underline"),
|
||||
("color-column", "bg:#ccaacc"),
|
||||
# Highlighting of matching brackets.
|
||||
("matching-bracket", ""),
|
||||
("matching-bracket.other", "#000000 bg:#aacccc"),
|
||||
("matching-bracket.cursor", "#ff8888 bg:#880000"),
|
||||
# Styling of other cursors, in case of block editing.
|
||||
("multiple-cursors", "#000000 bg:#ccccaa"),
|
||||
# Line numbers.
|
||||
("line-number", "#888888"),
|
||||
("line-number.current", "bold"),
|
||||
("tilde", "#8888ff"),
|
||||
# Default prompt.
|
||||
("prompt", ""),
|
||||
("prompt.arg", "noinherit"),
|
||||
("prompt.arg.text", ""),
|
||||
("prompt.search", "noinherit"),
|
||||
("prompt.search.text", ""),
|
||||
# Search toolbar.
|
||||
("search-toolbar", "bold"),
|
||||
("search-toolbar.text", "nobold"),
|
||||
# System toolbar
|
||||
("system-toolbar", "bold"),
|
||||
("system-toolbar.text", "nobold"),
|
||||
# "arg" toolbar.
|
||||
("arg-toolbar", "bold"),
|
||||
("arg-toolbar.text", "nobold"),
|
||||
# Validation toolbar.
|
||||
("validation-toolbar", "bg:#550000 #ffffff"),
|
||||
("window-too-small", "bg:#550000 #ffffff"),
|
||||
# Completions toolbar.
|
||||
("completion-toolbar", "bg:#bbbbbb #000000"),
|
||||
("completion-toolbar.arrow", "bg:#bbbbbb #000000 bold"),
|
||||
("completion-toolbar.completion", "bg:#bbbbbb #000000"),
|
||||
("completion-toolbar.completion.current", "bg:#444444 #ffffff"),
|
||||
# Completions menu.
|
||||
("completion-menu", "bg:#bbbbbb #000000"),
|
||||
("completion-menu.completion", ""),
|
||||
("completion-menu.completion.current", "bg:#888888 #ffffff"),
|
||||
("completion-menu.meta.completion", "bg:#999999 #000000"),
|
||||
("completion-menu.meta.completion.current", "bg:#aaaaaa #000000"),
|
||||
("completion-menu.multi-column-meta", "bg:#aaaaaa #000000"),
|
||||
# Fuzzy matches in completion menu (for FuzzyCompleter).
|
||||
("completion-menu.completion fuzzymatch.outside", "fg:#444444"),
|
||||
("completion-menu.completion fuzzymatch.inside", "bold"),
|
||||
("completion-menu.completion fuzzymatch.inside.character", "underline"),
|
||||
("completion-menu.completion.current fuzzymatch.outside", "fg:default"),
|
||||
("completion-menu.completion.current fuzzymatch.inside", "nobold"),
|
||||
# Styling of readline-like completions.
|
||||
("readline-like-completions", ""),
|
||||
("readline-like-completions.completion", ""),
|
||||
("readline-like-completions.completion fuzzymatch.outside", "#888888"),
|
||||
("readline-like-completions.completion fuzzymatch.inside", ""),
|
||||
("readline-like-completions.completion fuzzymatch.inside.character", "underline"),
|
||||
# Scrollbars.
|
||||
("scrollbar.background", "bg:#aaaaaa"),
|
||||
("scrollbar.button", "bg:#444444"),
|
||||
("scrollbar.arrow", "noinherit bold"),
|
||||
# Start/end of scrollbars. Adding 'underline' here provides a nice little
|
||||
# detail to the progress bar, but it doesn't look good on all terminals.
|
||||
# ('scrollbar.start', 'underline #ffffff'),
|
||||
# ('scrollbar.end', 'underline #000000'),
|
||||
# Auto suggestion text.
|
||||
("auto-suggestion", "#666666"),
|
||||
# Trailing whitespace and tabs.
|
||||
("trailing-whitespace", "#999999"),
|
||||
("tab", "#999999"),
|
||||
# When Control-C/D has been pressed. Grayed.
|
||||
("aborting", "#888888 bg:default noreverse noitalic nounderline noblink"),
|
||||
("exiting", "#888888 bg:default noreverse noitalic nounderline noblink"),
|
||||
# Entering a Vi digraph.
|
||||
("digraph", "#4444ff"),
|
||||
# Control characters, like ^C, ^X.
|
||||
("control-character", "ansiblue"),
|
||||
# Non-breaking space.
|
||||
("nbsp", "underline ansiyellow"),
|
||||
# Default styling of HTML elements.
|
||||
("i", "italic"),
|
||||
("u", "underline"),
|
||||
("b", "bold"),
|
||||
("em", "italic"),
|
||||
("strong", "bold"),
|
||||
("hidden", "hidden"),
|
||||
# It should be possible to use the style names in HTML.
|
||||
# <reverse>...</reverse> or <noreverse>...</noreverse>.
|
||||
("italic", "italic"),
|
||||
("underline", "underline"),
|
||||
("bold", "bold"),
|
||||
("reverse", "reverse"),
|
||||
("noitalic", "noitalic"),
|
||||
("nounderline", "nounderline"),
|
||||
("nobold", "nobold"),
|
||||
("noreverse", "noreverse"),
|
||||
# Prompt bottom toolbar
|
||||
("bottom-toolbar", "reverse"),
|
||||
]
|
||||
|
||||
|
||||
# Style that will turn for instance the class 'red' into 'red'.
|
||||
COLORS_STYLE = [(name, "fg:" + name) for name in ANSI_COLOR_NAMES] + [
|
||||
(name.lower(), "fg:" + name) for name in NAMED_COLORS
|
||||
]
|
||||
|
||||
|
||||
WIDGETS_STYLE = [
|
||||
# Dialog windows.
|
||||
("dialog", "bg:#4444ff"),
|
||||
("dialog.body", "bg:#ffffff #000000"),
|
||||
("dialog.body text-area", "bg:#cccccc"),
|
||||
("dialog.body text-area last-line", "underline"),
|
||||
("dialog frame.label", "#ff0000 bold"),
|
||||
# Scrollbars in dialogs.
|
||||
("dialog.body scrollbar.background", ""),
|
||||
("dialog.body scrollbar.button", "bg:#000000"),
|
||||
("dialog.body scrollbar.arrow", ""),
|
||||
("dialog.body scrollbar.start", "nounderline"),
|
||||
("dialog.body scrollbar.end", "nounderline"),
|
||||
# Buttons.
|
||||
("button", ""),
|
||||
("button.arrow", "bold"),
|
||||
("button.focused", "bg:#aa0000 #ffffff"),
|
||||
# Menu bars.
|
||||
("menu-bar", "bg:#aaaaaa #000000"),
|
||||
("menu-bar.selected-item", "bg:#ffffff #000000"),
|
||||
("menu", "bg:#888888 #ffffff"),
|
||||
("menu.border", "#aaaaaa"),
|
||||
("menu.border shadow", "#444444"),
|
||||
# Shadows.
|
||||
("dialog shadow", "bg:#000088"),
|
||||
("dialog.body shadow", "bg:#aaaaaa"),
|
||||
("progress-bar", "bg:#000088"),
|
||||
("progress-bar.used", "bg:#ff0000"),
|
||||
]
|
||||
|
||||
|
||||
# The default Pygments style, include this by default in case a Pygments lexer
|
||||
# is used.
|
||||
PYGMENTS_DEFAULT_STYLE = {
|
||||
"pygments.whitespace": "#bbbbbb",
|
||||
"pygments.comment": "italic #408080",
|
||||
"pygments.comment.preproc": "noitalic #bc7a00",
|
||||
"pygments.keyword": "bold #008000",
|
||||
"pygments.keyword.pseudo": "nobold",
|
||||
"pygments.keyword.type": "nobold #b00040",
|
||||
"pygments.operator": "#666666",
|
||||
"pygments.operator.word": "bold #aa22ff",
|
||||
"pygments.name.builtin": "#008000",
|
||||
"pygments.name.function": "#0000ff",
|
||||
"pygments.name.class": "bold #0000ff",
|
||||
"pygments.name.namespace": "bold #0000ff",
|
||||
"pygments.name.exception": "bold #d2413a",
|
||||
"pygments.name.variable": "#19177c",
|
||||
"pygments.name.constant": "#880000",
|
||||
"pygments.name.label": "#a0a000",
|
||||
"pygments.name.entity": "bold #999999",
|
||||
"pygments.name.attribute": "#7d9029",
|
||||
"pygments.name.tag": "bold #008000",
|
||||
"pygments.name.decorator": "#aa22ff",
|
||||
# Note: In Pygments, Token.String is an alias for Token.Literal.String,
|
||||
# and Token.Number as an alias for Token.Literal.Number.
|
||||
"pygments.literal.string": "#ba2121",
|
||||
"pygments.literal.string.doc": "italic",
|
||||
"pygments.literal.string.interpol": "bold #bb6688",
|
||||
"pygments.literal.string.escape": "bold #bb6622",
|
||||
"pygments.literal.string.regex": "#bb6688",
|
||||
"pygments.literal.string.symbol": "#19177c",
|
||||
"pygments.literal.string.other": "#008000",
|
||||
"pygments.literal.number": "#666666",
|
||||
"pygments.generic.heading": "bold #000080",
|
||||
"pygments.generic.subheading": "bold #800080",
|
||||
"pygments.generic.deleted": "#a00000",
|
||||
"pygments.generic.inserted": "#00a000",
|
||||
"pygments.generic.error": "#ff0000",
|
||||
"pygments.generic.emph": "italic",
|
||||
"pygments.generic.strong": "bold",
|
||||
"pygments.generic.prompt": "bold #000080",
|
||||
"pygments.generic.output": "#888",
|
||||
"pygments.generic.traceback": "#04d",
|
||||
"pygments.error": "border:#ff0000",
|
||||
}
|
||||
|
||||
|
||||
@memoized()
|
||||
def default_ui_style() -> BaseStyle:
|
||||
"""
|
||||
Create a default `Style` object.
|
||||
"""
|
||||
return merge_styles(
|
||||
[
|
||||
Style(PROMPT_TOOLKIT_STYLE),
|
||||
Style(COLORS_STYLE),
|
||||
Style(WIDGETS_STYLE),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@memoized()
|
||||
def default_pygments_style() -> Style:
|
||||
"""
|
||||
Create a `Style` object that contains the default Pygments style.
|
||||
"""
|
||||
return Style.from_dict(PYGMENTS_DEFAULT_STYLE)
|
161
venv/Lib/site-packages/prompt_toolkit/styles/named_colors.py
Normal file
161
venv/Lib/site-packages/prompt_toolkit/styles/named_colors.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
"""
|
||||
All modern web browsers support these 140 color names.
|
||||
Taken from: https://www.w3schools.com/colors/colors_names.asp
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
__all__ = [
|
||||
"NAMED_COLORS",
|
||||
]
|
||||
|
||||
|
||||
NAMED_COLORS: Dict[str, str] = {
|
||||
"AliceBlue": "#f0f8ff",
|
||||
"AntiqueWhite": "#faebd7",
|
||||
"Aqua": "#00ffff",
|
||||
"Aquamarine": "#7fffd4",
|
||||
"Azure": "#f0ffff",
|
||||
"Beige": "#f5f5dc",
|
||||
"Bisque": "#ffe4c4",
|
||||
"Black": "#000000",
|
||||
"BlanchedAlmond": "#ffebcd",
|
||||
"Blue": "#0000ff",
|
||||
"BlueViolet": "#8a2be2",
|
||||
"Brown": "#a52a2a",
|
||||
"BurlyWood": "#deb887",
|
||||
"CadetBlue": "#5f9ea0",
|
||||
"Chartreuse": "#7fff00",
|
||||
"Chocolate": "#d2691e",
|
||||
"Coral": "#ff7f50",
|
||||
"CornflowerBlue": "#6495ed",
|
||||
"Cornsilk": "#fff8dc",
|
||||
"Crimson": "#dc143c",
|
||||
"Cyan": "#00ffff",
|
||||
"DarkBlue": "#00008b",
|
||||
"DarkCyan": "#008b8b",
|
||||
"DarkGoldenRod": "#b8860b",
|
||||
"DarkGray": "#a9a9a9",
|
||||
"DarkGreen": "#006400",
|
||||
"DarkGrey": "#a9a9a9",
|
||||
"DarkKhaki": "#bdb76b",
|
||||
"DarkMagenta": "#8b008b",
|
||||
"DarkOliveGreen": "#556b2f",
|
||||
"DarkOrange": "#ff8c00",
|
||||
"DarkOrchid": "#9932cc",
|
||||
"DarkRed": "#8b0000",
|
||||
"DarkSalmon": "#e9967a",
|
||||
"DarkSeaGreen": "#8fbc8f",
|
||||
"DarkSlateBlue": "#483d8b",
|
||||
"DarkSlateGray": "#2f4f4f",
|
||||
"DarkSlateGrey": "#2f4f4f",
|
||||
"DarkTurquoise": "#00ced1",
|
||||
"DarkViolet": "#9400d3",
|
||||
"DeepPink": "#ff1493",
|
||||
"DeepSkyBlue": "#00bfff",
|
||||
"DimGray": "#696969",
|
||||
"DimGrey": "#696969",
|
||||
"DodgerBlue": "#1e90ff",
|
||||
"FireBrick": "#b22222",
|
||||
"FloralWhite": "#fffaf0",
|
||||
"ForestGreen": "#228b22",
|
||||
"Fuchsia": "#ff00ff",
|
||||
"Gainsboro": "#dcdcdc",
|
||||
"GhostWhite": "#f8f8ff",
|
||||
"Gold": "#ffd700",
|
||||
"GoldenRod": "#daa520",
|
||||
"Gray": "#808080",
|
||||
"Green": "#008000",
|
||||
"GreenYellow": "#adff2f",
|
||||
"Grey": "#808080",
|
||||
"HoneyDew": "#f0fff0",
|
||||
"HotPink": "#ff69b4",
|
||||
"IndianRed": "#cd5c5c",
|
||||
"Indigo": "#4b0082",
|
||||
"Ivory": "#fffff0",
|
||||
"Khaki": "#f0e68c",
|
||||
"Lavender": "#e6e6fa",
|
||||
"LavenderBlush": "#fff0f5",
|
||||
"LawnGreen": "#7cfc00",
|
||||
"LemonChiffon": "#fffacd",
|
||||
"LightBlue": "#add8e6",
|
||||
"LightCoral": "#f08080",
|
||||
"LightCyan": "#e0ffff",
|
||||
"LightGoldenRodYellow": "#fafad2",
|
||||
"LightGray": "#d3d3d3",
|
||||
"LightGreen": "#90ee90",
|
||||
"LightGrey": "#d3d3d3",
|
||||
"LightPink": "#ffb6c1",
|
||||
"LightSalmon": "#ffa07a",
|
||||
"LightSeaGreen": "#20b2aa",
|
||||
"LightSkyBlue": "#87cefa",
|
||||
"LightSlateGray": "#778899",
|
||||
"LightSlateGrey": "#778899",
|
||||
"LightSteelBlue": "#b0c4de",
|
||||
"LightYellow": "#ffffe0",
|
||||
"Lime": "#00ff00",
|
||||
"LimeGreen": "#32cd32",
|
||||
"Linen": "#faf0e6",
|
||||
"Magenta": "#ff00ff",
|
||||
"Maroon": "#800000",
|
||||
"MediumAquaMarine": "#66cdaa",
|
||||
"MediumBlue": "#0000cd",
|
||||
"MediumOrchid": "#ba55d3",
|
||||
"MediumPurple": "#9370db",
|
||||
"MediumSeaGreen": "#3cb371",
|
||||
"MediumSlateBlue": "#7b68ee",
|
||||
"MediumSpringGreen": "#00fa9a",
|
||||
"MediumTurquoise": "#48d1cc",
|
||||
"MediumVioletRed": "#c71585",
|
||||
"MidnightBlue": "#191970",
|
||||
"MintCream": "#f5fffa",
|
||||
"MistyRose": "#ffe4e1",
|
||||
"Moccasin": "#ffe4b5",
|
||||
"NavajoWhite": "#ffdead",
|
||||
"Navy": "#000080",
|
||||
"OldLace": "#fdf5e6",
|
||||
"Olive": "#808000",
|
||||
"OliveDrab": "#6b8e23",
|
||||
"Orange": "#ffa500",
|
||||
"OrangeRed": "#ff4500",
|
||||
"Orchid": "#da70d6",
|
||||
"PaleGoldenRod": "#eee8aa",
|
||||
"PaleGreen": "#98fb98",
|
||||
"PaleTurquoise": "#afeeee",
|
||||
"PaleVioletRed": "#db7093",
|
||||
"PapayaWhip": "#ffefd5",
|
||||
"PeachPuff": "#ffdab9",
|
||||
"Peru": "#cd853f",
|
||||
"Pink": "#ffc0cb",
|
||||
"Plum": "#dda0dd",
|
||||
"PowderBlue": "#b0e0e6",
|
||||
"Purple": "#800080",
|
||||
"RebeccaPurple": "#663399",
|
||||
"Red": "#ff0000",
|
||||
"RosyBrown": "#bc8f8f",
|
||||
"RoyalBlue": "#4169e1",
|
||||
"SaddleBrown": "#8b4513",
|
||||
"Salmon": "#fa8072",
|
||||
"SandyBrown": "#f4a460",
|
||||
"SeaGreen": "#2e8b57",
|
||||
"SeaShell": "#fff5ee",
|
||||
"Sienna": "#a0522d",
|
||||
"Silver": "#c0c0c0",
|
||||
"SkyBlue": "#87ceeb",
|
||||
"SlateBlue": "#6a5acd",
|
||||
"SlateGray": "#708090",
|
||||
"SlateGrey": "#708090",
|
||||
"Snow": "#fffafa",
|
||||
"SpringGreen": "#00ff7f",
|
||||
"SteelBlue": "#4682b4",
|
||||
"Tan": "#d2b48c",
|
||||
"Teal": "#008080",
|
||||
"Thistle": "#d8bfd8",
|
||||
"Tomato": "#ff6347",
|
||||
"Turquoise": "#40e0d0",
|
||||
"Violet": "#ee82ee",
|
||||
"Wheat": "#f5deb3",
|
||||
"White": "#ffffff",
|
||||
"WhiteSmoke": "#f5f5f5",
|
||||
"Yellow": "#ffff00",
|
||||
"YellowGreen": "#9acd32",
|
||||
}
|
67
venv/Lib/site-packages/prompt_toolkit/styles/pygments.py
Normal file
67
venv/Lib/site-packages/prompt_toolkit/styles/pygments.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
"""
|
||||
Adaptor for building prompt_toolkit styles, starting from a Pygments style.
|
||||
|
||||
Usage::
|
||||
|
||||
from pygments.styles.tango import TangoStyle
|
||||
style = style_from_pygments_cls(pygments_style_cls=TangoStyle)
|
||||
"""
|
||||
from typing import TYPE_CHECKING, Dict, Type
|
||||
|
||||
from .style import Style
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pygments.style import Style as PygmentsStyle
|
||||
from pygments.token import Token
|
||||
|
||||
|
||||
__all__ = [
|
||||
"style_from_pygments_cls",
|
||||
"style_from_pygments_dict",
|
||||
"pygments_token_to_classname",
|
||||
]
|
||||
|
||||
|
||||
def style_from_pygments_cls(pygments_style_cls: Type["PygmentsStyle"]) -> Style:
|
||||
"""
|
||||
Shortcut to create a :class:`.Style` instance from a Pygments style class
|
||||
and a style dictionary.
|
||||
|
||||
Example::
|
||||
|
||||
from prompt_toolkit.styles.from_pygments import style_from_pygments_cls
|
||||
from pygments.styles import get_style_by_name
|
||||
style = style_from_pygments_cls(get_style_by_name('monokai'))
|
||||
|
||||
:param pygments_style_cls: Pygments style class to start from.
|
||||
"""
|
||||
# Import inline.
|
||||
from pygments.style import Style as PygmentsStyle
|
||||
|
||||
assert issubclass(pygments_style_cls, PygmentsStyle)
|
||||
|
||||
return style_from_pygments_dict(pygments_style_cls.styles)
|
||||
|
||||
|
||||
def style_from_pygments_dict(pygments_dict: Dict["Token", str]) -> Style:
|
||||
"""
|
||||
Create a :class:`.Style` instance from a Pygments style dictionary.
|
||||
(One that maps Token objects to style strings.)
|
||||
"""
|
||||
pygments_style = []
|
||||
|
||||
for token, style in pygments_dict.items():
|
||||
pygments_style.append((pygments_token_to_classname(token), style))
|
||||
|
||||
return Style(pygments_style)
|
||||
|
||||
|
||||
def pygments_token_to_classname(token: "Token") -> str:
|
||||
"""
|
||||
Turn e.g. `Token.Name.Exception` into `'pygments.name.exception'`.
|
||||
|
||||
(Our Pygments lexer will also turn the tokens that pygments produces in a
|
||||
prompt_toolkit list of fragments that match these styling rules.)
|
||||
"""
|
||||
parts = ("pygments",) + token
|
||||
return ".".join(parts).lower()
|
398
venv/Lib/site-packages/prompt_toolkit/styles/style.py
Normal file
398
venv/Lib/site-packages/prompt_toolkit/styles/style.py
Normal file
|
@ -0,0 +1,398 @@
|
|||
"""
|
||||
Tool for creating styles from a dictionary.
|
||||
"""
|
||||
import itertools
|
||||
import re
|
||||
import sys
|
||||
from enum import Enum
|
||||
from typing import Dict, Hashable, List, Set, Tuple, TypeVar
|
||||
|
||||
from prompt_toolkit.cache import SimpleCache
|
||||
|
||||
from .base import (
|
||||
ANSI_COLOR_NAMES,
|
||||
ANSI_COLOR_NAMES_ALIASES,
|
||||
DEFAULT_ATTRS,
|
||||
Attrs,
|
||||
BaseStyle,
|
||||
)
|
||||
from .named_colors import NAMED_COLORS
|
||||
|
||||
__all__ = [
|
||||
"Style",
|
||||
"parse_color",
|
||||
"Priority",
|
||||
"merge_styles",
|
||||
]
|
||||
|
||||
_named_colors_lowercase = {k.lower(): v.lstrip("#") for k, v in NAMED_COLORS.items()}
|
||||
|
||||
|
||||
def parse_color(text: str) -> str:
|
||||
"""
|
||||
Parse/validate color format.
|
||||
|
||||
Like in Pygments, but also support the ANSI color names.
|
||||
(These will map to the colors of the 16 color palette.)
|
||||
"""
|
||||
# ANSI color names.
|
||||
if text in ANSI_COLOR_NAMES:
|
||||
return text
|
||||
if text in ANSI_COLOR_NAMES_ALIASES:
|
||||
return ANSI_COLOR_NAMES_ALIASES[text]
|
||||
|
||||
# 140 named colors.
|
||||
try:
|
||||
# Replace by 'hex' value.
|
||||
return _named_colors_lowercase[text.lower()]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Hex codes.
|
||||
if text[0:1] == "#":
|
||||
col = text[1:]
|
||||
|
||||
# Keep this for backwards-compatibility (Pygments does it).
|
||||
# I don't like the '#' prefix for named colors.
|
||||
if col in ANSI_COLOR_NAMES:
|
||||
return col
|
||||
elif col in ANSI_COLOR_NAMES_ALIASES:
|
||||
return ANSI_COLOR_NAMES_ALIASES[col]
|
||||
|
||||
# 6 digit hex color.
|
||||
elif len(col) == 6:
|
||||
return col
|
||||
|
||||
# 3 digit hex color.
|
||||
elif len(col) == 3:
|
||||
return col[0] * 2 + col[1] * 2 + col[2] * 2
|
||||
|
||||
# Default.
|
||||
elif text in ("", "default"):
|
||||
return text
|
||||
|
||||
raise ValueError("Wrong color format %r" % text)
|
||||
|
||||
|
||||
# Attributes, when they are not filled in by a style. None means that we take
|
||||
# the value from the parent.
|
||||
_EMPTY_ATTRS = Attrs(
|
||||
color=None,
|
||||
bgcolor=None,
|
||||
bold=None,
|
||||
underline=None,
|
||||
italic=None,
|
||||
blink=None,
|
||||
reverse=None,
|
||||
hidden=None,
|
||||
)
|
||||
|
||||
|
||||
def _expand_classname(classname: str) -> List[str]:
|
||||
"""
|
||||
Split a single class name at the `.` operator, and build a list of classes.
|
||||
|
||||
E.g. 'a.b.c' becomes ['a', 'a.b', 'a.b.c']
|
||||
"""
|
||||
result = []
|
||||
parts = classname.split(".")
|
||||
|
||||
for i in range(1, len(parts) + 1):
|
||||
result.append(".".join(parts[:i]).lower())
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _parse_style_str(style_str: str) -> Attrs:
|
||||
"""
|
||||
Take a style string, e.g. 'bg:red #88ff00 class:title'
|
||||
and return a `Attrs` instance.
|
||||
"""
|
||||
# Start from default Attrs.
|
||||
if "noinherit" in style_str:
|
||||
attrs = DEFAULT_ATTRS
|
||||
else:
|
||||
attrs = _EMPTY_ATTRS
|
||||
|
||||
# Now update with the given attributes.
|
||||
for part in style_str.split():
|
||||
if part == "noinherit":
|
||||
pass
|
||||
elif part == "bold":
|
||||
attrs = attrs._replace(bold=True)
|
||||
elif part == "nobold":
|
||||
attrs = attrs._replace(bold=False)
|
||||
elif part == "italic":
|
||||
attrs = attrs._replace(italic=True)
|
||||
elif part == "noitalic":
|
||||
attrs = attrs._replace(italic=False)
|
||||
elif part == "underline":
|
||||
attrs = attrs._replace(underline=True)
|
||||
elif part == "nounderline":
|
||||
attrs = attrs._replace(underline=False)
|
||||
|
||||
# prompt_toolkit extensions. Not in Pygments.
|
||||
elif part == "blink":
|
||||
attrs = attrs._replace(blink=True)
|
||||
elif part == "noblink":
|
||||
attrs = attrs._replace(blink=False)
|
||||
elif part == "reverse":
|
||||
attrs = attrs._replace(reverse=True)
|
||||
elif part == "noreverse":
|
||||
attrs = attrs._replace(reverse=False)
|
||||
elif part == "hidden":
|
||||
attrs = attrs._replace(hidden=True)
|
||||
elif part == "nohidden":
|
||||
attrs = attrs._replace(hidden=False)
|
||||
|
||||
# Pygments properties that we ignore.
|
||||
elif part in ("roman", "sans", "mono"):
|
||||
pass
|
||||
elif part.startswith("border:"):
|
||||
pass
|
||||
|
||||
# Ignore pieces in between square brackets. This is internal stuff.
|
||||
# Like '[transparent]' or '[set-cursor-position]'.
|
||||
elif part.startswith("[") and part.endswith("]"):
|
||||
pass
|
||||
|
||||
# Colors.
|
||||
elif part.startswith("bg:"):
|
||||
attrs = attrs._replace(bgcolor=parse_color(part[3:]))
|
||||
elif part.startswith("fg:"): # The 'fg:' prefix is optional.
|
||||
attrs = attrs._replace(color=parse_color(part[3:]))
|
||||
else:
|
||||
attrs = attrs._replace(color=parse_color(part))
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
CLASS_NAMES_RE = re.compile(r"^[a-z0-9.\s_-]*$") # This one can't contain a comma!
|
||||
|
||||
|
||||
class Priority(Enum):
|
||||
"""
|
||||
The priority of the rules, when a style is created from a dictionary.
|
||||
|
||||
In a `Style`, rules that are defined later will always override previous
|
||||
defined rules, however in a dictionary, the key order was arbitrary before
|
||||
Python 3.6. This means that the style could change at random between rules.
|
||||
|
||||
We have two options:
|
||||
|
||||
- `DICT_KEY_ORDER`: This means, iterate through the dictionary, and take
|
||||
the key/value pairs in order as they come. This is a good option if you
|
||||
have Python >3.6. Rules at the end will override rules at the beginning.
|
||||
- `MOST_PRECISE`: keys that are defined with most precision will get higher
|
||||
priority. (More precise means: more elements.)
|
||||
"""
|
||||
|
||||
DICT_KEY_ORDER = "KEY_ORDER"
|
||||
MOST_PRECISE = "MOST_PRECISE"
|
||||
|
||||
|
||||
# In the latest python verions, we take the dictionary ordering like it is,
|
||||
# In older versions, we sort by by precision. If you need to write code that
|
||||
# runs on all Python versions, it's best to sort them manually, with the most
|
||||
# precise rules at the bottom.
|
||||
if sys.version_info >= (3, 6):
|
||||
default_priority = Priority.DICT_KEY_ORDER
|
||||
else:
|
||||
default_priority = Priority.MOST_PRECISE
|
||||
|
||||
|
||||
class Style(BaseStyle):
|
||||
"""
|
||||
Create a ``Style`` instance from a list of style rules.
|
||||
|
||||
The `style_rules` is supposed to be a list of ('classnames', 'style') tuples.
|
||||
The classnames are a whitespace separated string of class names and the
|
||||
style string is just like a Pygments style definition, but with a few
|
||||
additions: it supports 'reverse' and 'blink'.
|
||||
|
||||
Later rules always override previous rules.
|
||||
|
||||
Usage::
|
||||
|
||||
Style([
|
||||
('title', '#ff0000 bold underline'),
|
||||
('something-else', 'reverse'),
|
||||
('class1 class2', 'reverse'),
|
||||
])
|
||||
|
||||
The ``from_dict`` classmethod is similar, but takes a dictionary as input.
|
||||
"""
|
||||
|
||||
def __init__(self, style_rules: List[Tuple[str, str]]) -> None:
|
||||
class_names_and_attrs = []
|
||||
|
||||
# Loop through the rules in the order they were defined.
|
||||
# Rules that are defined later get priority.
|
||||
for class_names, style_str in style_rules:
|
||||
assert CLASS_NAMES_RE.match(class_names), repr(class_names)
|
||||
|
||||
# The order of the class names doesn't matter.
|
||||
# (But the order of rules does matter.)
|
||||
class_names_set = frozenset(class_names.lower().split())
|
||||
attrs = _parse_style_str(style_str)
|
||||
|
||||
class_names_and_attrs.append((class_names_set, attrs))
|
||||
|
||||
self._style_rules = style_rules
|
||||
self.class_names_and_attrs = class_names_and_attrs
|
||||
|
||||
@property
|
||||
def style_rules(self) -> List[Tuple[str, str]]:
|
||||
return self._style_rules
|
||||
|
||||
@classmethod
|
||||
def from_dict(
|
||||
cls, style_dict: Dict[str, str], priority: Priority = default_priority
|
||||
) -> "Style":
|
||||
"""
|
||||
:param style_dict: Style dictionary.
|
||||
:param priority: `Priority` value.
|
||||
"""
|
||||
if priority == Priority.MOST_PRECISE:
|
||||
|
||||
def key(item: Tuple[str, str]) -> int:
|
||||
# Split on '.' and whitespace. Count elements.
|
||||
return sum(len(i.split(".")) for i in item[0].split())
|
||||
|
||||
return cls(sorted(style_dict.items(), key=key))
|
||||
else:
|
||||
return cls(list(style_dict.items()))
|
||||
|
||||
def get_attrs_for_style_str(
|
||||
self, style_str: str, default: Attrs = DEFAULT_ATTRS
|
||||
) -> Attrs:
|
||||
"""
|
||||
Get `Attrs` for the given style string.
|
||||
"""
|
||||
list_of_attrs = [default]
|
||||
class_names: Set[str] = set()
|
||||
|
||||
# Apply default styling.
|
||||
for names, attr in self.class_names_and_attrs:
|
||||
if not names:
|
||||
list_of_attrs.append(attr)
|
||||
|
||||
# Go from left to right through the style string. Things on the right
|
||||
# take precedence.
|
||||
for part in style_str.split():
|
||||
# This part represents a class.
|
||||
# Do lookup of this class name in the style definition, as well
|
||||
# as all class combinations that we have so far.
|
||||
if part.startswith("class:"):
|
||||
# Expand all class names (comma separated list).
|
||||
new_class_names = []
|
||||
for p in part[6:].lower().split(","):
|
||||
new_class_names.extend(_expand_classname(p))
|
||||
|
||||
for new_name in new_class_names:
|
||||
# Build a set of all possible class combinations to be applied.
|
||||
combos = set()
|
||||
combos.add(frozenset([new_name]))
|
||||
|
||||
for count in range(1, len(class_names) + 1):
|
||||
for c2 in itertools.combinations(class_names, count):
|
||||
combos.add(frozenset(c2 + (new_name,)))
|
||||
|
||||
# Apply the styles that match these class names.
|
||||
for names, attr in self.class_names_and_attrs:
|
||||
if names in combos:
|
||||
list_of_attrs.append(attr)
|
||||
|
||||
class_names.add(new_name)
|
||||
|
||||
# Process inline style.
|
||||
else:
|
||||
inline_attrs = _parse_style_str(part)
|
||||
list_of_attrs.append(inline_attrs)
|
||||
|
||||
return _merge_attrs(list_of_attrs)
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return id(self.class_names_and_attrs)
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def _merge_attrs(list_of_attrs: List[Attrs]) -> Attrs:
|
||||
"""
|
||||
Take a list of :class:`.Attrs` instances and merge them into one.
|
||||
Every `Attr` in the list can override the styling of the previous one. So,
|
||||
the last one has highest priority.
|
||||
"""
|
||||
|
||||
def _or(*values: _T) -> _T:
|
||||
" Take first not-None value, starting at the end. "
|
||||
for v in values[::-1]:
|
||||
if v is not None:
|
||||
return v
|
||||
raise ValueError # Should not happen, there's always one non-null value.
|
||||
|
||||
return Attrs(
|
||||
color=_or("", *[a.color for a in list_of_attrs]),
|
||||
bgcolor=_or("", *[a.bgcolor for a in list_of_attrs]),
|
||||
bold=_or(False, *[a.bold for a in list_of_attrs]),
|
||||
underline=_or(False, *[a.underline for a in list_of_attrs]),
|
||||
italic=_or(False, *[a.italic for a in list_of_attrs]),
|
||||
blink=_or(False, *[a.blink for a in list_of_attrs]),
|
||||
reverse=_or(False, *[a.reverse for a in list_of_attrs]),
|
||||
hidden=_or(False, *[a.hidden for a in list_of_attrs]),
|
||||
)
|
||||
|
||||
|
||||
def merge_styles(styles: List[BaseStyle]) -> "_MergedStyle":
|
||||
"""
|
||||
Merge multiple `Style` objects.
|
||||
"""
|
||||
styles = [s for s in styles if s is not None]
|
||||
return _MergedStyle(styles)
|
||||
|
||||
|
||||
class _MergedStyle(BaseStyle):
|
||||
"""
|
||||
Merge multiple `Style` objects into one.
|
||||
This is supposed to ensure consistency: if any of the given styles changes,
|
||||
then this style will be updated.
|
||||
"""
|
||||
|
||||
# NOTE: previously, we used an algorithm where we did not generate the
|
||||
# combined style. Instead this was a proxy that called one style
|
||||
# after the other, passing the outcome of the previous style as the
|
||||
# default for the next one. This did not work, because that way, the
|
||||
# priorities like described in the `Style` class don't work.
|
||||
# 'class:aborted' was for instance never displayed in gray, because
|
||||
# the next style specified a default color for any text. (The
|
||||
# explicit styling of class:aborted should have taken priority,
|
||||
# because it was more precise.)
|
||||
def __init__(self, styles: List[BaseStyle]) -> None:
|
||||
self.styles = styles
|
||||
self._style: SimpleCache[Hashable, Style] = SimpleCache(maxsize=1)
|
||||
|
||||
@property
|
||||
def _merged_style(self) -> Style:
|
||||
" The `Style` object that has the other styles merged together. "
|
||||
|
||||
def get() -> Style:
|
||||
return Style(self.style_rules)
|
||||
|
||||
return self._style.get(self.invalidation_hash(), get)
|
||||
|
||||
@property
|
||||
def style_rules(self) -> List[Tuple[str, str]]:
|
||||
style_rules = []
|
||||
for s in self.styles:
|
||||
style_rules.extend(s.style_rules)
|
||||
return style_rules
|
||||
|
||||
def get_attrs_for_style_str(
|
||||
self, style_str: str, default: Attrs = DEFAULT_ATTRS
|
||||
) -> Attrs:
|
||||
return self._merged_style.get_attrs_for_style_str(style_str, default)
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return tuple(s.invalidation_hash() for s in self.styles)
|
|
@ -0,0 +1,375 @@
|
|||
"""
|
||||
Collection of style transformations.
|
||||
|
||||
Think of it as a kind of color post processing after the rendering is done.
|
||||
This could be used for instance to change the contrast/saturation; swap light
|
||||
and dark colors or even change certain colors for other colors.
|
||||
|
||||
When the UI is rendered, these transformations can be applied right after the
|
||||
style strings are turned into `Attrs` objects that represent the actual
|
||||
formatting.
|
||||
"""
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from colorsys import hls_to_rgb, rgb_to_hls
|
||||
from typing import Callable, Hashable, Optional, Sequence, Tuple, Union
|
||||
|
||||
from prompt_toolkit.cache import memoized
|
||||
from prompt_toolkit.filters import FilterOrBool, to_filter
|
||||
from prompt_toolkit.utils import AnyFloat, to_float, to_str
|
||||
|
||||
from .base import ANSI_COLOR_NAMES, Attrs
|
||||
from .style import parse_color
|
||||
|
||||
__all__ = [
|
||||
"StyleTransformation",
|
||||
"SwapLightAndDarkStyleTransformation",
|
||||
"ReverseStyleTransformation",
|
||||
"SetDefaultColorStyleTransformation",
|
||||
"AdjustBrightnessStyleTransformation",
|
||||
"DummyStyleTransformation",
|
||||
"ConditionalStyleTransformation",
|
||||
"DynamicStyleTransformation",
|
||||
"merge_style_transformations",
|
||||
]
|
||||
|
||||
|
||||
class StyleTransformation(metaclass=ABCMeta):
|
||||
"""
|
||||
Base class for any style transformation.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
"""
|
||||
Take an `Attrs` object and return a new `Attrs` object.
|
||||
|
||||
Remember that the color formats can be either "ansi..." or a 6 digit
|
||||
lowercase hexadecimal color (without '#' prefix).
|
||||
"""
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
"""
|
||||
When this changes, the cache should be invalidated.
|
||||
"""
|
||||
return "%s-%s" % (self.__class__.__name__, id(self))
|
||||
|
||||
|
||||
class SwapLightAndDarkStyleTransformation(StyleTransformation):
|
||||
"""
|
||||
Turn dark colors into light colors and the other way around.
|
||||
|
||||
This is meant to make color schemes that work on a dark background usable
|
||||
on a light background (and the other way around).
|
||||
|
||||
Notice that this doesn't swap foreground and background like "reverse"
|
||||
does. It turns light green into dark green and the other way around.
|
||||
Foreground and background colors are considered individually.
|
||||
|
||||
Also notice that when <reverse> is used somewhere and no colors are given
|
||||
in particular (like what is the default for the bottom toolbar), then this
|
||||
doesn't change anything. This is what makes sense, because when the
|
||||
'default' color is chosen, it's what works best for the terminal, and
|
||||
reverse works good with that.
|
||||
"""
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
"""
|
||||
Return the `Attrs` used when opposite luminosity should be used.
|
||||
"""
|
||||
# Reverse colors.
|
||||
attrs = attrs._replace(color=get_opposite_color(attrs.color))
|
||||
attrs = attrs._replace(bgcolor=get_opposite_color(attrs.bgcolor))
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
class ReverseStyleTransformation(StyleTransformation):
|
||||
"""
|
||||
Swap the 'reverse' attribute.
|
||||
|
||||
(This is still experimental.)
|
||||
"""
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
return attrs._replace(reverse=not attrs.reverse)
|
||||
|
||||
|
||||
class SetDefaultColorStyleTransformation(StyleTransformation):
|
||||
"""
|
||||
Set default foreground/background color for output that doesn't specify
|
||||
anything. This is useful for overriding the terminal default colors.
|
||||
|
||||
:param fg: Color string or callable that returns a color string for the
|
||||
foreground.
|
||||
:param bg: Like `fg`, but for the background.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, fg: Union[str, Callable[[], str]], bg: Union[str, Callable[[], str]]
|
||||
) -> None:
|
||||
|
||||
self.fg = fg
|
||||
self.bg = bg
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
if attrs.bgcolor in ("", "default"):
|
||||
attrs = attrs._replace(bgcolor=parse_color(to_str(self.bg)))
|
||||
|
||||
if attrs.color in ("", "default"):
|
||||
attrs = attrs._replace(color=parse_color(to_str(self.fg)))
|
||||
|
||||
return attrs
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return (
|
||||
"set-default-color",
|
||||
to_str(self.fg),
|
||||
to_str(self.bg),
|
||||
)
|
||||
|
||||
|
||||
class AdjustBrightnessStyleTransformation(StyleTransformation):
|
||||
"""
|
||||
Adjust the brightness to improve the rendering on either dark or light
|
||||
backgrounds.
|
||||
|
||||
For dark backgrounds, it's best to increase `min_brightness`. For light
|
||||
backgrounds it's best to decrease `max_brightness`. Usually, only one
|
||||
setting is adjusted.
|
||||
|
||||
This will only change the brightness for text that has a foreground color
|
||||
defined, but no background color. It works best for 256 or true color
|
||||
output.
|
||||
|
||||
.. note:: Notice that there is no universal way to detect whether the
|
||||
application is running in a light or dark terminal. As a
|
||||
developer of an command line application, you'll have to make
|
||||
this configurable for the user.
|
||||
|
||||
:param min_brightness: Float between 0.0 and 1.0 or a callable that returns
|
||||
a float.
|
||||
:param max_brightness: Float between 0.0 and 1.0 or a callable that returns
|
||||
a float.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, min_brightness: AnyFloat = 0.0, max_brightness: AnyFloat = 1.0
|
||||
) -> None:
|
||||
|
||||
self.min_brightness = min_brightness
|
||||
self.max_brightness = max_brightness
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
min_brightness = to_float(self.min_brightness)
|
||||
max_brightness = to_float(self.max_brightness)
|
||||
assert 0 <= min_brightness <= 1
|
||||
assert 0 <= max_brightness <= 1
|
||||
|
||||
# Don't do anything if the whole brightness range is acceptable.
|
||||
# This also avoids turning ansi colors into RGB sequences.
|
||||
if min_brightness == 0.0 and max_brightness == 1.0:
|
||||
return attrs
|
||||
|
||||
# If a foreground color is given without a background color.
|
||||
no_background = not attrs.bgcolor or attrs.bgcolor == "default"
|
||||
has_fgcolor = attrs.color and attrs.color != "ansidefault"
|
||||
|
||||
if has_fgcolor and no_background:
|
||||
# Calculate new RGB values.
|
||||
r, g, b = self._color_to_rgb(attrs.color or "")
|
||||
hue, brightness, saturation = rgb_to_hls(r, g, b)
|
||||
brightness = self._interpolate_brightness(
|
||||
brightness, min_brightness, max_brightness
|
||||
)
|
||||
r, g, b = hls_to_rgb(hue, brightness, saturation)
|
||||
new_color = "%02x%02x%02x" % (int(r * 255), int(g * 255), int(b * 255))
|
||||
|
||||
attrs = attrs._replace(color=new_color)
|
||||
|
||||
return attrs
|
||||
|
||||
def _color_to_rgb(self, color: str) -> Tuple[float, float, float]:
|
||||
"""
|
||||
Parse `style.Attrs` color into RGB tuple.
|
||||
"""
|
||||
# Do RGB lookup for ANSI colors.
|
||||
try:
|
||||
from prompt_toolkit.output.vt100 import ANSI_COLORS_TO_RGB
|
||||
|
||||
r, g, b = ANSI_COLORS_TO_RGB[color]
|
||||
return r / 255.0, g / 255.0, b / 255.0
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Parse RRGGBB format.
|
||||
return (
|
||||
int(color[0:2], 16) / 255.0,
|
||||
int(color[2:4], 16) / 255.0,
|
||||
int(color[4:6], 16) / 255.0,
|
||||
)
|
||||
|
||||
# NOTE: we don't have to support named colors here. They are already
|
||||
# transformed into RGB values in `style.parse_color`.
|
||||
|
||||
def _interpolate_brightness(
|
||||
self, value: float, min_brightness: float, max_brightness: float
|
||||
) -> float:
|
||||
"""
|
||||
Map the brightness to the (min_brightness..max_brightness) range.
|
||||
"""
|
||||
return min_brightness + (max_brightness - min_brightness) * value
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return (
|
||||
"adjust-brightness",
|
||||
to_float(self.min_brightness),
|
||||
to_float(self.max_brightness),
|
||||
)
|
||||
|
||||
|
||||
class DummyStyleTransformation(StyleTransformation):
|
||||
"""
|
||||
Don't transform anything at all.
|
||||
"""
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
return attrs
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
# Always return the same hash for these dummy instances.
|
||||
return "dummy-style-transformation"
|
||||
|
||||
|
||||
class DynamicStyleTransformation(StyleTransformation):
|
||||
"""
|
||||
StyleTransformation class that can dynamically returns any
|
||||
`StyleTransformation`.
|
||||
|
||||
:param get_style_transformation: Callable that returns a
|
||||
:class:`.StyleTransformation` instance.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, get_style_transformation: Callable[[], Optional[StyleTransformation]]
|
||||
) -> None:
|
||||
|
||||
self.get_style_transformation = get_style_transformation
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
style_transformation = (
|
||||
self.get_style_transformation() or DummyStyleTransformation()
|
||||
)
|
||||
return style_transformation.transform_attrs(attrs)
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
style_transformation = (
|
||||
self.get_style_transformation() or DummyStyleTransformation()
|
||||
)
|
||||
return style_transformation.invalidation_hash()
|
||||
|
||||
|
||||
class ConditionalStyleTransformation(StyleTransformation):
|
||||
"""
|
||||
Apply the style transformation depending on a condition.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, style_transformation: StyleTransformation, filter: FilterOrBool
|
||||
) -> None:
|
||||
|
||||
self.style_transformation = style_transformation
|
||||
self.filter = to_filter(filter)
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
if self.filter():
|
||||
return self.style_transformation.transform_attrs(attrs)
|
||||
return attrs
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return (self.filter(), self.style_transformation.invalidation_hash())
|
||||
|
||||
|
||||
class _MergedStyleTransformation(StyleTransformation):
|
||||
def __init__(self, style_transformations: Sequence[StyleTransformation]) -> None:
|
||||
self.style_transformations = style_transformations
|
||||
|
||||
def transform_attrs(self, attrs: Attrs) -> Attrs:
|
||||
for transformation in self.style_transformations:
|
||||
attrs = transformation.transform_attrs(attrs)
|
||||
return attrs
|
||||
|
||||
def invalidation_hash(self) -> Hashable:
|
||||
return tuple(t.invalidation_hash() for t in self.style_transformations)
|
||||
|
||||
|
||||
def merge_style_transformations(
|
||||
style_transformations: Sequence[StyleTransformation],
|
||||
) -> StyleTransformation:
|
||||
"""
|
||||
Merge multiple transformations together.
|
||||
"""
|
||||
return _MergedStyleTransformation(style_transformations)
|
||||
|
||||
|
||||
# Dictionary that maps ANSI color names to their opposite. This is useful for
|
||||
# turning color schemes that are optimized for a black background usable for a
|
||||
# white background.
|
||||
OPPOSITE_ANSI_COLOR_NAMES = {
|
||||
"ansidefault": "ansidefault",
|
||||
"ansiblack": "ansiwhite",
|
||||
"ansired": "ansibrightred",
|
||||
"ansigreen": "ansibrightgreen",
|
||||
"ansiyellow": "ansibrightyellow",
|
||||
"ansiblue": "ansibrightblue",
|
||||
"ansimagenta": "ansibrightmagenta",
|
||||
"ansicyan": "ansibrightcyan",
|
||||
"ansigray": "ansibrightblack",
|
||||
"ansiwhite": "ansiblack",
|
||||
"ansibrightred": "ansired",
|
||||
"ansibrightgreen": "ansigreen",
|
||||
"ansibrightyellow": "ansiyellow",
|
||||
"ansibrightblue": "ansiblue",
|
||||
"ansibrightmagenta": "ansimagenta",
|
||||
"ansibrightcyan": "ansicyan",
|
||||
"ansibrightblack": "ansigray",
|
||||
}
|
||||
assert set(OPPOSITE_ANSI_COLOR_NAMES.keys()) == set(ANSI_COLOR_NAMES)
|
||||
assert set(OPPOSITE_ANSI_COLOR_NAMES.values()) == set(ANSI_COLOR_NAMES)
|
||||
|
||||
|
||||
@memoized()
|
||||
def get_opposite_color(colorname: Optional[str]) -> Optional[str]:
|
||||
"""
|
||||
Take a color name in either 'ansi...' format or 6 digit RGB, return the
|
||||
color of opposite luminosity (same hue/saturation).
|
||||
|
||||
This is used for turning color schemes that work on a light background
|
||||
usable on a dark background.
|
||||
"""
|
||||
if colorname is None: # Because color/bgcolor can be None in `Attrs`.
|
||||
return None
|
||||
|
||||
# Special values.
|
||||
if colorname in ("", "default"):
|
||||
return colorname
|
||||
|
||||
# Try ANSI color names.
|
||||
try:
|
||||
return OPPOSITE_ANSI_COLOR_NAMES[colorname]
|
||||
except KeyError:
|
||||
# Try 6 digit RGB colors.
|
||||
r = int(colorname[:2], 16) / 255.0
|
||||
g = int(colorname[2:4], 16) / 255.0
|
||||
b = int(colorname[4:6], 16) / 255.0
|
||||
|
||||
h, l, s = rgb_to_hls(r, g, b)
|
||||
|
||||
l = 1 - l
|
||||
|
||||
r, g, b = hls_to_rgb(h, l, s)
|
||||
|
||||
r = int(r * 255)
|
||||
g = int(g * 255)
|
||||
b = int(b * 255)
|
||||
|
||||
return "%02x%02x%02x" % (r, g, b)
|
Loading…
Add table
Add a link
Reference in a new issue