diff --git a/CHANGELOG.md b/CHANGELOG.md index a1fee9deab..60011f95b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ - [Fix][issue2774]: Properly support `\n` in `evennia connections` long descriptions (Griatch) - [Fix][issue3312]: Handle all edge cases breaking `monitor/monitored` `input_funcs` (Griatch) - [Fix][issue3154]: Persistent EvMenu caused multiple cmdsets on reload (Griatch) +- [Fix][issue3193]: Formatting of inner nested evtable would break (Griatch) - [Doc][pull3801]: Move Evennia doc build system to latest Sphinx/myST (PowershellNinja, also honorary mention to electroglyph) - [Doc][pull3800]: Describe support for Telnet SSH in HAProxy documentation (holl0wstar) @@ -79,6 +80,7 @@ [issue2774]: https://github.com/evennia/evennia/issues/2774 [issue3312]: https://github.com/evennia/evennia/issues/3312 [issue3154]: https://github.com/evennia/evennia/issues/3154 +[issue3193]: https://github.com/evennia/evennia/issues/3193 ## Evennia 5.0.1 diff --git a/evennia/utils/evtable.py b/evennia/utils/evtable.py index 4c3a501172..181d57e4f8 100644 --- a/evennia/utils/evtable.py +++ b/evennia/utils/evtable.py @@ -184,19 +184,17 @@ class ANSITextWrapper(TextWrapper): 'use', ' ', 'the', ' ', '-b', ' ', option!' otherwise. """ - # NOTE-PYTHON3: The following code only roughly approximates what this - # function used to do. Regex splitting on ANSIStrings is - # dropping ANSI codes, so we're using ANSIString.split - # for the time being. - # - # A less hackier solution would be appreciated. - chunks = _to_ansi(text).split() - - chunks = [chunk + " " for chunk in chunks if chunk] # remove empty chunks - - if len(chunks) > 1: - chunks[-1] = chunks[-1][0:-1] - + # ANSIString.split(None) collapses repeated whitespace, which breaks + # pre-formatted content (such as nested EvTables). Split on explicit + # spaces instead and keep separators as separate chunks. + text = _to_ansi(text) + parts = text.split(" ") + chunks = [] + for idx, part in enumerate(parts): + if part: + chunks.append(part) + if idx < len(parts) - 1: + chunks.append(" ") return chunks def _wrap_chunks(self, chunks): @@ -557,7 +555,26 @@ class EvCell: align = self.align hfill_char = self.hfill_char width = self.width - return [justify(line, width, align=align, fillchar=hfill_char) for line in data] + aligned = [] + for line in data: + # Preserve manually spaced/pre-formatted lines (like nested tables). + if " " in line or (line and (line[0].isspace() or line[-1].isspace())): + line_width = d_len(line) + if line_width >= width: + aligned.append(justify(line, width, align="a", fillchar=hfill_char)) + continue + pad = width - line_width + if align == "r": + aligned.append(hfill_char * pad + line) + elif align == "c": + left = pad // 2 + right = pad - left + aligned.append(hfill_char * left + line + hfill_char * right) + else: + aligned.append(line + hfill_char * pad) + else: + aligned.append(justify(line, width, align=align, fillchar=hfill_char)) + return aligned def _valign(self, data): """ diff --git a/evennia/utils/tests/test_evtable.py b/evennia/utils/tests/test_evtable.py index 34fb3d40fc..e4a5f41339 100644 --- a/evennia/utils/tests/test_evtable.py +++ b/evennia/utils/tests/test_evtable.py @@ -272,18 +272,13 @@ class TestEvTable(EvenniaTestCase): self._validate(expected, str(table)) # add by passing a column to constructor directly - colB = evtable.EvColumn("another", width=8) - table = evtable.EvTable(table=[colB], fill_char=".", pad_char="#") - self._validate(expected, str(table)) # more complex table - colA = evtable.EvColumn("this", "is", "a", "column") # no width colB = evtable.EvColumn("and", "another", "one", "here", width=8) - table = evtable.EvTable(table=[colA, colB], fill_char=".", pad_char="#") expected = """ @@ -295,9 +290,32 @@ class TestEvTable(EvenniaTestCase): |#column#|#here.#| +--------+-------+ """ - self._validate(expected, str(table)) + def test_nested_table_preserves_inner_spacing(self): + """ + Testing https://github.com/evennia/evennia/issues/3193 + + Nested EvTables should preserve spacing in inner table rows. + """ + content = [evtable.EvColumn(align="r"), evtable.EvColumn(align="l")] + content[0].add_rows("Item 1") + content[0].add_rows("Item 2") + content[1].add_rows("Item 3") + content[1].add_rows("Item 4") + + left_table = evtable.EvTable(table=content, border="cells", header=False, width=24, align="l") + right_table = evtable.EvTable( + table=content, border="cells", header=False, width=24, align="r" + ) + nested_table = evtable.EvTable( + table=[[left_table], [right_table]], border="incols", width=55, align="c" + ) + + rendered = ansi.strip_ansi(str(nested_table)) + self.assertIn("| Item 1 | Item 3 |", rendered) + self.assertIn("| Item 2 | Item 4 |", rendered) + def test_width_enforcement(self): """ Testing https://github.com/evennia/evennia/issues/2761