mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Invalid pseudo selectors (#2445)
* token error * error on bad pseudo selectors
This commit is contained in:
@@ -61,6 +61,14 @@ VALID_STYLE_FLAGS: Final = {
|
|||||||
"underline",
|
"underline",
|
||||||
"uu",
|
"uu",
|
||||||
}
|
}
|
||||||
|
VALID_PSEUDO_CLASSES: Final = {
|
||||||
|
"blur",
|
||||||
|
"disabled",
|
||||||
|
"enabled",
|
||||||
|
"focus-within",
|
||||||
|
"focus",
|
||||||
|
"hover",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
NULL_SPACING: Final = Spacing.all(0)
|
NULL_SPACING: Final = Spacing.all(0)
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ from rich.panel import Panel
|
|||||||
from rich.syntax import Syntax
|
from rich.syntax import Syntax
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
|
from ..suggestions import get_suggestion
|
||||||
from ._error_tools import friendly_list
|
from ._error_tools import friendly_list
|
||||||
|
from .constants import VALID_PSEUDO_CLASSES
|
||||||
|
|
||||||
|
|
||||||
class TokenError(Exception):
|
class TokenError(Exception):
|
||||||
@@ -56,7 +58,7 @@ class TokenError(Exception):
|
|||||||
line_numbers=True,
|
line_numbers=True,
|
||||||
indent_guides=True,
|
indent_guides=True,
|
||||||
line_range=(max(0, line_no - 2), line_no + 2),
|
line_range=(max(0, line_no - 2), line_no + 2),
|
||||||
highlight_lines={line_no},
|
highlight_lines={line_no + 1},
|
||||||
)
|
)
|
||||||
syntax.stylize_range("reverse bold", self.start, self.end)
|
syntax.stylize_range("reverse bold", self.start, self.end)
|
||||||
return Panel(syntax, border_style="red")
|
return Panel(syntax, border_style="red")
|
||||||
@@ -227,6 +229,29 @@ class Tokenizer:
|
|||||||
(line_no, col_no),
|
(line_no, col_no),
|
||||||
referenced_by=None,
|
referenced_by=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
token.name == "pseudo_class"
|
||||||
|
and token.value.strip(":") not in VALID_PSEUDO_CLASSES
|
||||||
|
):
|
||||||
|
pseudo_class = token.value.strip(":")
|
||||||
|
suggestion = get_suggestion(pseudo_class, list(VALID_PSEUDO_CLASSES))
|
||||||
|
all_valid = f"must be one of {friendly_list(VALID_PSEUDO_CLASSES)}"
|
||||||
|
if suggestion:
|
||||||
|
raise TokenError(
|
||||||
|
self.path,
|
||||||
|
self.code,
|
||||||
|
(line_no, col_no),
|
||||||
|
f"unknown pseudo-class {pseudo_class!r}; did you mean {suggestion!r}?; {all_valid}",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise TokenError(
|
||||||
|
self.path,
|
||||||
|
self.code,
|
||||||
|
(line_no, col_no),
|
||||||
|
f"unknown pseudo-class {pseudo_class!r}; {all_valid}",
|
||||||
|
)
|
||||||
|
|
||||||
col_no += len(value)
|
col_no += len(value)
|
||||||
if col_no >= len(line):
|
if col_no >= len(line):
|
||||||
line_no += 1
|
line_no += 1
|
||||||
|
|||||||
@@ -140,52 +140,52 @@ A1
|
|||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
||||||
A:foo {}
|
A:focus {}
|
||||||
A:foo:bar {}
|
A:focus:hover {}
|
||||||
A
|
A
|
||||||
:foo {}
|
:focus {}
|
||||||
A
|
A
|
||||||
:foo:bar {}
|
:focus:hover {}
|
||||||
A
|
A
|
||||||
:foo
|
:focus
|
||||||
:bar {}
|
:hover {}
|
||||||
A:foo-bar {}
|
A:enabled {}
|
||||||
A
|
A
|
||||||
:foo-bar {}
|
:enabled {}
|
||||||
|
|
||||||
A :foo {}
|
A :focus {}
|
||||||
A :foo :bar {}
|
A :focus :hover {}
|
||||||
A :foo-bar {}
|
A :enabled {}
|
||||||
|
|
||||||
.A:foo {}
|
.A:focus {}
|
||||||
.A:foo:bar {}
|
.A:focus:hover {}
|
||||||
.A
|
.A
|
||||||
:foo {}
|
:focus {}
|
||||||
.A
|
.A
|
||||||
:foo:bar {}
|
:focus:hover {}
|
||||||
.A
|
.A
|
||||||
:foo
|
:focus
|
||||||
:bar {}
|
:hover {}
|
||||||
.A:foo-bar {}
|
.A:enabled {}
|
||||||
.A
|
.A
|
||||||
:foo-bar {}
|
:enabled {}
|
||||||
|
|
||||||
#A:foo {}
|
#A:focus {}
|
||||||
#A:foo:bar {}
|
#A:focus:hover {}
|
||||||
#A
|
#A
|
||||||
:foo {}
|
:focus {}
|
||||||
#A
|
#A
|
||||||
:foo:bar {}
|
:focus:hover {}
|
||||||
#A
|
#A
|
||||||
:foo
|
:focus
|
||||||
:bar {}
|
:hover {}
|
||||||
#A:foo-bar {}
|
#A:enabled {}
|
||||||
#A
|
#A
|
||||||
:foo-bar {}
|
:enabled {}
|
||||||
|
|
||||||
A1.A1.A1:foo {}
|
A1.A1.A1:focus {}
|
||||||
A1.A1#A1:foo {}
|
A1.A1#A1:focus {}
|
||||||
A1:foo.A1:foo#A1:foo {}
|
A1:focus.A1:focus#A1:focus {}
|
||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
||||||
|
|||||||
@@ -1226,3 +1226,21 @@ class TestTypeNames:
|
|||||||
stylesheet.add_source(f"StartType {separator} 1TestType {{}}")
|
stylesheet.add_source(f"StartType {separator} 1TestType {{}}")
|
||||||
with pytest.raises(TokenError):
|
with pytest.raises(TokenError):
|
||||||
stylesheet.parse()
|
stylesheet.parse()
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_bad_psuedo_selector():
|
||||||
|
"""Check unknown selector raises a token error."""
|
||||||
|
|
||||||
|
bad_selector = """\
|
||||||
|
Widget:foo{
|
||||||
|
border: red;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
stylesheet = Stylesheet()
|
||||||
|
stylesheet.add_source(bad_selector, "foo")
|
||||||
|
|
||||||
|
with pytest.raises(TokenError) as error:
|
||||||
|
stylesheet.parse()
|
||||||
|
|
||||||
|
assert error.value.start == (0, 6)
|
||||||
|
|||||||
Reference in New Issue
Block a user