import re
from abc import ABC, abstractmethod
from typing import List, Union
from .text import Span, Text
def _combine_regex(*regexes: str) -> str:
return "|".join(regexes)
class Highlighter(ABC):
def __call__(self, text: Union[str, Text]) -> Text:
if isinstance(text, str):
highlight_text = Text(text)
elif isinstance(text, Text):
highlight_text = text.copy()
else:
raise TypeError(f"str or Text instance required, not {text!r}")
self.highlight(highlight_text)
return highlight_text
@abstractmethod
def highlight(self, text: Text) -> None:
class NullHighlighter(Highlighter):
def highlight(self, text: Text) -> None:
class RegexHighlighter(Highlighter):
highlights: List[str] = []
base_style: str = ""
def highlight(self, text: Text) -> None:
highlight_regex = text.highlight_regex
for re_highlight in self.highlights:
highlight_regex(re_highlight, style_prefix=self.base_style)
class ReprHighlighter(RegexHighlighter):
base_style = "repr."
highlights = [
r"(?P<tag_start><)(?P<tag_name>[-\w.:|]*)(?P<tag_contents>[\w\W]*)(?P<tag_end>>)",
r'(?P<attrib_name>[\w_]{1,50})=(?P<attrib_value>"?[\w_]+"?)?',
r"(?P<brace>[][{}()])",
_combine_regex(
r"(?P<ipv4>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})",
r"(?P<ipv6>([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})",
r"(?P<eui64>(?:[0-9A-Fa-f]{1,2}-){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){3}[0-9A-Fa-f]{4})",
r"(?P<eui48>(?:[0-9A-Fa-f]{1,2}-){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})",
r"(?P<uuid>[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})",
r"(?P<call>[\w.]*?)\(",
r"\b(?P<bool_true>True)\b|\b(?P<bool_false>False)\b|\b(?P<none>None)\b",
r"(?P<ellipsis>\.\.\.)",
r"(?P<number_complex>(?<!\w)(?:\-?[0-9]+\.?[0-9]*(?:e[-+]?\d+?)?)(?:[-+](?:[0-9]+\.?[0-9]*(?:e[-+]?\d+)?))?j)",
r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[-+]?\d+?)?\b|0x[0-9a-fA-F]*)",
r"(?P<path>\B(/[-\w._+]+)*\/)(?P<filename>[-\w._+]*)?",
r"(?<![\\\w])(?P<str>b?'''.*?(?<!\\)'''|b?'.*?(?<!\\)'|b?\"\"\".*?(?<!\\)\"\"\"|b?\".*?(?<!\\)\")",
r"(?P<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#]*)",
),
]
class JSONHighlighter(RegexHighlighter):
JSON_STR = r"(?<![\\\w])(?P<str>b?\".*?(?<!\\)\")"
JSON_WHITESPACE = {" ", "\n", "\r", "\t"}
base_style = "json."
highlights = [
_combine_regex(
r"(?P<brace>[\{\[\(\)\]\}])",
r"\b(?P<bool_true>true)\b|\b(?P<bool_false>false)\b|\b(?P<null>null)\b",
r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[\-\+]?\d+?)?\b|0x[0-9a-fA-F]*)",
JSON_STR,
),
]
def highlight(self, text: Text) -> None:
super().highlight(text)
plain = text.plain
append = text.spans.append
whitespace = self.JSON_WHITESPACE
for match in re.finditer(self.JSON_STR, plain):
start, end = match.span()
cursor = end
while cursor < len(plain):
char = plain[cursor]
cursor += 1
if char == ":":
append(Span(start, end, "json.key"))
elif char in whitespace:
continue
break
class ISO8601Highlighter(RegexHighlighter):
base_style = "iso8601."
highlights = [
r"^(?P<year>[0-9]{4})-(?P<month>1[0-2]|0[1-9])$",
r"^(?P<date>(?P<year>[0-9]{4})(?P<month>1[0-2]|0[1-9])(?P<day>3[01]|0[1-9]|[12][0-9]))$",
r"^(?P<date>(?P<year>[0-9]{4})-?(?P<day>36[0-6]|3[0-5][0-9]|[12][0-9]{2}|0[1-9][0-9]|00[1-9]))$",
r"^(?P<date>(?P<year>[0-9]{4})-?W(?P<week>5[0-3]|[1-4][0-9]|0[1-9]))$",
r"^(?P<date>(?P<year>[0-9]{4})-?W(?P<week>5[0-3]|[1-4][0-9]|0[1-9])-?(?P<day>[1-7]))$",
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9]):?(?P<minute>[0-5][0-9]))$",
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9]))$",
r"^(?P<timezone>(Z|[+-](?:2[0-3]|[01][0-9])(?::?(?:[0-5][0-9]))?))$",
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9]))(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9])(?::?(?:[0-5][0-9]))?)$",
r"^(?P<date>(?P<year>[0-9]{4})(?P<hyphen>-)?(?P<month>1[0-2]|0[1-9])(?(hyphen)-)(?P<day>3[01]|0[1-9]|[12][0-9])) (?P<time>(?P<hour>2[0-3]|[01][0-9])(?(hyphen):)(?P<minute>[0-5][0-9])(?(hyphen):)(?P<second>[0-5][0-9]))$",
r"^(?P<date>(?P<year>-?(?:[1-9][0-9]*)?[0-9]{4})-(?P<month>1[0-2]|0[1-9])-(?P<day>3[01]|0[1-9]|[12][0-9]))(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
r"^(?P<time>(?P<hour>2[0-3]|[01][0-9]):(?P<minute>[0-5][0-9]):(?P<second>[0-5][0-9])(?P<frac>\.[0-9]+)?)(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
r"^(?P<date>(?P<year>-?(?:[1-9][0-9]*)?[0-9]{4})-(?P<month>1[0-2]|0[1-9])-(?P<day>3[01]|0[1-9]|[12][0-9]))T(?P<time>(?P<hour>2[0-3]|[01][0-9]):(?P<minute>[0-5][0-9]):(?P<second>[0-5][0-9])(?P<ms>\.[0-9]+)?)(?P<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
]
if __name__ == "__main__": from .console import Console
console = Console()
console.print("[bold green]hello world![/bold green]")
console.print("'[bold green]hello world![/bold green]'")
console.print(" /foo")
console.print("/foo/")
console.print("/foo/bar")
console.print("foo/bar/baz")
console.print("/foo/bar/baz?foo=bar+egg&egg=baz")
console.print("/foo/bar/baz/")
console.print("/foo/bar/baz/egg")
console.print("/foo/bar/baz/egg.py")
console.print("/foo/bar/baz/egg.py word")
console.print(" /foo/bar/baz/egg.py word")
console.print("foo /foo/bar/baz/egg.py word")
console.print("foo /foo/bar/ba._++z/egg+.py word")
console.print("https://example.org?foo=bar#header")
console.print(1234567.34)
console.print(1 / 2)
console.print(-1 / 123123123123)
console.print(
"127.0.1.1 bar 192.168.1.4 2001:0db8:85a3:0000:0000:8a2e:0370:7334 foo"
)
import json
console.print_json(json.dumps(obj={"name": "apple", "count": 1}), indent=None)