mirror of
https://github.com/Textualize/rich.git
synced 2025-08-04 01:58:24 +00:00
markup syntax
This commit is contained in:
parent
1f09278776
commit
7da3ee6299
4 changed files with 43 additions and 19 deletions
|
@ -38,17 +38,14 @@ If your terminal software supports hyperlinks, you will be able to click the wor
|
|||
Escaping
|
||||
~~~~~~~~
|
||||
|
||||
Occasionally you may want to print something that Rich would interpret as markup. You can *escape* square brackets by doubling them up. Here's an example::
|
||||
Occasionally you may want to print something that Rich would interpret as markup. You can *escape* a tab by preceding it with backslash. Here's an example::
|
||||
|
||||
>>> from rich import print
|
||||
>>> print("foo[[bar]]")
|
||||
>>> print("foo\[bar]")
|
||||
foo[bar]
|
||||
|
||||
The function :func:`~rich.markup.escape` will handle escape of text for you.
|
||||
|
||||
.. warning::
|
||||
Be careful when using f-strings with console markup. You will need to escape any variables if they could contain square brackets.
|
||||
|
||||
Rendering Markup
|
||||
----------------
|
||||
|
||||
|
|
|
@ -116,6 +116,10 @@ You can also use these custom styles via markup. For example::
|
|||
|
||||
console.print("[warning]The pod bay doors are locked[/warning]")
|
||||
|
||||
|
||||
.. note::
|
||||
style names must be lower case, start with a letter, and only contain letters or the characters ``"."``, ``"-"``, ``"_"``.
|
||||
|
||||
If you prefer you can write your styles in an external config file rather than in Python. Here's an example of the format::
|
||||
|
||||
[styles]
|
||||
|
|
|
@ -7,7 +7,13 @@ from .text import Span, Text
|
|||
from ._emoji_replace import _emoji_replace
|
||||
|
||||
|
||||
re_tags = re.compile(r"(\[\[)|(\]\])|\[([a-zA-Z\-_#\/].*?)\]")
|
||||
RE_TAGS = re.compile(
|
||||
r"""
|
||||
(\\\[)|
|
||||
\[([a-z#\/].*?)\]
|
||||
""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
class Tag(NamedTuple):
|
||||
|
@ -41,7 +47,7 @@ def escape(markup: str) -> str:
|
|||
Returns:
|
||||
str: Markup with square brackets escaped.
|
||||
"""
|
||||
return markup.replace("[", "[[").replace("]", "]]")
|
||||
return markup.replace("[", "\[")
|
||||
|
||||
|
||||
def _parse(markup: str) -> Iterable[Tuple[int, Optional[str], Optional[Tag]]]:
|
||||
|
@ -52,19 +58,19 @@ def _parse(markup: str) -> Iterable[Tuple[int, Optional[str], Optional[Tag]]]:
|
|||
|
||||
"""
|
||||
position = 0
|
||||
for match in re_tags.finditer(markup):
|
||||
escape_open, escape_close, tag_text = match.groups()
|
||||
for match in RE_TAGS.finditer(markup):
|
||||
(escape_open, tag_text) = match.groups()
|
||||
start, end = match.span()
|
||||
if start > position:
|
||||
yield start, markup[position:start], None
|
||||
if tag_text:
|
||||
if escape_open:
|
||||
yield start, "[", None
|
||||
else:
|
||||
text, equals, parameters = tag_text.partition("=")
|
||||
if equals:
|
||||
yield start, None, Tag(text, parameters)
|
||||
else:
|
||||
yield start, None, Tag(tag_text.strip(), None)
|
||||
else:
|
||||
yield start, (escape_open and "[") or (escape_close and "]"), None # type: ignore
|
||||
position = end
|
||||
if position < len(markup):
|
||||
yield position, markup[position:], None
|
||||
|
|
|
@ -1,15 +1,33 @@
|
|||
import pytest
|
||||
|
||||
from rich.markup import escape, MarkupError, _parse, render, Tag
|
||||
from rich.markup import escape, MarkupError, _parse, render, Tag, RE_TAGS
|
||||
from rich.text import Span
|
||||
|
||||
|
||||
def test_re_no_match():
|
||||
assert RE_TAGS.match("[True]") == None
|
||||
assert RE_TAGS.match("[False]") == None
|
||||
assert RE_TAGS.match("[None]") == None
|
||||
assert RE_TAGS.match("[1]") == None
|
||||
assert RE_TAGS.match("[2]") == None
|
||||
assert RE_TAGS.match("[]") == None
|
||||
|
||||
|
||||
def test_re_match():
|
||||
assert RE_TAGS.match("[true]")
|
||||
assert RE_TAGS.match("[false]")
|
||||
assert RE_TAGS.match("[none]")
|
||||
assert RE_TAGS.match("[color(1)]")
|
||||
assert RE_TAGS.match("[#ff00ff]")
|
||||
assert RE_TAGS.match("[/]")
|
||||
|
||||
|
||||
def test_escape():
|
||||
assert escape("foo[bar]") == "foo[[bar]]"
|
||||
assert escape("foo[bar]") == "foo\[bar]"
|
||||
|
||||
|
||||
def test_parse():
|
||||
result = list(_parse("[foo]hello[/foo][bar]world[/][[escaped]]"))
|
||||
result = list(_parse("[foo]hello[/foo][bar]world[/]\[escaped]"))
|
||||
expected = [
|
||||
(0, None, Tag(name="foo", parameters=None)),
|
||||
(10, "hello", None),
|
||||
|
@ -18,8 +36,7 @@ def test_parse():
|
|||
(26, "world", None),
|
||||
(26, None, Tag(name="/", parameters=None)),
|
||||
(29, "[", None),
|
||||
(38, "escaped", None),
|
||||
(38, "]", None),
|
||||
(31, "escaped]", None),
|
||||
]
|
||||
assert result == expected
|
||||
|
||||
|
@ -41,8 +58,8 @@ def test_render():
|
|||
|
||||
|
||||
def test_render_not_tags():
|
||||
result = render('[1], [1,2,3,4], ["hello"]')
|
||||
assert str(result) == '[1], [1,2,3,4], ["hello"]'
|
||||
result = render('[[1], [1,2,3,4], ["hello"], [None], [False], [True]]')
|
||||
assert str(result) == '[[1], [1,2,3,4], ["hello"], [None], [False], [True]]'
|
||||
assert result.spans == []
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue