Skip to content

Commit 635b6f6

Browse files
committed
Highlighters: Refactor tim.prettify_markup as a highlighter class
This makes a bit more sense semantically, and the implementation is a lot cleaner & more robust now.
1 parent bd310f9 commit 635b6f6

File tree

8 files changed

+92
-29
lines changed

8 files changed

+92
-29
lines changed

pytermgui/cmd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ def _create_table(source: Iterable[tuple[str, str]]) -> ptg.Container:
326326

327327
def on_exit(self) -> None:
328328
super().on_exit()
329-
print(ptg.tim.prettify_markup(self._input.value))
329+
ptg.tim.print(ptg.highlight_tim(self._input.value))
330330
ptg.tim.print(self._input.value)
331331

332332

pytermgui/colors.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@
1616
from dataclasses import dataclass, field
1717
from functools import cached_property, lru_cache
1818
from math import sqrt # pylint: disable=no-name-in-module
19-
from typing import Generator, Literal, Type
19+
from typing import TYPE_CHECKING, Generator, Literal, Type
2020

2121
from .ansi_interface import reset as reset_style
2222
from .color_info import COLOR_TABLE, CSS_COLORS
2323
from .exceptions import ColorSyntaxError
24-
from .fancy_repr import FancyYield
2524
from .input import getch
2625
from .terminal import ColorSystem, terminal
2726

27+
if TYPE_CHECKING:
28+
from .fancy_repr import FancyYield
29+
2830
__all__ = [
2931
"COLOR_TABLE",
3032
"XTERM_NAMED_COLORS",

pytermgui/highlighters.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
import keyword
77
import re
88
from dataclasses import dataclass, field
9+
from functools import lru_cache
910
from typing import TYPE_CHECKING, Callable, Generator, Match, Pattern, Protocol
1011

12+
from .markup import Token, consume_tag
1113
from .regex import RE_MARKUP
1214

1315
if TYPE_CHECKING:
@@ -16,6 +18,7 @@
1618
__all__ = [
1719
"Highlighter",
1820
"RegexHighlighter",
21+
"highlight_tim",
1922
"highlight_python",
2023
]
2124

@@ -137,7 +140,7 @@ def _insert_style(matchobj: Match) -> str:
137140
return text
138141

139142
def __fancy_repr__(self) -> Generator[FancyYield, None, None]:
140-
"""Yields some fancy looking repl text."""
143+
"""Yields some fancy looking repr text."""
141144

142145
preview = self("highlight_python()") + "\x1b[0m"
143146
pattern = self._pattern.pattern
@@ -151,6 +154,72 @@ def __fancy_repr__(self) -> Generator[FancyYield, None, None]:
151154
yield ">"
152155

153156

157+
def highlight_tim(text: str, cache: bool = True) -> str:
158+
"""Highlights some TIM code."""
159+
160+
@lru_cache(1048)
161+
def _highlight(txt: str) -> str:
162+
output = ""
163+
cursor = 0
164+
active_tokens: list[Token] = []
165+
166+
def _get_active_markup() -> str:
167+
active_markup = " ".join(tkn.markup for tkn in active_tokens)
168+
169+
if active_markup == "":
170+
return ""
171+
172+
return f"[{active_markup}]"
173+
174+
for matchobj in RE_MARKUP.finditer(txt):
175+
start, end = matchobj.span()
176+
177+
if cursor < start:
178+
if cursor > 0:
179+
output += "]"
180+
181+
output += _get_active_markup()
182+
output += f"{txt[cursor:start]}[/]"
183+
184+
*_, tags = matchobj.groups()
185+
186+
output += "["
187+
for tag in tags.split():
188+
token = consume_tag(tag)
189+
output += f"{token.prettified_markup} "
190+
191+
if Token.is_clear(token):
192+
active_tokens = [
193+
tkn for tkn in active_tokens if not token.targets(tkn)
194+
]
195+
196+
else:
197+
active_tokens.append(token)
198+
199+
output = output.rstrip()
200+
cursor = end
201+
202+
if cursor < len(txt) - 1:
203+
if cursor > 0:
204+
output += "]"
205+
206+
output += _get_active_markup()
207+
output += f"{txt[cursor:]}"
208+
209+
if len(active_tokens) > 0:
210+
output += "[/]"
211+
212+
if output.count("[") != output.count("]"):
213+
output += "]"
214+
215+
return output
216+
217+
if cache:
218+
return _highlight(text)
219+
220+
return _highlight.__wrapped__(text)
221+
222+
154223
_BUILTIN_NAMES = "|".join(f"(?:{item})" for item in dir(builtins))
155224
_KEYWORD_NAMES = "|".join(
156225
f"(?:{keyw})" for keyw in list(keyword.kwlist) + ["builtin", "function", "module"]

pytermgui/markup/language.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -233,24 +233,6 @@ def group_styles(
233233
text, tokenizer=tokenizer, context=self.context
234234
)
235235

236-
def prettify_markup(self, markup: str) -> str:
237-
"""Syntax-highlight markup."""
238-
239-
output = ""
240-
241-
for span in StyledText.group_styles(
242-
markup, tokenizer=tokenize_markup, context=self.context
243-
):
244-
tags = " ".join(token.prettified_markup for token in span.tokens[:-1])
245-
markup = " ".join(tkn.markup for tkn in span.tokens[:-1])
246-
247-
if len(tags) > 0 and len(markup) > 0:
248-
output += f"[{tags}][{markup}]"
249-
250-
output += f"{span.plain}[/]"
251-
252-
return self.parse(output, optimize=True)
253-
254236
def print(self, *args, **kwargs) -> None:
255237
"""Parse all arguments and pass them through to print, along with kwargs."""
256238

pytermgui/markup/tokens.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88

99
from dataclasses import dataclass
1010
from functools import cached_property
11-
from typing import Any, Generator, Iterator
11+
from typing import TYPE_CHECKING, Any, Generator, Iterator
1212

1313
from typing_extensions import TypeGuard
1414

1515
from ..colors import Color
16-
from ..fancy_repr import FancyYield
16+
17+
if TYPE_CHECKING:
18+
from ..fancy_repr import FancyYield
1719

1820
__all__ = [
1921
"Token",

pytermgui/prettifiers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from typing import Any
1111

1212
from .fancy_repr import build_fancy_repr, supports_fancy_repr
13-
from .highlighters import highlight_python
13+
from .highlighters import highlight_python, highlight_tim
1414
from .markup import tim
1515
from .regex import RE_MARKUP
1616

@@ -61,8 +61,10 @@ def prettify( # pylint: disable=too-many-branches
6161

6262
if isinstance(target, str):
6363
if RE_MARKUP.match(target) is not None:
64+
target = highlight_tim(target)
65+
6466
if parse:
65-
return f'"{tim.prettify_markup(target)}"'
67+
return f'"{tim.parse(target)}"'
6668

6769
return target + "[/]"
6870

pytermgui/pretty.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
```
99
"""
1010

11+
# isort: skip_file
12+
1113
from __future__ import annotations
1214

1315
import builtins
@@ -24,7 +26,9 @@
2426
# IPython runtime, so if running outside of that context a NameError
2527
# is raised.
2628
IPYTHON = get_ipython() # type: ignore
27-
from IPython.core.formatters import BaseFormatter # pylint: disable=import-error
29+
from IPython.core.formatters import ( # type: ignore # pylint: disable=import-error
30+
BaseFormatter,
31+
)
2832

2933
except NameError:
3034
IPYTHON = None

pytermgui/terminal.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
from enum import Enum
1515
from functools import cached_property
1616
from shutil import get_terminal_size
17-
from typing import Any, Callable, Generator, TextIO
17+
from typing import TYPE_CHECKING, Any, Callable, Generator, TextIO
1818

19-
from .fancy_repr import FancyYield
2019
from .input import getch_timeout
2120
from .regex import RE_PIXEL_SIZE, has_open_sequence, real_length, strip_ansi
2221

22+
if TYPE_CHECKING:
23+
from .fancy_repr import FancyYield
24+
2325
__all__ = [
2426
"terminal",
2527
"set_global_terminal",

0 commit comments

Comments
 (0)