Fix newlines in yaml
This commit is contained in:
parent
94aeaf6e7a
commit
bbbbe33e54
4 changed files with 84 additions and 7 deletions
|
|
@ -157,6 +157,9 @@ class ErbTranslator:
|
|||
return self.ruby_value(expr)
|
||||
|
||||
def statement_to_erb(self, stmt: str) -> str:
|
||||
if stmt.endswith("-"):
|
||||
stmt = stmt[:-1].rstrip()
|
||||
|
||||
if stmt.startswith("for "):
|
||||
m = re.match(
|
||||
r"^for\s+([A-Za-z_][A-Za-z0-9_]*)\s+in\s+([A-Za-z_][A-Za-z0-9_]*)$",
|
||||
|
|
@ -172,7 +175,7 @@ class ErbTranslator:
|
|||
if stmt == "endfor":
|
||||
if self.loop_stack:
|
||||
self.loop_stack.pop()
|
||||
return "<% end -%>"
|
||||
return "<% end %>"
|
||||
|
||||
if stmt.startswith("if "):
|
||||
cond = stmt[3:].strip()
|
||||
|
|
|
|||
|
|
@ -246,8 +246,18 @@ class YamlHandler(DictLikeHandler):
|
|||
return None, None
|
||||
return None, None
|
||||
|
||||
def next_significant_line(index: int) -> tuple[int, str] | None:
|
||||
for future_line in lines[index + 1 :]:
|
||||
future_stripped = future_line.lstrip()
|
||||
if not future_stripped.strip() or future_stripped.startswith("#"):
|
||||
continue
|
||||
return len(future_line) - len(future_stripped), future_stripped
|
||||
return None
|
||||
|
||||
for line_index, raw_line in enumerate(lines):
|
||||
stripped = raw_line.lstrip()
|
||||
is_blank = not stripped.strip()
|
||||
is_comment = stripped.startswith("#")
|
||||
indent = len(raw_line) - len(stripped)
|
||||
|
||||
# If we're skipping lines inside a collection replaced by a loop,
|
||||
|
|
@ -261,7 +271,22 @@ class YamlHandler(DictLikeHandler):
|
|||
# Stop only when a non-list item at the parent indentation appears,
|
||||
# or when indentation moves above the parent collection.
|
||||
if skip_until_indent is not None:
|
||||
if not stripped or stripped.startswith("#"):
|
||||
if is_blank:
|
||||
next_line = next_significant_line(line_index)
|
||||
if next_line is None:
|
||||
skip_until_indent = None
|
||||
out_lines.append(raw_line)
|
||||
else:
|
||||
next_indent, next_stripped = next_line
|
||||
still_in_collection = next_indent > skip_until_indent or (
|
||||
next_indent == skip_until_indent
|
||||
and next_stripped.startswith("- ")
|
||||
)
|
||||
if not still_in_collection:
|
||||
skip_until_indent = None
|
||||
out_lines.append(raw_line)
|
||||
continue
|
||||
if is_comment:
|
||||
if indent <= skip_until_indent:
|
||||
skip_until_indent = None
|
||||
out_lines.append(raw_line)
|
||||
|
|
@ -277,7 +302,7 @@ class YamlHandler(DictLikeHandler):
|
|||
continue # Skip this line
|
||||
|
||||
# Blank or comment lines
|
||||
if not stripped or stripped.startswith("#"):
|
||||
if is_blank or is_comment:
|
||||
out_lines.append(raw_line)
|
||||
continue
|
||||
|
||||
|
|
@ -490,7 +515,7 @@ class YamlHandler(DictLikeHandler):
|
|||
lines.append(j2.for_start(item_var, collection_var))
|
||||
lines.append(j2.for_end())
|
||||
|
||||
return "\n".join(lines) + "\n"
|
||||
return "\n".join(lines)
|
||||
|
||||
def _dict_to_yaml_lines(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -43,8 +43,10 @@ def to_json(
|
|||
return filtered(value, f"to_json({', '.join(args)})")
|
||||
|
||||
|
||||
def statement(value: str) -> str:
|
||||
def statement(value: str, *, trim_right: bool = False) -> str:
|
||||
"""Return a Jinja2 statement tag for an already-built statement body."""
|
||||
if trim_right:
|
||||
return f"{{% {value} -%}}"
|
||||
return f"{{% {value} %}}"
|
||||
|
||||
|
||||
|
|
@ -64,8 +66,8 @@ def for_start(item_var: str, collection_var: str) -> str:
|
|||
return statement(f"for {item_var} in {collection_var}")
|
||||
|
||||
|
||||
def for_end() -> str:
|
||||
return statement("endfor")
|
||||
def for_end(*, trim_right: bool = False) -> str:
|
||||
return statement("endfor", trim_right=trim_right)
|
||||
|
||||
|
||||
def yaml_scalar_expression(var_name: str, raw_value: str | None = None) -> str:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from jinjaturtle.core import (
|
|||
analyze_loops,
|
||||
flatten_config,
|
||||
generate_ansible_yaml,
|
||||
generate_erb_template,
|
||||
generate_jinja2_template,
|
||||
)
|
||||
from jinjaturtle.handlers.yaml import YamlHandler
|
||||
|
|
@ -169,6 +170,52 @@ def test_yaml_indentless_sequence_loop_roundtrips_semantically(tmp_path: Path):
|
|||
assert " vagrant:" not in template
|
||||
|
||||
|
||||
def test_yaml_loop_preserves_blank_separator_after_list(tmp_path: Path):
|
||||
from jinja2 import Template
|
||||
|
||||
cases = [
|
||||
(
|
||||
"blank",
|
||||
"require:\n - rubocop-performance\n - rubocop-rspec\n\nAllCops:\n NewCops: enable\n",
|
||||
"\n - rubocop-rspec\n\nAllCops:",
|
||||
"<% end %>\nAllCops:",
|
||||
),
|
||||
(
|
||||
"no_blank",
|
||||
"require:\n - rubocop-performance\n - rubocop-rspec\nAllCops:\n NewCops: enable\n",
|
||||
"\n - rubocop-rspec\nAllCops:",
|
||||
"<% end %>AllCops:",
|
||||
),
|
||||
]
|
||||
|
||||
for label, text, rendered_expected, erb_expected in cases:
|
||||
path = tmp_path / f"{label}.yml"
|
||||
path.write_text(text, encoding="utf-8")
|
||||
|
||||
fmt, parsed = parse_config(path)
|
||||
loop_candidates = analyze_loops(fmt, parsed)
|
||||
flat_items = flatten_config(fmt, parsed, loop_candidates)
|
||||
defaults = yaml.safe_load(
|
||||
generate_ansible_yaml("role", flat_items, loop_candidates)
|
||||
)
|
||||
|
||||
template = generate_jinja2_template(
|
||||
fmt, parsed, "role", original_text=text, loop_candidates=loop_candidates
|
||||
)
|
||||
rendered = Template(template).render(**defaults)
|
||||
assert rendered_expected in rendered
|
||||
|
||||
erb_template = generate_erb_template(
|
||||
fmt,
|
||||
parsed,
|
||||
"role",
|
||||
original_text=text,
|
||||
loop_candidates=loop_candidates,
|
||||
flat_items=flat_items,
|
||||
)
|
||||
assert erb_expected in erb_template
|
||||
|
||||
|
||||
def test_yaml_loop_preserves_following_top_level_comments(tmp_path: Path):
|
||||
from jinja2 import Template
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue