From 2774242aa88dabfd9eea4d2faba852746d900ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Sun, 23 Aug 2020 11:47:40 +0200 Subject: [PATCH] (fluent.syntax) Don't drop to newline when serializing patterns starting with special chars --- fluent.syntax/fluent/syntax/serializer.py | 33 +++++--- .../fixtures_reference/special_chars.ftl | 14 ++++ .../fixtures_reference/special_chars.json | 82 +++++++++++++++++++ fluent.syntax/tests/syntax/test_serializer.py | 30 ++++++- 4 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 fluent.syntax/tests/syntax/fixtures_reference/special_chars.ftl create mode 100644 fluent.syntax/tests/syntax/fixtures_reference/special_chars.json diff --git a/fluent.syntax/fluent/syntax/serializer.py b/fluent.syntax/fluent/syntax/serializer.py index 449d1bda..7c1bb087 100644 --- a/fluent.syntax/fluent/syntax/serializer.py +++ b/fluent.syntax/fluent/syntax/serializer.py @@ -2,7 +2,7 @@ from . import ast -def indent(content): +def indent_except_first_line(content): return " ".join( content.splitlines(True) ) @@ -18,6 +18,20 @@ def is_select_expr(elem): isinstance(elem.expression, ast.SelectExpression)) +def should_start_on_new_line(pattern): + is_multiline = any(is_select_expr(elem) for elem in pattern.elements) \ + or any(includes_new_line(elem) for elem in pattern.elements) + + if is_multiline: + first_element = pattern.elements[0] + if isinstance(first_element, ast.TextElement): + first_char = first_element.value[0] + if first_char in ("[", ".", "*"): + return False + return True + return False + + class FluentSerializer(object): """FluentSerializer converts :class:`.ast.SyntaxNode` objects to unicode strings. @@ -119,19 +133,16 @@ def serialize_term(term): def serialize_attribute(attribute): return "\n .{} ={}".format( attribute.id.name, - indent(serialize_pattern(attribute.value)) + indent_except_first_line(serialize_pattern(attribute.value)) ) def serialize_pattern(pattern): - content = "".join([ - serialize_element(elem) - for elem in pattern.elements]) - start_on_new_line = any( - includes_new_line(elem) or is_select_expr(elem) - for elem in pattern.elements) - if start_on_new_line: - return '\n {}'.format(indent(content)) + content = "".join(serialize_element(elem) for elem in pattern.elements) + content = indent_except_first_line(content) + + if should_start_on_new_line(pattern): + return '\n {}'.format(content) return ' {}'.format(content) @@ -193,7 +204,7 @@ def serialize_variant(variant): return "\n{}[{}]{}".format( " *" if variant.default else " ", serialize_variant_key(variant.key), - indent(serialize_pattern(variant.value)) + indent_except_first_line(serialize_pattern(variant.value)) ) diff --git a/fluent.syntax/tests/syntax/fixtures_reference/special_chars.ftl b/fluent.syntax/tests/syntax/fixtures_reference/special_chars.ftl new file mode 100644 index 00000000..5224bad7 --- /dev/null +++ b/fluent.syntax/tests/syntax/fixtures_reference/special_chars.ftl @@ -0,0 +1,14 @@ +## OK + +bracket-inline = [Value] +dot-inline = .Value +star-inline = *Value + +## ERRORS + +bracket-newline = + [Value] +dot-newline = + .Value +star-newline = + *Value diff --git a/fluent.syntax/tests/syntax/fixtures_reference/special_chars.json b/fluent.syntax/tests/syntax/fixtures_reference/special_chars.json new file mode 100644 index 00000000..77f2ff60 --- /dev/null +++ b/fluent.syntax/tests/syntax/fixtures_reference/special_chars.json @@ -0,0 +1,82 @@ +{ + "type": "Resource", + "body": [ + { + "type": "GroupComment", + "content": "OK" + }, + { + "type": "Message", + "id": { + "type": "Identifier", + "name": "bracket-inline" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "TextElement", + "value": "[Value]" + } + ] + }, + "attributes": [], + "comment": null + }, + { + "type": "Message", + "id": { + "type": "Identifier", + "name": "dot-inline" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "TextElement", + "value": ".Value" + } + ] + }, + "attributes": [], + "comment": null + }, + { + "type": "Message", + "id": { + "type": "Identifier", + "name": "star-inline" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "TextElement", + "value": "*Value" + } + ] + }, + "attributes": [], + "comment": null + }, + { + "type": "GroupComment", + "content": "ERRORS" + }, + { + "type": "Junk", + "annotations": [], + "content": "bracket-newline =\n [Value]\n" + }, + { + "type": "Junk", + "annotations": [], + "content": "dot-newline =\n .Value\n" + }, + { + "type": "Junk", + "annotations": [], + "content": "star-newline =\n *Value\n" + } + ] +} diff --git a/fluent.syntax/tests/syntax/test_serializer.py b/fluent.syntax/tests/syntax/test_serializer.py index 5714f65c..1f716bc7 100644 --- a/fluent.syntax/tests/syntax/test_serializer.py +++ b/fluent.syntax/tests/syntax/test_serializer.py @@ -139,12 +139,31 @@ def test_comment_standalone(self): input = """\ foo = Foo - # A multiline + # A standalone comment bar = Bar """ self.assertEqual(self.pretty_ftl(input), dedent_ftl(input)) + def test_multiline_starting_inline(self): + input = """\ + foo = Foo + Bar + """ + output = """\ + foo = + Foo + Bar + """ + self.assertEqual(self.pretty_ftl(input), dedent_ftl(output)) + + def test_multiline_starting_inline_with_special_char(self): + input = """\ + foo = *Foo + Bar + """ + self.assertEqual(self.pretty_ftl(input), dedent_ftl(input)) + def test_multiline_with_placeable(self): input = """\ foo = @@ -269,6 +288,15 @@ def test_select_expression_in_inline_value(self): """ self.assertEqual(self.pretty_ftl(input), dedent_ftl(output)) + def test_select_expression_in_inline_value_starting_with_special_char(self): + input = """\ + foo = .Foo { $sel -> + *[a] A + [b] B + } + """ + self.assertEqual(self.pretty_ftl(input), dedent_ftl(input)) + def test_select_expression_in_multi_multiline(self): input = """\ foo =