|
|
from abc import ABC, abstractmethod |
|
|
from typing import List, Union |
|
|
|
|
|
from .text import Text |
|
|
|
|
|
|
|
|
def _combine_regex(*regexes: str) -> str: |
|
|
"""Combine a number of regexes in to a single regex. |
|
|
|
|
|
Returns: |
|
|
str: New regex with all regexes ORed together. |
|
|
""" |
|
|
return "|".join(regexes) |
|
|
|
|
|
|
|
|
class Highlighter(ABC): |
|
|
"""Abstract base class for highlighters.""" |
|
|
|
|
|
def __call__(self, text: Union[str, Text]) -> Text: |
|
|
"""Highlight a str or Text instance. |
|
|
|
|
|
Args: |
|
|
text (Union[str, ~Text]): Text to highlight. |
|
|
|
|
|
Raises: |
|
|
TypeError: If not called with text or str. |
|
|
|
|
|
Returns: |
|
|
Text: A test instance with highlighting applied. |
|
|
""" |
|
|
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: |
|
|
"""Apply highlighting in place to text. |
|
|
|
|
|
Args: |
|
|
text (~Text): A text object highlight. |
|
|
""" |
|
|
|
|
|
|
|
|
class NullHighlighter(Highlighter): |
|
|
"""A highlighter object that doesn't highlight. |
|
|
|
|
|
May be used to disable highlighting entirely. |
|
|
|
|
|
""" |
|
|
|
|
|
def highlight(self, text: Text) -> None: |
|
|
"""Nothing to do""" |
|
|
|
|
|
|
|
|
class RegexHighlighter(Highlighter): |
|
|
"""Applies highlighting from a list of regular expressions.""" |
|
|
|
|
|
highlights: List[str] = [] |
|
|
base_style: str = "" |
|
|
|
|
|
def highlight(self, text: Text) -> None: |
|
|
"""Highlight :class:`rich.text.Text` using regular expressions. |
|
|
|
|
|
Args: |
|
|
text (~Text): Text to highlighted. |
|
|
|
|
|
""" |
|
|
|
|
|
highlight_regex = text.highlight_regex |
|
|
for re_highlight in self.highlights: |
|
|
highlight_regex(re_highlight, style_prefix=self.base_style) |
|
|
|
|
|
|
|
|
class ReprHighlighter(RegexHighlighter): |
|
|
"""Highlights the text typically produced from ``__repr__`` methods.""" |
|
|
|
|
|
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<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>(?<!\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<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<url>(file|https|http|ws|wss):\/\/[0-9a-zA-Z\$\-\_\+\!`\(\)\,\.\?\/\;\:\&\=\%\#]*)", |
|
|
), |
|
|
] |
|
|
|
|
|
|
|
|
class JSONHighlighter(RegexHighlighter): |
|
|
"""Highlights JSON""" |
|
|
|
|
|
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]*)", |
|
|
r"(?<![\\\w])(?P<str>b?\".*?(?<!\\)\")", |
|
|
), |
|
|
r"(?<![\\\w])(?P<key>b?\".*?(?<!\\)\")\:", |
|
|
] |
|
|
|
|
|
|
|
|
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" |
|
|
) |
|
|
|