diff --git a/evennia/utils/ansi.py b/evennia/utils/ansi.py index 0427a70d4b..b02f053438 100644 --- a/evennia/utils/ansi.py +++ b/evennia/utils/ansi.py @@ -977,6 +977,29 @@ class ANSIString(with_metaclass(ANSIMeta, unicode)): break return s + def __mul__(self, other): + """ + Multiplication method. Implemented for performance reasons. + + """ + if not isinstance(other, int): + return NotImplemented + raw_string = self._raw_string * other + clean_string = self._clean_string * other + code_indexes = self._code_indexes[:] + char_indexes = self._char_indexes[:] + for i in range(1, other + 1): + code_indexes.extend( + self._shifter(self._code_indexes, i * len(self._raw_string))) + char_indexes.extend( + self._shifter(self._char_indexes, i * len(self._raw_string))) + return ANSIString( + raw_string, code_indexes=code_indexes, char_indexes=char_indexes, + clean_string=clean_string) + + def __rmul__(self, other): + return self.__mul__(other) + def split(self, by=None, maxsplit=-1): """ Stolen from PyPy's pure Python string implementation, tweaked for @@ -1010,29 +1033,6 @@ class ANSIString(with_metaclass(ANSIMeta, unicode)): return [part for part in res if part != ""] return res - def __mul__(self, other): - """ - Multiplication method. Implemented for performance reasons. - - """ - if not isinstance(other, int): - return NotImplemented - raw_string = self._raw_string * other - clean_string = self._clean_string * other - code_indexes = self._code_indexes[:] - char_indexes = self._char_indexes[:] - for i in range(1, other + 1): - code_indexes.extend( - self._shifter(self._code_indexes, i * len(self._raw_string))) - char_indexes.extend( - self._shifter(self._char_indexes, i * len(self._raw_string))) - return ANSIString( - raw_string, code_indexes=code_indexes, char_indexes=char_indexes, - clean_string=clean_string) - - def __rmul__(self, other): - return self.__mul__(other) - def rsplit(self, by=None, maxsplit=-1): """ Stolen from PyPy's pure Python string implementation, tweaked for @@ -1066,6 +1066,88 @@ class ANSIString(with_metaclass(ANSIMeta, unicode)): return [part for part in res if part != ""] return res + def strip(self, chars=None): + """ + Strip from both ends, taking ANSI markers into account. + """ + clean = self._clean_string + raw = self._raw_string + + # count continuous sequence of chars from left and right + nlen = len(clean) + nlstripped = nlen - len(clean.lstrip(chars)) + nrstripped = nlen - len(clean.rstrip(chars)) + + # within the stripped regions, only retain parts of the raw + # string *not* matching the clean string (these are ansi/mxp tags) + lstripped = "" + ic, ir1 = 0, 0 + while nlstripped: + if ic >= nlstripped: + break + elif raw[ir1] != clean[ic]: + lstripped += raw[ir1] + else: + ic += 1 + ir1 += 1 + rstripped = "" + ic, ir2 = nlen-1, len(raw)-1 + while nrstripped: + if nlen - ic > nrstripped: + break + elif raw[ir2] != clean[ic]: + rstripped += raw[ir2] + else: + ic -= 1 + ir2 -= 1 + rstripped = rstripped[::-1] + return ANSIString(lstripped + raw[ir1:ir2+1] + rstripped) + + + def lstrip(self, chars=None): + """ + Strip from the left, taking ANSI markers into account. + """ + clean = self._clean_string + raw = self._raw_string + + # count continuous sequence of chars from left and right + nlen = len(clean) + nlstripped = nlen - len(clean.lstrip(chars)) + # within the stripped regions, only retain parts of the raw + # string *not* matching the clean string (these are ansi/mxp tags) + lstripped = "" + ic, ir1 = 0, 0 + while nlstripped: + if ic >= nlstripped: + break + elif raw[ir1] != clean[ic]: + lstripped += raw[ir1] + else: + ic += 1 + ir1 += 1 + return ANSIString(lstripped + raw[ir1:]) + + def rstrip(self, chars=None): + """ + Strip from the right, taking ANSI markers into account. + """ + clean = self._clean_string + raw = self._raw_string + nlen = len(clean) + nrstripped = nlen - len(clean.rstrip(chars)) + rstripped = "" + ic, ir2 = nlen-1, len(raw)-1 + while nrstripped: + if nlen - ic > nrstripped: + break + elif raw[ir2] != clean[ic]: + rstripped += raw[ir2] + else: + ic -= 1 + ir2 -= 1 + return ANSIString(raw[:ir2+1] + rstripped) + def join(self, iterable): """ Joins together strings in an iterable. diff --git a/evennia/utils/evtable.py b/evennia/utils/evtable.py index 82941f7caf..b27eb6c3be 100644 --- a/evennia/utils/evtable.py +++ b/evennia/utils/evtable.py @@ -576,7 +576,8 @@ class EvCell(object): hfill_char = self.hfill_char width = self.width if align == "l": - return [(line.lstrip(" ") + " " if line.startswith(" ") and not line.startswith(" ") else line) + hfill_char * (width - m_len(line)) for line in data] + lines= [(line.lstrip(" ") + " " if line.startswith(" ") and not line.startswith(" ") else line) + hfill_char * (width - m_len(line)) for line in data] + return lines elif align == "r": return [hfill_char * (width - m_len(line)) + (" " + line.rstrip(" ") if line.endswith(" ") and not line.endswith(" ") else line) for line in data] else: # center, 'c' diff --git a/evennia/utils/tests.py b/evennia/utils/tests.py index f6f8a93270..247c8ad0b3 100644 --- a/evennia/utils/tests.py +++ b/evennia/utils/tests.py @@ -152,6 +152,17 @@ class ANSIStringTestCase(TestCase): ] self.table_check(c, char_table, code_table) + def test_strip(self): + """ + Test the ansi-aware .strip() methods + """ + a = ANSIString(" |r Test of stuff |b with spaces |n ") + b = ANSIString("|r|b") + self.assertEqual(a.strip(), ANSIString("|rTest of stuff |b with spaces|n")) + self.assertEqual(a.lstrip(), ANSIString("|rTest of stuff |b with spaces |n ")) + self.assertEqual(a.rstrip(), ANSIString(" |r Test of stuff |b with spaces|n")) + self.assertEqual(b.strip(), b) + class TestIsIter(TestCase): def test_is_iter(self):