mirror of https://github.com/Textualize/rich.git
windows color support
This commit is contained in:
parent
37c511f017
commit
df88f78eab
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.3.3] - Unreleased
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Windows color support
|
||||
|
||||
## [0.3.2] - 2020-01-26
|
||||
|
||||
### Added
|
||||
|
|
|
@ -4,6 +4,7 @@ from dataclasses import dataclass
|
|||
from enum import IntEnum
|
||||
from functools import lru_cache
|
||||
from math import sqrt
|
||||
import platform
|
||||
from typing import Iterable, List, NamedTuple, Optional, Sequence, Tuple, TYPE_CHECKING
|
||||
|
||||
from ._palettes import STANDARD_PALETTE, EIGHT_BIT_PALETTE
|
||||
|
@ -13,12 +14,16 @@ if TYPE_CHECKING: # pragma: no cover
|
|||
from .theme import Theme
|
||||
|
||||
|
||||
WINDOWS = platform.system() == "Windows"
|
||||
|
||||
|
||||
class ColorSystem(IntEnum):
|
||||
"""One of the 3 color system supported by terminals."""
|
||||
|
||||
STANDARD = 1
|
||||
EIGHT_BIT = 2
|
||||
TRUECOLOR = 3
|
||||
WINDOWS = 4
|
||||
|
||||
|
||||
class ColorType(IntEnum):
|
||||
|
@ -28,6 +33,7 @@ class ColorType(IntEnum):
|
|||
STANDARD = 1
|
||||
EIGHT_BIT = 2
|
||||
TRUECOLOR = 3
|
||||
WINDOWS = 4
|
||||
|
||||
|
||||
ANSI_COLOR_NAMES = {
|
||||
|
@ -291,6 +297,8 @@ class Color(NamedTuple):
|
|||
elif self.type == ColorType.STANDARD:
|
||||
assert self.number is not None
|
||||
return theme.ansi_colors[self.number]
|
||||
elif self.type == ColorType.WINDOWS:
|
||||
return STANDARD_PALETTE[self.number]
|
||||
else: # self.type == ColorType.DEFAULT:
|
||||
assert self.number is None
|
||||
return theme.foreground_color if foreground else theme.background_color
|
||||
|
@ -371,7 +379,7 @@ class Color(NamedTuple):
|
|||
if _type == ColorType.DEFAULT:
|
||||
return ["39" if foreground else "49"]
|
||||
|
||||
elif _type == ColorType.STANDARD:
|
||||
elif _type in (ColorType.STANDARD, ColorType.WINDOWS):
|
||||
number = self.number
|
||||
assert number is not None
|
||||
return [str(30 + number if foreground else 40 + number)]
|
||||
|
@ -423,6 +431,21 @@ class Color(NamedTuple):
|
|||
color_number = STANDARD_PALETTE.match(triplet)
|
||||
return Color(self.name, ColorType.STANDARD, number=color_number)
|
||||
|
||||
elif system == ColorSystem.WINDOWS:
|
||||
if self.system == ColorSystem.TRUECOLOR:
|
||||
assert self.triplet is not None
|
||||
triplet = self.triplet
|
||||
else: # self.system == ColorSystem.EIGHT_BIT
|
||||
assert self.number is not None
|
||||
if self.number < 8:
|
||||
return Color(self.name, ColorType.WINDOWS, number=self.number)
|
||||
elif self.number < 16:
|
||||
return Color(self.name, ColorType.WINDOWS, number=self.number - 8)
|
||||
triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number])
|
||||
|
||||
color_number = STANDARD_PALETTE.match(triplet)
|
||||
return Color(self.name, ColorType.WINDOWS, number=color_number)
|
||||
|
||||
return self
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import inspect
|
|||
from itertools import chain
|
||||
import os
|
||||
from operator import itemgetter
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
@ -48,8 +49,9 @@ from .segment import Segment
|
|||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .text import Text
|
||||
|
||||
HighlighterType = Callable[[Union[str, "Text"]], "Text"]
|
||||
WINDOWS = platform.system() == "Windows"
|
||||
|
||||
HighlighterType = Callable[[Union[str, "Text"]], "Text"]
|
||||
JustifyValues = Optional[Literal["left", "center", "right", "full"]]
|
||||
|
||||
|
||||
|
@ -151,6 +153,7 @@ COLOR_SYSTEMS = {
|
|||
"standard": ColorSystem.STANDARD,
|
||||
"256": ColorSystem.EIGHT_BIT,
|
||||
"truecolor": ColorSystem.TRUECOLOR,
|
||||
"windows": ColorSystem.WINDOWS,
|
||||
}
|
||||
|
||||
|
||||
|
@ -179,7 +182,7 @@ class Console:
|
|||
def __init__(
|
||||
self,
|
||||
color_system: Optional[
|
||||
Literal["auto", "standard", "256", "truecolor"]
|
||||
Literal["auto", "standard", "256", "truecolor", "windows"]
|
||||
] = "auto",
|
||||
styles: Dict[str, Style] = None,
|
||||
file: IO = None,
|
||||
|
@ -226,6 +229,8 @@ class Console:
|
|||
"""Detect color system from env vars."""
|
||||
if not self.is_terminal:
|
||||
return None
|
||||
if WINDOWS:
|
||||
return ColorSystem.WINDOWS
|
||||
if os.environ.get("COLORTERM", "").strip().lower() == "truecolor":
|
||||
return ColorSystem.TRUECOLOR
|
||||
# 256 can be considered standard nowadays
|
||||
|
@ -311,6 +316,8 @@ class Console:
|
|||
return ConsoleDimensions(self._width, self._height)
|
||||
|
||||
width, height = shutil.get_terminal_size()
|
||||
if WINDOWS:
|
||||
width -= 1
|
||||
return ConsoleDimensions(
|
||||
width if self._width is None else self._width,
|
||||
height if self._height is None else self._height,
|
||||
|
@ -796,7 +803,7 @@ class Console:
|
|||
|
||||
"""
|
||||
text = self.export_text(clear=clear, styles=styles)
|
||||
with open(path, "wt") as write_file:
|
||||
with open(path, "wt", encoding="utf-8") as write_file:
|
||||
write_file.write(text)
|
||||
|
||||
def export_html(
|
||||
|
@ -899,7 +906,7 @@ class Console:
|
|||
code_format=code_format,
|
||||
inline_styles=inline_styles,
|
||||
)
|
||||
with open(path, "wt") as write_file:
|
||||
with open(path, "wt", encoding="utf-8") as write_file:
|
||||
write_file.write(html)
|
||||
|
||||
|
||||
|
|
|
@ -368,7 +368,7 @@ class Markdown:
|
|||
inlines = self.inlines
|
||||
new_line = False
|
||||
for current, entering in nodes:
|
||||
print(current, current.literal)
|
||||
# print(current, current.literal)
|
||||
node_type = current.t
|
||||
if node_type in ("html_inline", "html_block", "text"):
|
||||
context.on_text(current.literal)
|
||||
|
@ -433,12 +433,44 @@ if __name__ == "__main__": # pragma: no cover
|
|||
markup = """
|
||||
An h1 header
|
||||
============
|
||||
|
||||
| Syntax | Description |
|
||||
| --- | ----------- |
|
||||
| Header | Title |
|
||||
| Paragraph | Text |
|
||||
|
||||
Paragraphs are separated by a blank line.
|
||||
2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists look like:
|
||||
* this one
|
||||
* that one
|
||||
* the other one
|
||||
Note that --- not considering the asterisk --- the actual text content starts at 4-columns in.
|
||||
> Block quotes are
|
||||
> written like so.
|
||||
>
|
||||
> They can span multiple paragraphs,
|
||||
> if you like.
|
||||
Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all in chapters 12--14"). Three dots ... will be converted to an ellipsis. Unicode is supported. ☺
|
||||
An h2 header
|
||||
------------
|
||||
```python
|
||||
@classmethod
|
||||
def adjust_line_length(
|
||||
cls, line: List[Segment], length: int, style: Style = None
|
||||
) -> List[Segment]:
|
||||
line_length = sum(len(text) for text, _style in line)
|
||||
if line_length < length:
|
||||
return line[:] + [Segment(" " * (length - line_length), style)]
|
||||
elif line_length > length:
|
||||
line_length = 0
|
||||
new_line: List[Segment] = []
|
||||
append = new_line.append
|
||||
for segment in line:
|
||||
segment_length = len(segment.text)
|
||||
if line_length + segment_length < length:
|
||||
append(segment)
|
||||
line_length += segment_length
|
||||
else:
|
||||
text, style = segment
|
||||
append(Segment(text[: length - line_length], style))
|
||||
break
|
||||
return new_line
|
||||
return line
|
||||
```
|
||||
"""
|
||||
|
||||
import commonmark
|
||||
|
|
Loading…
Reference in New Issue