From: Christian Heller <c.heller@plomlompom.de>
Date: Sun, 27 Apr 2025 18:00:38 +0000 (+0200)
Subject: Offer removal of redundant empty lines.
X-Git-Url: https://plomlompom.com/repos/%7B%7B%20web_path%20%7D%7D/static/%22https:/validator.w3.org/edit?a=commitdiff_plain;p=ledgplom

Offer removal of redundant empty lines.
---

diff --git a/src/ledgplom/http.py b/src/ledgplom/http.py
index 2eea419..550ad6f 100644
--- a/src/ledgplom/http.py
+++ b/src/ledgplom/http.py
@@ -86,6 +86,9 @@ class _Handler(PlomHttpHandler):
 
     def post_ledger_action(self) -> Path:
         """Call .server.ledger.(move|copy|add_empty_new)_booking."""
+        if 'remove_redundant_empty_lines' in self.postvars.as_dict:
+            self.server.ledger.remove_redundant_empty_lines()
+            return Path(self.path)
         if 'add_booking' in self.postvars.as_dict:
             id_ = self.server.ledger.add_empty_block()
         else:
@@ -106,7 +109,8 @@ class _Handler(PlomHttpHandler):
                 Path('/').joinpath(_PAGENAME_EDIT_STRUCTURED
                                    ).joinpath(self.path_toks[2]))
             return
-        ctx = {'unsaved_changes': self.server.ledger.tainted, 'path': self.path}
+        ctx = {'unsaved_changes': self.server.ledger.tainted,
+               'path': self.path}
         if self.pagename == 'balance':
             self.get_balance(ctx)
         elif self.pagename.startswith(_PREFIX_EDIT):
@@ -188,5 +192,7 @@ class _Handler(PlomHttpHandler):
     def get_ledger(self, ctx: dict[str, Any], raw: bool) -> None:
         """Display ledger of all Bookings."""
         ctx['blocks'] = self.server.ledger.blocks
+        ctx['has_redundant_empty_lines'] =\
+            self.server.ledger.has_redundant_empty_lines
         self._send_rendered(
             _PAGENAME_LEDGER_RAW if raw else _PAGENAME_LEDGER_STRUCTURED, ctx)
diff --git a/src/ledgplom/ledger.py b/src/ledgplom/ledger.py
index 1587fd7..b7796ff 100644
--- a/src/ledgplom/ledger.py
+++ b/src/ledgplom/ledger.py
@@ -134,7 +134,7 @@ class _DatLine:
         """Return as how to be written in .dat file's text content."""
         comment_part = ' ; '.join([''] + [s for s in [self.comment] if s])
         code_part = f'{self.code} ' if self.code else ''
-        return f'{code_part}{comment_part.lstrip()}'
+        return f'{code_part}{comment_part.lstrip()}'.rstrip()
 
     def copy(self) -> Self:
         """Create new instance copying the fields named in .to_copy."""
@@ -344,6 +344,27 @@ class _Gap(_LinesBlock[_GapLine]):
         """Grow self by lines."""
         self._lines += lines
 
+    @property
+    def redundant_empty_lines(self):
+        """If self has more empty lines than necessary."""
+        redundancies = []
+        prev_line = None
+        idx_last_non_empty = -1
+        for idx, line in enumerate(self._lines):
+            if line.comment:
+                idx_last_non_empty = idx
+            elif '' == prev_line and not line.comment:
+                redundancies += [line]
+            prev_line = line
+        redundancies += [line for line in self._lines[idx_last_non_empty + 2:]
+                         if line not in redundancies]
+        return redundancies
+
+    def remove_redundant_empty_lines(self):
+        """From self remove redundant empty lines."""
+        for line in self.redundant_empty_lines:
+            self._lines.remove(line)
+
 
 class _Booking(_LinesBlock[_BookingLine]):
 
@@ -633,6 +654,16 @@ class Ledger:
                 return False
         return True
 
+    @property
+    def has_redundant_empty_lines(self) -> bool:
+        """If any gaps have redunant empty lines."""
+        return bool([b for b in self.blocks if b.gap.redundant_empty_lines])
+
+    def remove_redundant_empty_lines(self) -> None:
+        """From all .blocks remove redundant empty lines."""
+        for gap in [b.gap for b in self.blocks if b.gap.redundant_empty_lines]:
+            gap.remove_redundant_empty_lines()
+
     @property
     def tainted(self) -> bool:
         """If ._dat_lines different to those of last .load()."""
diff --git a/src/templates/_macros.tmpl b/src/templates/_macros.tmpl
index a9a4b22..dcb3f82 100644
--- a/src/templates/_macros.tmpl
+++ b/src/templates/_macros.tmpl
@@ -70,6 +70,15 @@ td.block_column.critical {
 
 
 
+{% macro ledger_empty_lines_fix(has_redundant_empty_lines) %}
+{% if has_redundant_empty_lines %}
+    Detected redundant empty lines in gaps, <input type="submit" name="remove_redundant_empty_lines" value="fix?" />
+    <hr />
+{% endif %}
+{% endmacro %}
+
+
+
 {% macro ledger_block_columns(mode, block) %}
 <tr></tr><!-- just to keep the background-color alternation in proper order -->
 <tr id="block_{{block.id_}}">
diff --git a/src/templates/ledger_raw.tmpl b/src/templates/ledger_raw.tmpl
index 76a3298..8779504 100644
--- a/src/templates/ledger_raw.tmpl
+++ b/src/templates/ledger_raw.tmpl
@@ -13,6 +13,7 @@ table {
 
 {% block content %}
 <form action="/ledger_raw" method="POST">
+{{macros.ledger_empty_lines_fix(has_redundant_empty_lines)}}
 <table class="alternating">
 {% for block in blocks %}
     {{macros.ledger_block_columns('raw', block)}}
diff --git a/src/templates/ledger_structured.tmpl b/src/templates/ledger_structured.tmpl
index 12b7400..efb742e 100644
--- a/src/templates/ledger_structured.tmpl
+++ b/src/templates/ledger_structured.tmpl
@@ -17,6 +17,7 @@ td.amount, td.currency {
 
 {% block content %}
 <form action="/ledger_structured" method="POST">
+{{macros.ledger_empty_lines_fix(has_redundant_empty_lines)}}
 <table class="alternating">
 {% for block in blocks %}
     {{macros.ledger_block_columns('structured', block)}}