windows color support

This commit is contained in:
Will McGugan 2020-02-02 14:13:31 +00:00
parent 37c511f017
commit df88f78eab
4 changed files with 80 additions and 12 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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