Repository: Textualize/rich

Issue:
console markup escaping issue
### Discussed in https://github.com/Textualize/rich/discussions/1939

<div type='discussions-op-text'>

<sup>Originally posted by **codenotworking** February  9, 2022</sup>
My code:
```python
from rich import print as rprint

rprint("[dim white]\[url=[/]")
```

I'm trying to print simply `[url=` but I end up getting `[url=[/]`.  What am I doing wrong?</div>


Discussion:
@codenotworking You may have discovered an edge case with the escaping.

In the meantime, you could do this:

```python
rprint("[dim white]\[url=")
```

More generally, you could work with a Text object which will give you more fine control.
@darrenburns Can you look at why this doesn't work. I would have expected that to render `[url=`
> In the meantime, you could do this:
> 
> ```python
> rprint("[dim white]\[url=")
> ```

```python
# Code
rprint("[dim white]\[url=[cyan]https://github.com/[/][dim white]][/]")

# Result
[url=[cyan]https://github.com/]
```


> More generally, you could work with a Text object which will give you more fine control.  

Got it.  It seems [rich.Text](https://rich.readthedocs.io/en/latest/text.html) will serve me.

Resolving patch:
diff --git a/rich/markup.py b/rich/markup.py
--- a/rich/markup.py
+++ b/rich/markup.py
@@ -1,21 +1,20 @@
+import re
 from ast import literal_eval
 from operator import attrgetter
-import re
 from typing import Callable, Iterable, List, Match, NamedTuple, Optional, Tuple, Union
 
+from ._emoji_replace import _emoji_replace
+from .emoji import EmojiVariant
 from .errors import MarkupError
 from .style import Style
 from .text import Span, Text
-from .emoji import EmojiVariant
-from ._emoji_replace import _emoji_replace
-
 
 RE_TAGS = re.compile(
-    r"""((\\*)\[([a-z#\/@].*?)\])""",
+    r"""((\\*)\[([a-z#/@][^[]*?)])""",
     re.VERBOSE,
 )
 
-RE_HANDLER = re.compile(r"^([\w\.]*?)(\(.*?\))?$")
+RE_HANDLER = re.compile(r"^([\w.]*?)(\(.*?\))?$")
 
 
 class Tag(NamedTuple):
@@ -146,6 +145,8 @@ def pop_style(style_name: str) -> Tuple[int, Tag]:
 
     for position, plain_text, tag in _parse(markup):
         if plain_text is not None:
+            # Handle open brace escapes, where the brace is not part of a tag.
+            plain_text = plain_text.replace("\\[", "[")
             append(emoji_replace(plain_text) if emoji else plain_text)
         elif tag is not None:
             if tag.name.startswith("/"):  # Closing tag
@@ -233,8 +234,8 @@ def pop_style(style_name: str) -> Tuple[int, Tag]:
         ":warning-emoji: [bold red blink] DANGER![/]",
     ]
 
-    from rich.table import Table
     from rich import print
+    from rich.table import Table
 
     grid = Table("Markup", "Result", padding=(0, 1))
 


Test patch:
diff --git a/tests/test_markup.py b/tests/test_markup.py
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -1,7 +1,7 @@
 import pytest
 
 from rich.console import Console
-from rich.markup import escape, MarkupError, _parse, render, Tag, RE_TAGS
+from rich.markup import RE_TAGS, MarkupError, Tag, _parse, escape, render
 from rich.text import Span
 
 
@@ -139,6 +139,11 @@ def test_markup_error():
         assert render("[foo]hello[/bar]")
 
 
+def test_markup_escape():
+    result = str(render("[dim white]\[url=[/]"))
+    assert result == "[url="
+
+
 def test_escape_escape():
     # Escaped escapes (i.e. double backslash)should be treated as literal
     result = render(r"\\[bold]FOO")
@@ -165,7 +170,6 @@ def test_escape_escape():
 
 
 def test_events():
-
     result = render("[@click]Hello[/@click] [@click='view.toggle', 'left']World[/]")
     assert str(result) == "Hello World"