diff --git a/src/django_components/util/tag_parser.py b/src/django_components/util/tag_parser.py index 8b0e74fb..ad409133 100644 --- a/src/django_components/util/tag_parser.py +++ b/src/django_components/util/tag_parser.py @@ -34,6 +34,12 @@ class TagAttr: value = f"...{value}" return value + def formatted(self) -> str: + s = self.formatted_value() + if self.key: + return f"{self.key}={s}" + return s + # Parse the content of a Django template tag like this: # @@ -139,6 +145,7 @@ def parse_tag_attrs(text: str) -> Tuple[str, List[TagAttr]]: take_while(TAG_WHITESPACE) start_index = len(normalized) + is_translation = False # If token starts with a quote, we assume it's a value without key part. # e.g. `component 'my_comp'` @@ -185,8 +192,6 @@ def parse_tag_attrs(text: str) -> Tuple[str, List[TagAttr]]: if is_next_token("_("): taken_n(2) # _( is_translation = True - else: - is_translation = False # NOTE: We assume no space between the translation syntax and the quote. quote_char = taken_n(1) # " or ' @@ -216,7 +221,7 @@ def parse_tag_attrs(text: str) -> Tuple[str, List[TagAttr]]: start_index=start_index, quoted=quoted, spread=is_spread, - translation=False, + translation=is_translation, ) ) diff --git a/tests/test_tag_parser.py b/tests/test_tag_parser.py index c67de8d6..483b988b 100644 --- a/tests/test_tag_parser.py +++ b/tests/test_tag_parser.py @@ -9,61 +9,96 @@ setup_test_config({"autodiscover": False}) class TagParserTests(BaseTestCase): def test_tag_parser(self): _, attrs = parse_tag_attrs("component 'my_comp' key=val key2='val2 two' ") + + expected_attrs = [ + TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), + TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), + TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), + TagAttr(key="key2", value="val2 two", start_index=28, quoted="'", spread=False, translation=False), + ] + + self.assertEqual(attrs, expected_attrs) self.assertEqual( - attrs, + [a.formatted() for a in attrs], [ - TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), - TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), - TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), - TagAttr(key="key2", value="val2 two", start_index=28, quoted="'", spread=False, translation=False), + "component", + "'my_comp'", + "key=val", + "key2='val2 two'", ], ) def test_tag_parser_nested_quotes(self): _, attrs = parse_tag_attrs("component 'my_comp' key=val key2='val2 \"two\"' text=\"organisation's\" ") + + expected_attrs = [ + TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), + TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), + TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), + TagAttr(key="key2", value='val2 "two"', start_index=28, quoted="'", spread=False, translation=False), + TagAttr(key="text", value="organisation's", start_index=46, quoted='"', spread=False, translation=False), + ] + + self.assertEqual(attrs, expected_attrs) self.assertEqual( - attrs, + [a.formatted() for a in attrs], [ - TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), - TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), - TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), - TagAttr(key="key2", value='val2 "two"', start_index=28, quoted="'", spread=False, translation=False), - TagAttr( - key="text", value="organisation's", start_index=46, quoted='"', spread=False, translation=False - ), + "component", + "'my_comp'", + "key=val", + "key2='val2 \"two\"'", + 'text="organisation\'s"', ], ) def test_tag_parser_trailing_quote_single(self): _, attrs = parse_tag_attrs("component 'my_comp' key=val key2='val2 \"two\"' text=\"organisation's\" 'abc") + expected_attrs = [ + TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), + TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), + TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), + TagAttr(key="key2", value='val2 "two"', start_index=28, quoted="'", spread=False, translation=False), + TagAttr(key="text", value="organisation's", start_index=46, quoted='"', spread=False, translation=False), + TagAttr(key=None, value="'abc", start_index=68, quoted=None, spread=False, translation=False), + ] + + self.assertEqual(attrs, expected_attrs) self.assertEqual( - attrs, + [a.formatted() for a in attrs], [ - TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), - TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), - TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), - TagAttr(key="key2", value='val2 "two"', start_index=28, quoted="'", spread=False, translation=False), - TagAttr( - key="text", value="organisation's", start_index=46, quoted='"', spread=False, translation=False - ), - TagAttr(key=None, value="'abc", start_index=68, quoted=None, spread=False, translation=False), + "component", + "'my_comp'", + "key=val", + "key2='val2 \"two\"'", + 'text="organisation\'s"', + "'abc", ], ) def test_tag_parser_trailing_quote_double(self): _, attrs = parse_tag_attrs('component "my_comp" key=val key2="val2 \'two\'" text=\'organisation"s\' "abc') + expected_attrs = [ + TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), + TagAttr(key=None, value="my_comp", start_index=10, quoted='"', spread=False, translation=False), + TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), + TagAttr(key="key2", value="val2 'two'", start_index=28, quoted='"', spread=False, translation=False), + TagAttr( + key="text", value='organisation"s', start_index=46, quoted="'", spread=False, translation=False + ), # noqa: E501 + TagAttr(key=None, value='"abc', start_index=68, quoted=None, spread=False, translation=False), + ] + + self.assertEqual(attrs, expected_attrs) self.assertEqual( - attrs, + [a.formatted() for a in attrs], [ - TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), - TagAttr(key=None, value="my_comp", start_index=10, quoted='"', spread=False, translation=False), - TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), - TagAttr(key="key2", value="val2 'two'", start_index=28, quoted='"', spread=False, translation=False), - TagAttr( - key="text", value='organisation"s', start_index=46, quoted="'", spread=False, translation=False - ), # noqa: E501 - TagAttr(key=None, value='"abc', start_index=68, quoted=None, spread=False, translation=False), + "component", + '"my_comp"', + "key=val", + "key2=\"val2 'two'\"", + "text='organisation\"s'", + '"abc', ], ) @@ -71,17 +106,25 @@ class TagParserTests(BaseTestCase): _, attrs = parse_tag_attrs( "component 'my_comp' key=val key2='val2 \"two\"' text=\"organisation's\" value='abc" ) + expected_attrs = [ + TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), + TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), + TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), + TagAttr(key="key2", value='val2 "two"', start_index=28, quoted="'", spread=False, translation=False), + TagAttr(key="text", value="organisation's", start_index=46, quoted='"', spread=False, translation=False), + TagAttr(key="value", value="'abc", start_index=68, quoted=None, spread=False, translation=False), + ] + + self.assertEqual(attrs, expected_attrs) self.assertEqual( - attrs, + [a.formatted() for a in attrs], [ - TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), - TagAttr(key=None, value="my_comp", start_index=10, quoted="'", spread=False, translation=False), - TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), - TagAttr(key="key2", value='val2 "two"', start_index=28, quoted="'", spread=False, translation=False), - TagAttr( - key="text", value="organisation's", start_index=46, quoted='"', spread=False, translation=False - ), - TagAttr(key="value", value="'abc", start_index=68, quoted=None, spread=False, translation=False), + "component", + "'my_comp'", + "key=val", + "key2='val2 \"two\"'", + 'text="organisation\'s"', + "value='abc", ], ) @@ -89,16 +132,45 @@ class TagParserTests(BaseTestCase): _, attrs = parse_tag_attrs( 'component "my_comp" key=val key2="val2 \'two\'" text=\'organisation"s\' value="abc' ) + expected_attrs = [ + TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), + TagAttr(key=None, value="my_comp", start_index=10, quoted='"', spread=False, translation=False), + TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), + TagAttr(key="key2", value="val2 'two'", start_index=28, quoted='"', spread=False, translation=False), + TagAttr(key="text", value='organisation"s', start_index=46, quoted="'", spread=False, translation=False), + TagAttr(key="value", value='"abc', start_index=68, quoted=None, spread=False, translation=False), + ] + + self.assertEqual(attrs, expected_attrs) self.assertEqual( - attrs, + [a.formatted() for a in attrs], [ - TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), - TagAttr(key=None, value="my_comp", start_index=10, quoted='"', spread=False, translation=False), - TagAttr(key="key", value="val", start_index=20, quoted=None, spread=False, translation=False), - TagAttr(key="key2", value="val2 'two'", start_index=28, quoted='"', spread=False, translation=False), - TagAttr( - key="text", value='organisation"s', start_index=46, quoted="'", spread=False, translation=False - ), - TagAttr(key="value", value='"abc', start_index=68, quoted=None, spread=False, translation=False), + "component", + '"my_comp"', + "key=val", + "key2=\"val2 'two'\"", + "text='organisation\"s'", + 'value="abc', + ], + ) + + def test_tag_parser_translation(self): + _, attrs = parse_tag_attrs('component "my_comp" _("one") key=_("two")') + + expected_attrs = [ + TagAttr(key=None, value="component", start_index=0, quoted=None, spread=False, translation=False), + TagAttr(key=None, value="my_comp", start_index=10, quoted='"', spread=False, translation=False), + TagAttr(key=None, value="one", start_index=20, quoted='"', spread=False, translation=True), + TagAttr(key="key", value="two", start_index=29, quoted='"', spread=False, translation=True), + ] + + self.assertEqual(attrs, expected_attrs) + self.assertEqual( + [a.formatted() for a in attrs], + [ + "component", + '"my_comp"', + '_("one")', + 'key=_("two")', ], )