Added m_len and made ANSIString ignore MXP.
This commit is contained in:
parent
b9e560660e
commit
571b1d5fad
3 changed files with 58 additions and 16 deletions
|
|
@ -82,7 +82,7 @@ class ANSIParser(object):
|
||||||
"""
|
"""
|
||||||
return self.ansi_map.get(ansimatch.group(), "")
|
return self.ansi_map.get(ansimatch.group(), "")
|
||||||
|
|
||||||
def sub_xterm256(self, rgbmatch):
|
def sub_xterm256(self, rgbmatch, convert=False):
|
||||||
"""
|
"""
|
||||||
This is a replacer method called by `re.sub` with the matched
|
This is a replacer method called by `re.sub` with the matched
|
||||||
tag. It must return the correct ansi sequence.
|
tag. It must return the correct ansi sequence.
|
||||||
|
|
@ -102,7 +102,7 @@ class ANSIParser(object):
|
||||||
else:
|
else:
|
||||||
red, green, blue = int(rgbtag[0]), int(rgbtag[1]), int(rgbtag[2])
|
red, green, blue = int(rgbtag[0]), int(rgbtag[1]), int(rgbtag[2])
|
||||||
|
|
||||||
if self.do_xterm256:
|
if convert:
|
||||||
colval = 16 + (red * 36) + (green * 6) + blue
|
colval = 16 + (red * 36) + (green * 6) + blue
|
||||||
#print "RGB colours:", red, green, blue
|
#print "RGB colours:", red, green, blue
|
||||||
return "\033[%s8;5;%s%s%sm" % (3 + int(background), colval/100, (colval % 100)/10, colval%10)
|
return "\033[%s8;5;%s%s%sm" % (3 + int(background), colval/100, (colval % 100)/10, colval%10)
|
||||||
|
|
@ -201,15 +201,16 @@ class ANSIParser(object):
|
||||||
if cachekey in _PARSE_CACHE:
|
if cachekey in _PARSE_CACHE:
|
||||||
return _PARSE_CACHE[cachekey]
|
return _PARSE_CACHE[cachekey]
|
||||||
|
|
||||||
self.do_xterm256 = xterm256
|
def do_xterm256(part):
|
||||||
self.do_mxp = mxp
|
return self.sub_xterm256(part, xterm256)
|
||||||
|
|
||||||
in_string = utils.to_str(string)
|
in_string = utils.to_str(string)
|
||||||
|
|
||||||
# do string replacement
|
# do string replacement
|
||||||
parsed_string = ""
|
parsed_string = ""
|
||||||
parts = self.ansi_escapes.split(in_string) + [" "]
|
parts = self.ansi_escapes.split(in_string) + [" "]
|
||||||
for part, sep in zip(parts[::2], parts[1::2]):
|
for part, sep in zip(parts[::2], parts[1::2]):
|
||||||
pstring = self.xterm256_sub.sub(self.sub_xterm256, part)
|
pstring = self.xterm256_sub.sub(do_xterm256, part)
|
||||||
pstring = self.ansi_sub.sub(self.sub_ansi, pstring)
|
pstring = self.ansi_sub.sub(self.sub_ansi, pstring)
|
||||||
parsed_string += "%s%s" % (pstring, sep[0].strip())
|
parsed_string += "%s%s" % (pstring, sep[0].strip())
|
||||||
|
|
||||||
|
|
@ -221,8 +222,7 @@ class ANSIParser(object):
|
||||||
# inserted in string)
|
# inserted in string)
|
||||||
return self.strip_raw_codes(parsed_string)
|
return self.strip_raw_codes(parsed_string)
|
||||||
|
|
||||||
|
# cache and crop old cache
|
||||||
# cache and crop old cache
|
|
||||||
_PARSE_CACHE[cachekey] = parsed_string
|
_PARSE_CACHE[cachekey] = parsed_string
|
||||||
if len(_PARSE_CACHE) > _PARSE_CACHE_SIZE:
|
if len(_PARSE_CACHE) > _PARSE_CACHE_SIZE:
|
||||||
_PARSE_CACHE.popitem(last=False)
|
_PARSE_CACHE.popitem(last=False)
|
||||||
|
|
@ -342,10 +342,6 @@ class ANSIParser(object):
|
||||||
ansi_re = r"\033\[[0-9;]+m"
|
ansi_re = r"\033\[[0-9;]+m"
|
||||||
ansi_regex = re.compile(ansi_re)
|
ansi_regex = re.compile(ansi_re)
|
||||||
|
|
||||||
# merged regex for both ansi and mxp, for use by ansistring
|
|
||||||
mxp_tags = r'\{lc.*?\{lt|\{le'
|
|
||||||
tags_regex = re.compile("%s|%s" % (ansi_re, mxp_tags), re.DOTALL)
|
|
||||||
|
|
||||||
# escapes - these double-chars will be replaced with a single
|
# escapes - these double-chars will be replaced with a single
|
||||||
# instance of each
|
# instance of each
|
||||||
ansi_escapes = re.compile(r"(%s)" % "|".join(ANSI_ESCAPES), re.DOTALL)
|
ansi_escapes = re.compile(r"(%s)" % "|".join(ANSI_ESCAPES), re.DOTALL)
|
||||||
|
|
@ -527,7 +523,7 @@ class ANSIString(unicode):
|
||||||
decoded = True
|
decoded = True
|
||||||
if not decoded:
|
if not decoded:
|
||||||
# Completely new ANSI String
|
# Completely new ANSI String
|
||||||
clean_string = to_unicode(parser.parse_ansi(string, strip_ansi=True))
|
clean_string = to_unicode(parser.parse_ansi(string, strip_ansi=True, mxp=True))
|
||||||
string = parser.parse_ansi(string, xterm256=True, mxp=True)
|
string = parser.parse_ansi(string, xterm256=True, mxp=True)
|
||||||
elif clean_string is not None:
|
elif clean_string is not None:
|
||||||
# We have an explicit clean string.
|
# We have an explicit clean string.
|
||||||
|
|
@ -781,8 +777,7 @@ class ANSIString(unicode):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
code_indexes = []
|
code_indexes = []
|
||||||
#for match in self.parser.ansi_regex.finditer(self._raw_string):
|
for match in self.parser.ansi_regex.finditer(self._raw_string):
|
||||||
for match in self.parser.tags_regex.finditer(self._raw_string):
|
|
||||||
code_indexes.extend(range(match.start(), match.end()))
|
code_indexes.extend(range(match.start(), match.end()))
|
||||||
if not code_indexes:
|
if not code_indexes:
|
||||||
# Plain string, no ANSI codes.
|
# Plain string, no ANSI codes.
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from ansi import ANSIString
|
from .ansi import ANSIString
|
||||||
import utils
|
from evennia import utils
|
||||||
|
|
||||||
|
|
||||||
class ANSIStringTestCase(TestCase):
|
class ANSIStringTestCase(TestCase):
|
||||||
|
|
@ -121,6 +121,20 @@ class ANSIStringTestCase(TestCase):
|
||||||
result = u'\x1b[1m\x1b[32mTest\x1b[0m'
|
result = u'\x1b[1m\x1b[32mTest\x1b[0m'
|
||||||
self.checker(target.capitalize(), result, u'Test')
|
self.checker(target.capitalize(), result, u'Test')
|
||||||
|
|
||||||
|
def test_mxp_agnostic(self):
|
||||||
|
"""
|
||||||
|
Make sure MXP tags are not treated like ANSI codes, but normal text.
|
||||||
|
"""
|
||||||
|
mxp1 = "{lclook{ltat{le"
|
||||||
|
mxp2 = "Start to {lclook here{ltclick somewhere here{le first"
|
||||||
|
self.assertEqual(15, len(ANSIString(mxp1)))
|
||||||
|
self.assertEqual(53, len(ANSIString(mxp2)))
|
||||||
|
# These would indicate an issue with the tables.
|
||||||
|
self.assertEqual(len(ANSIString(mxp1)), len(ANSIString(mxp1).split("\n")[0]))
|
||||||
|
self.assertEqual(len(ANSIString(mxp2)), len(ANSIString(mxp2).split("\n")[0]))
|
||||||
|
self.assertEqual(mxp1, ANSIString(mxp1))
|
||||||
|
self.assertEqual(mxp2, unicode(ANSIString(mxp2)))
|
||||||
|
|
||||||
|
|
||||||
class TestIsIter(TestCase):
|
class TestIsIter(TestCase):
|
||||||
def test_is_iter(self):
|
def test_is_iter(self):
|
||||||
|
|
@ -177,3 +191,26 @@ class TestListToString(TestCase):
|
||||||
self.assertEqual('1, 2 and 3', utils.list_to_string([1,2,3]))
|
self.assertEqual('1, 2 and 3', utils.list_to_string([1,2,3]))
|
||||||
self.assertEqual('"1", "2" and "3"', utils.list_to_string([1,2,3], endsep="and", addquote=True))
|
self.assertEqual('"1", "2" and "3"', utils.list_to_string([1,2,3], endsep="and", addquote=True))
|
||||||
|
|
||||||
|
|
||||||
|
class TestMLen(TestCase):
|
||||||
|
"""
|
||||||
|
Verifies that m_len behaves like len in all situations except those
|
||||||
|
where MXP may be involved.
|
||||||
|
"""
|
||||||
|
def test_non_mxp_string(self):
|
||||||
|
self.assertEqual(utils.m_len('Test_string'), 11)
|
||||||
|
|
||||||
|
def test_mxp_string(self):
|
||||||
|
self.assertEqual(utils.m_len('{lclook{ltat{le'), 2)
|
||||||
|
|
||||||
|
def test_mxp_ansi_string(self):
|
||||||
|
self.assertEqual(utils.m_len(ANSIString('{lcl{gook{ltat{le{n')), 2)
|
||||||
|
|
||||||
|
def test_non_mxp_ansi_string(self):
|
||||||
|
self.assertEqual(utils.m_len(ANSIString('{gHello{n')), 5)
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
self.assertEqual(utils.m_len([None, None]), 2)
|
||||||
|
|
||||||
|
def test_dict(self):
|
||||||
|
self.assertEqual(utils.m_len({'hello': True, 'Goodbye': False}), 2)
|
||||||
|
|
|
||||||
|
|
@ -1245,3 +1245,13 @@ def calledby(callerdepth=1):
|
||||||
return "[called by '%s': %s:%s %s]" % (frame[3], path, frame[2], frame[4])
|
return "[called by '%s': %s:%s %s]" % (frame[3], path, frame[2], frame[4])
|
||||||
|
|
||||||
|
|
||||||
|
def m_len(target):
|
||||||
|
"""
|
||||||
|
Provides length checking for strings with MXP patterns, and falls
|
||||||
|
back to normal len for other objects.
|
||||||
|
"""
|
||||||
|
# Would create circular import if in module root.
|
||||||
|
from evennia.utils.ansi import ANSI_PARSER
|
||||||
|
if inherits_from(target, basestring):
|
||||||
|
return len(ANSI_PARSER.strip_mxp(target))
|
||||||
|
return len(target)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue