From 2e1f87c0e63e1ecc42d5f74e680e6a0e77202606 Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Sun, 4 Feb 2024 14:46:04 -0700 Subject: [PATCH 1/4] remove hardcoded newline req in appearance --- evennia/objects/objects.py | 11 +++++++---- evennia/utils/utils.py | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 7e742b1cbc..cf3b6279de 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -30,6 +30,7 @@ from evennia.utils.utils import ( iter_to_str, lazy_property, make_iter, + strip_extra_whitespace, to_str, variable_from_module, ) @@ -220,7 +221,9 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): {header} |c{name}|n {desc} -{exits}{characters}{things} +{exits} +{characters} +{things} {footer} """ # on-object properties @@ -1385,7 +1388,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): char.get_display_name(looker, **kwargs) for char in characters ) - return f"\n|wCharacters:|n {character_names}" if character_names else "" + return f"|wCharacters:|n {character_names}" if character_names else "" def get_display_things(self, looker, **kwargs): """ @@ -1416,7 +1419,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): singular, plural = thing.get_numbered_name(nthings, looker, key=thingname) thing_names.append(singular if nthings == 1 else plural) thing_names = iter_to_str(thing_names) - return f"\n|wYou see:|n {thing_names}" if thing_names else "" + return f"|wYou see:|n {thing_names}" if thing_names else "" def get_display_footer(self, looker, **kwargs): """ @@ -1443,7 +1446,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): str: The final formatted output. """ - return appearance.strip() + return strip_extra_whitespace(appearance) def return_appearance(self, looker, **kwargs): """ diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 03d1e98a55..5ababa62d1 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -472,6 +472,31 @@ def iter_to_str(iterable, sep=",", endsep=", and", addquote=False): list_to_string = iter_to_str iter_to_string = iter_to_str +re_empty = re.compile("\n\s*\n") + +def strip_extra_whitespace(text, max_linebreaks=1, max_spacing=2): + """ + Removes extra sequential whitespace in a block of text. This will also remove any trailing + whitespace at the end. + + Args: + text (str): A string which may contain excess internal whitespace. + + Keyword args: + max_linebreaks (int): How many linebreak characters are allowed to occur in a row. + max_spacing (int): How many spaces are allowed to occur in a row. + + """ + text = text.rstrip() + # replaces any non-visible lines that are just whitespace characters with actual empty lines + # this allows the blank-line compression to eliminate them if needed + text = re_empty.sub("\n\n", text) + # replace groups of extra spaces with the maximum number of spaces + text = re.sub(f" {{{max_spacing},}}", " "*max_spacing, text) + # replace groups of extra newlines with the maximum number of newlines + text = re.sub(f"\n{{{max_linebreaks},}}", "\n"*max_linebreaks, text) + return text + def wildcard_to_regexp(instring): """ From 77ce84a0b1e2302db1e1ee5e8dcbf51f1db887c6 Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Sun, 4 Feb 2024 15:00:54 -0700 Subject: [PATCH 2/4] add unit test --- evennia/utils/tests/test_utils.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/evennia/utils/tests/test_utils.py b/evennia/utils/tests/test_utils.py index 9c5d4827f0..3d44c08621 100644 --- a/evennia/utils/tests/test_utils.py +++ b/evennia/utils/tests/test_utils.py @@ -54,6 +54,22 @@ class TestDedent(TestCase): self.assertEqual(expected_string, utils.dedent(input_string)) +class TestStripWhitespace(TestCase): + def test_strip_extra_whitespace(self): + # No text, return no text + self.assertEqual("", utils.strip_extra_whitespace("")) + # If no whitespace is exceeded, should return the same + self.assertEqual("One line\nTwo spaces", utils.strip_extra_whitespace("One line\nTwo spaces")) + # Extra newlines are removed + self.assertEqual("First line\nSecond line", utils.strip_extra_whitespace("First line\n\nSecond line")) + # Extra spaces are removed + self.assertEqual("Too many spaces", utils.strip_extra_whitespace("Too many spaces")) + # "Invisible" extra lines with whitespace are removed + self.assertEqual("First line\nSecond line", utils.strip_extra_whitespace("First line\n \n \nSecond line")) + # Max kwargs are respected + self.assertEqual("First line\n\nSecond line", utils.strip_extra_whitespace("First line\n\nSecond line", max_spacing=1, max_linebreaks=2)) + + class TestListToString(TestCase): """ Default function header from time.py: From 8562fb81677974cc0186327c10162a92bfbeb70e Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Sun, 4 Feb 2024 15:25:56 -0700 Subject: [PATCH 3/4] adds strip back onto appearance, for initial newline --- evennia/objects/objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index cf3b6279de..7cb2186767 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1446,7 +1446,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): str: The final formatted output. """ - return strip_extra_whitespace(appearance) + return strip_extra_whitespace(appearance).strip() def return_appearance(self, looker, **kwargs): """ From 054724cc97cd7e32667547c712da49fea29c5a50 Mon Sep 17 00:00:00 2001 From: Cal Date: Wed, 20 Mar 2024 16:06:42 -0600 Subject: [PATCH 4/4] preserve indentation levels --- evennia/objects/objects.py | 4 ++-- evennia/utils/tests/test_utils.py | 25 +++++++++++++++++-------- evennia/utils/utils.py | 8 ++++---- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 7cb2186767..5a07c27d1b 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -30,7 +30,7 @@ from evennia.utils.utils import ( iter_to_str, lazy_property, make_iter, - strip_extra_whitespace, + compress_whitespace, to_str, variable_from_module, ) @@ -1446,7 +1446,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): str: The final formatted output. """ - return strip_extra_whitespace(appearance).strip() + return compress_whitespace(appearance).strip() def return_appearance(self, looker, **kwargs): """ diff --git a/evennia/utils/tests/test_utils.py b/evennia/utils/tests/test_utils.py index 3d44c08621..1a6e8dd6c8 100644 --- a/evennia/utils/tests/test_utils.py +++ b/evennia/utils/tests/test_utils.py @@ -54,21 +54,30 @@ class TestDedent(TestCase): self.assertEqual(expected_string, utils.dedent(input_string)) -class TestStripWhitespace(TestCase): - def test_strip_extra_whitespace(self): +class TestCompressWhitespace(TestCase): + def test_compress_whitespace(self): # No text, return no text - self.assertEqual("", utils.strip_extra_whitespace("")) + self.assertEqual("", utils.compress_whitespace("")) # If no whitespace is exceeded, should return the same - self.assertEqual("One line\nTwo spaces", utils.strip_extra_whitespace("One line\nTwo spaces")) + self.assertEqual("One line\nTwo spaces", utils.compress_whitespace("One line\nTwo spaces")) # Extra newlines are removed - self.assertEqual("First line\nSecond line", utils.strip_extra_whitespace("First line\n\nSecond line")) + self.assertEqual("First line\nSecond line", utils.compress_whitespace("First line\n\nSecond line")) # Extra spaces are removed - self.assertEqual("Too many spaces", utils.strip_extra_whitespace("Too many spaces")) + self.assertEqual("Too many spaces", utils.compress_whitespace("Too many spaces")) # "Invisible" extra lines with whitespace are removed - self.assertEqual("First line\nSecond line", utils.strip_extra_whitespace("First line\n \n \nSecond line")) + self.assertEqual("First line\nSecond line", utils.compress_whitespace("First line\n \n \nSecond line")) # Max kwargs are respected - self.assertEqual("First line\n\nSecond line", utils.strip_extra_whitespace("First line\n\nSecond line", max_spacing=1, max_linebreaks=2)) + self.assertEqual("First line\n\nSecond line", utils.compress_whitespace("First line\n\nSecond line", max_spacing=1, max_linebreaks=2)) + def test_preserve_indents(self): + """Ensure that indentation spacing is preserved.""" + indented = """\ +Hanging Indents + they're great + let's keep them\ +""" + # since there is no doubled-up spacing besides indents, input should equal output + self.assertEqual(indented, utils.compress_whitespace(indented)) class TestListToString(TestCase): """ diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 5ababa62d1..ce43e69caf 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -474,14 +474,14 @@ iter_to_string = iter_to_str re_empty = re.compile("\n\s*\n") -def strip_extra_whitespace(text, max_linebreaks=1, max_spacing=2): +def compress_whitespace(text, max_linebreaks=1, max_spacing=2): """ Removes extra sequential whitespace in a block of text. This will also remove any trailing whitespace at the end. Args: text (str): A string which may contain excess internal whitespace. - + Keyword args: max_linebreaks (int): How many linebreak characters are allowed to occur in a row. max_spacing (int): How many spaces are allowed to occur in a row. @@ -492,11 +492,11 @@ def strip_extra_whitespace(text, max_linebreaks=1, max_spacing=2): # this allows the blank-line compression to eliminate them if needed text = re_empty.sub("\n\n", text) # replace groups of extra spaces with the maximum number of spaces - text = re.sub(f" {{{max_spacing},}}", " "*max_spacing, text) + text = re.sub(f"(?<=\S) {{{max_spacing},}}", " "*max_spacing, text) # replace groups of extra newlines with the maximum number of newlines text = re.sub(f"\n{{{max_linebreaks},}}", "\n"*max_linebreaks, text) return text - + def wildcard_to_regexp(instring): """