diff --git a/CHANGELOG.md b/CHANGELOG.md index f36d87b03..c938f54f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). + ## [0.12.0] - Unreleased ### Changed @@ -15,6 +16,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Removed `screen.visible_widgets` and `screen.widgets` +### Fixed + +- Numbers in a descendant-combined selector no longer cause an error https://github.com/Textualize/textual/issues/1836 + ## [0.11.1] - 2023-02-17 ### Fixed diff --git a/src/textual/css/tokenize.py b/src/textual/css/tokenize.py index 2a8677f68..b2cd3d30e 100644 --- a/src/textual/css/tokenize.py +++ b/src/textual/css/tokenize.py @@ -75,7 +75,7 @@ expect_selector_continue = Expect( selector_id=r"\#[a-zA-Z_\-][a-zA-Z0-9_\-]*", selector_class=r"\.[a-zA-Z_\-][a-zA-Z0-9_\-]*", selector_universal=r"\*", - selector=r"[a-zA-Z_\-]+", + selector=IDENTIFIER, combinator_child=">", new_selector=r",", declaration_set_start=r"\{", diff --git a/tests/css/test_parse.py b/tests/css/test_parse.py index 727d45af4..49d3368d6 100644 --- a/tests/css/test_parse.py +++ b/tests/css/test_parse.py @@ -8,7 +8,7 @@ from textual.css.parse import substitute_references from textual.css.scalar import Scalar, Unit from textual.css.stylesheet import Stylesheet, StylesheetParseError from textual.css.tokenize import tokenize -from textual.css.tokenizer import ReferencedBy, Token +from textual.css.tokenizer import ReferencedBy, Token, TokenError from textual.css.transition import Transition from textual.geometry import Spacing from textual.layouts.vertical import VerticalLayout @@ -1189,3 +1189,40 @@ class TestParseTextAlign: stylesheet = Stylesheet() stylesheet.add_source(css) assert stylesheet.rules[0].styles.text_align == "start" + + +class TestTypeNames: + def test_type_no_number(self): + stylesheet = Stylesheet() + stylesheet.add_source("TestType {}") + assert len(stylesheet.rules) == 1 + + def test_type_with_number(self): + stylesheet = Stylesheet() + stylesheet.add_source("TestType1 {}") + assert len(stylesheet.rules) == 1 + + def test_type_starts_with_number(self): + stylesheet = Stylesheet() + stylesheet.add_source("1TestType {}") + with pytest.raises(TokenError): + stylesheet.parse() + + def test_combined_type_no_number(self): + for separator in " >,": + stylesheet = Stylesheet() + stylesheet.add_source(f"StartType {separator} TestType {{}}") + assert len(stylesheet.rules) == 1 + + def test_combined_type_with_number(self): + for separator in " >,": + stylesheet = Stylesheet() + stylesheet.add_source(f"StartType {separator} TestType1 {{}}") + assert len(stylesheet.rules) == 1 + + def test_combined_type_starts_with_number(self): + for separator in " >,": + stylesheet = Stylesheet() + stylesheet.add_source(f"StartType {separator} 1TestType {{}}") + with pytest.raises(TokenError): + stylesheet.parse()