mirror of
https://github.com/evennia/evennia.git
synced 2026-03-23 00:06:30 +01:00
Fix ANSIString parsing on partial slice from start/end of string. Resolve #2205.
This commit is contained in:
parent
c30e2a4629
commit
558a3d76c7
3 changed files with 81 additions and 4 deletions
|
|
@ -526,6 +526,13 @@ def raw(string):
|
|||
return string.replace("{", "{{").replace("|", "||")
|
||||
|
||||
|
||||
# ------------------------------------------------------------
|
||||
#
|
||||
# ANSIString - ANSI-aware string class
|
||||
#
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
def _spacing_preflight(func):
|
||||
"""
|
||||
This wrapper function is used to do some preflight checks on
|
||||
|
|
@ -896,9 +903,23 @@ class ANSIString(str, metaclass=ANSIMeta):
|
|||
replayed.
|
||||
|
||||
"""
|
||||
slice_indexes = self._char_indexes[slc]
|
||||
char_indexes = self._char_indexes
|
||||
slice_indexes = char_indexes[slc]
|
||||
# If it's the end of the string, we need to append final color codes.
|
||||
if not slice_indexes:
|
||||
# if we find no characters it may be because we are just outside
|
||||
# of the interval, using an open-ended slice. We must replay all
|
||||
# of the escape characters until/after this point.
|
||||
if char_indexes:
|
||||
if slc.start is None and slc.stop is None:
|
||||
# a [:] slice of only escape characters
|
||||
return ANSIString(self._raw_string[slc])
|
||||
if slc.start is None:
|
||||
# this is a [:x] slice
|
||||
return ANSIString(self._raw_string[:char_indexes[0]])
|
||||
if slc.stop is None:
|
||||
# a [x:] slice
|
||||
return ANSIString(self._raw_string[char_indexes[-1] + 1:])
|
||||
return ANSIString("")
|
||||
try:
|
||||
string = self[slc.start or 0]._raw_string
|
||||
|
|
@ -918,7 +939,7 @@ class ANSIString(str, metaclass=ANSIMeta):
|
|||
# raw_string not long enough
|
||||
pass
|
||||
if i is not None:
|
||||
append_tail = self._get_interleving(self._char_indexes.index(i) + 1)
|
||||
append_tail = self._get_interleving(char_indexes.index(i) + 1)
|
||||
else:
|
||||
append_tail = ""
|
||||
return ANSIString(string + append_tail, decoded=True)
|
||||
|
|
|
|||
|
|
@ -165,9 +165,12 @@ def _to_rect(lines):
|
|||
|
||||
def _to_ansi(obj, regexable=False):
|
||||
"convert to ANSIString"
|
||||
if isinstance(obj, str):
|
||||
if isinstance(obj, ANSIString):
|
||||
return obj
|
||||
elif isinstance(obj, str):
|
||||
# since ansi will be parsed twice (here and in the normal ansi send), we have to
|
||||
# escape the |-structure twice.
|
||||
# escape the |-structure twice. TODO: This is tied to the default color-tag syntax
|
||||
# which is not ideal for those wanting to replace/extend it ...
|
||||
obj = _ANSI_ESCAPE.sub(r"||||", obj)
|
||||
if isinstance(obj, dict):
|
||||
return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items())
|
||||
|
|
|
|||
|
|
@ -177,6 +177,59 @@ class ANSIStringTestCase(TestCase):
|
|||
self.assertEqual(a.rstrip(), ANSIString(" |r Test of stuff |b with spaces|n"))
|
||||
self.assertEqual(b.strip(), b)
|
||||
|
||||
def test_regex_search(self):
|
||||
"""
|
||||
Test regex-search in ANSIString - the found position should ignore any ansi-markers
|
||||
"""
|
||||
string = ANSIString(" |r|[b Test ")
|
||||
match = re.search(r"Test", string)
|
||||
self.assertTrue(match)
|
||||
self.assertEqual(match.span(), (3, 7))
|
||||
|
||||
def test_regex_replace(self):
|
||||
"""
|
||||
Inserting text into an ansistring at an index position should ignore
|
||||
the ansi markers but not remove them!
|
||||
|
||||
"""
|
||||
string = ANSIString("A |rTest|n string")
|
||||
match = re.search(r"Test", string)
|
||||
ix1, ix2 = match.span()
|
||||
self.assertEqual((ix1, ix2), (2, 6))
|
||||
|
||||
result = string[:ix1] + "Replacement" + string[ix2:]
|
||||
expected = ANSIString("A |rReplacement|n string")
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_slice_insert(self):
|
||||
"""
|
||||
Inserting a slice should not remove ansi markup (issue #2205)
|
||||
"""
|
||||
string = ANSIString("|rTest|n")
|
||||
split_string = string[:0] + "Test" + string[4:]
|
||||
self.assertEqual(string.raw(), split_string.raw())
|
||||
|
||||
def test_slice_insert_longer(self):
|
||||
"""
|
||||
The ANSIString replays the color code before the split in order to
|
||||
produce a *visually* identical result. The result is a longer string in
|
||||
raw characters, but one which correctly represents the color output.
|
||||
"""
|
||||
string = ANSIString("A bigger |rTest|n of things |bwith more color|n")
|
||||
# from evennia import set_trace;set_trace()
|
||||
split_string = string[:9] + "Test" + string[13:]
|
||||
self.assertEqual(
|
||||
repr((ANSIString("A bigger ")
|
||||
+ ANSIString("|rTest") # note that the |r|n is replayed together on next line
|
||||
+ ANSIString("|r|n of things |bwith more color|n")).raw()),
|
||||
repr(split_string.raw()))
|
||||
|
||||
def test_slice_full(self):
|
||||
string = ANSIString("A bigger |rTest|n of things |bwith more color|n")
|
||||
split_string = string[:]
|
||||
self.assertEqual(string.raw(), split_string.raw())
|
||||
|
||||
|
||||
class TestTextToHTMLparser(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue