<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">import re
from abc import ABC, abstractmethod
from typing import List, Union

from .text import Span, Text


def _combine_regex(*regexes: str) -&gt; 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]) -&gt; 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) -&gt; 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) -&gt; 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) -&gt; 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&lt;tag_start&gt;&lt;)(?P&lt;tag_name&gt;[-\w.:|]*)(?P&lt;tag_contents&gt;[\w\W]*)(?P&lt;tag_end&gt;&gt;)",
        r'(?P&lt;attrib_name&gt;[\w_]{1,50})=(?P&lt;attrib_value&gt;"?[\w_]+"?)?',
        r"(?P&lt;brace&gt;[][{}()])",
        _combine_regex(
            r"(?P&lt;ipv4&gt;[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})",
            r"(?P&lt;ipv6&gt;([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})",
            r"(?P&lt;eui64&gt;(?:[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&lt;eui48&gt;(?:[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&lt;uuid&gt;[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&lt;call&gt;[\w.]*?)\(",
            r"\b(?P&lt;bool_true&gt;True)\b|\b(?P&lt;bool_false&gt;False)\b|\b(?P&lt;none&gt;None)\b",
            r"(?P&lt;ellipsis&gt;\.\.\.)",
            r"(?P&lt;number_complex&gt;(?&lt;!\w)(?:\-?[0-9]+\.?[0-9]*(?:e[-+]?\d+?)?)(?:[-+](?:[0-9]+\.?[0-9]*(?:e[-+]?\d+)?))?j)",
            r"(?P&lt;number&gt;(?&lt;!\w)\-?[0-9]+\.?[0-9]*(e[-+]?\d+?)?\b|0x[0-9a-fA-F]*)",
            r"(?P&lt;path&gt;\B(/[-\w._+]+)*\/)(?P&lt;filename&gt;[-\w._+]*)?",
            r"(?&lt;![\\\w])(?P&lt;str&gt;b?'''.*?(?&lt;!\\)'''|b?'.*?(?&lt;!\\)'|b?\"\"\".*?(?&lt;!\\)\"\"\"|b?\".*?(?&lt;!\\)\")",
            r"(?P&lt;url&gt;(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&amp;=%#]*)",
        ),
    ]


class JSONHighlighter(RegexHighlighter):
    """Highlights JSON"""

    # Captures the start and end of JSON strings, handling escaped quotes
    JSON_STR = r"(?&lt;![\\\w])(?P&lt;str&gt;b?\".*?(?&lt;!\\)\")"
    JSON_WHITESPACE = {" ", "\n", "\r", "\t"}

    base_style = "json."
    highlights = [
        _combine_regex(
            r"(?P&lt;brace&gt;[\{\[\(\)\]\}])",
            r"\b(?P&lt;bool_true&gt;true)\b|\b(?P&lt;bool_false&gt;false)\b|\b(?P&lt;null&gt;null)\b",
            r"(?P&lt;number&gt;(?&lt;!\w)\-?[0-9]+\.?[0-9]*(e[\-\+]?\d+?)?\b|0x[0-9a-fA-F]*)",
            JSON_STR,
        ),
    ]

    def highlight(self, text: Text) -&gt; None:
        super().highlight(text)

        # Additional work to handle highlighting JSON keys
        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 &lt; len(plain):
                char = plain[cursor]
                cursor += 1
                if char == ":":
                    append(Span(start, end, "json.key"))
                elif char in whitespace:
                    continue
                break


class ISO8601Highlighter(RegexHighlighter):
    """Highlights the ISO8601 date time strings.
    Regex reference: https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s07.html
    """

    base_style = "iso8601."
    highlights = [
        #
        # Dates
        #
        # Calendar month (e.g. 2008-08). The hyphen is required
        r"^(?P&lt;year&gt;[0-9]{4})-(?P&lt;month&gt;1[0-2]|0[1-9])$",
        # Calendar date w/o hyphens (e.g. 20080830)
        r"^(?P&lt;date&gt;(?P&lt;year&gt;[0-9]{4})(?P&lt;month&gt;1[0-2]|0[1-9])(?P&lt;day&gt;3[01]|0[1-9]|[12][0-9]))$",
        # Ordinal date (e.g. 2008-243). The hyphen is optional
        r"^(?P&lt;date&gt;(?P&lt;year&gt;[0-9]{4})-?(?P&lt;day&gt;36[0-6]|3[0-5][0-9]|[12][0-9]{2}|0[1-9][0-9]|00[1-9]))$",
        #
        # Weeks
        #
        # Week of the year (e.g., 2008-W35). The hyphen is optional
        r"^(?P&lt;date&gt;(?P&lt;year&gt;[0-9]{4})-?W(?P&lt;week&gt;5[0-3]|[1-4][0-9]|0[1-9]))$",
        # Week date (e.g., 2008-W35-6). The hyphens are optional
        r"^(?P&lt;date&gt;(?P&lt;year&gt;[0-9]{4})-?W(?P&lt;week&gt;5[0-3]|[1-4][0-9]|0[1-9])-?(?P&lt;day&gt;[1-7]))$",
        #
        # Times
        #
        # Hours and minutes (e.g., 17:21). The colon is optional
        r"^(?P&lt;time&gt;(?P&lt;hour&gt;2[0-3]|[01][0-9]):?(?P&lt;minute&gt;[0-5][0-9]))$",
        # Hours, minutes, and seconds w/o colons (e.g., 172159)
        r"^(?P&lt;time&gt;(?P&lt;hour&gt;2[0-3]|[01][0-9])(?P&lt;minute&gt;[0-5][0-9])(?P&lt;second&gt;[0-5][0-9]))$",
        # Time zone designator (e.g., Z, +07 or +07:00). The colons and the minutes are optional
        r"^(?P&lt;timezone&gt;(Z|[+-](?:2[0-3]|[01][0-9])(?::?(?:[0-5][0-9]))?))$",
        # Hours, minutes, and seconds with time zone designator (e.g., 17:21:59+07:00).
        # All the colons are optional. The minutes in the time zone designator are also optional
        r"^(?P&lt;time&gt;(?P&lt;hour&gt;2[0-3]|[01][0-9])(?P&lt;minute&gt;[0-5][0-9])(?P&lt;second&gt;[0-5][0-9]))(?P&lt;timezone&gt;Z|[+-](?:2[0-3]|[01][0-9])(?::?(?:[0-5][0-9]))?)$",
        #
        # Date and Time
        #
        # Calendar date with hours, minutes, and seconds (e.g., 2008-08-30 17:21:59 or 20080830 172159).
        # A space is required between the date and the time. The hyphens and colons are optional.
        # This regex matches dates and times that specify some hyphens or colons but omit others.
        # This does not follow ISO 8601
        r"^(?P&lt;date&gt;(?P&lt;year&gt;[0-9]{4})(?P&lt;hyphen&gt;-)?(?P&lt;month&gt;1[0-2]|0[1-9])(?(hyphen)-)(?P&lt;day&gt;3[01]|0[1-9]|[12][0-9])) (?P&lt;time&gt;(?P&lt;hour&gt;2[0-3]|[01][0-9])(?(hyphen):)(?P&lt;minute&gt;[0-5][0-9])(?(hyphen):)(?P&lt;second&gt;[0-5][0-9]))$",
        #
        # XML Schema dates and times
        #
        # Date, with optional time zone (e.g., 2008-08-30 or 2008-08-30+07:00).
        # Hyphens are required. This is the XML Schema 'date' type
        r"^(?P&lt;date&gt;(?P&lt;year&gt;-?(?:[1-9][0-9]*)?[0-9]{4})-(?P&lt;month&gt;1[0-2]|0[1-9])-(?P&lt;day&gt;3[01]|0[1-9]|[12][0-9]))(?P&lt;timezone&gt;Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
        # Time, with optional fractional seconds and time zone (e.g., 01:45:36 or 01:45:36.123+07:00).
        # There is no limit on the number of digits for the fractional seconds. This is the XML Schema 'time' type
        r"^(?P&lt;time&gt;(?P&lt;hour&gt;2[0-3]|[01][0-9]):(?P&lt;minute&gt;[0-5][0-9]):(?P&lt;second&gt;[0-5][0-9])(?P&lt;frac&gt;\.[0-9]+)?)(?P&lt;timezone&gt;Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
        # Date and time, with optional fractional seconds and time zone (e.g., 2008-08-30T01:45:36 or 2008-08-30T01:45:36.123Z).
        # This is the XML Schema 'dateTime' type
        r"^(?P&lt;date&gt;(?P&lt;year&gt;-?(?:[1-9][0-9]*)?[0-9]{4})-(?P&lt;month&gt;1[0-2]|0[1-9])-(?P&lt;day&gt;3[01]|0[1-9]|[12][0-9]))T(?P&lt;time&gt;(?P&lt;hour&gt;2[0-3]|[01][0-9]):(?P&lt;minute&gt;[0-5][0-9]):(?P&lt;second&gt;[0-5][0-9])(?P&lt;ms&gt;\.[0-9]+)?)(?P&lt;timezone&gt;Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$",
    ]


if __name__ == "__main__":  # pragma: no cover
    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&amp;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)
</pre></body></html>