Make utils.justify handle ANSIString properly. Resolve #2986
This commit is contained in:
parent
20520d99d5
commit
da03b22e1c
4 changed files with 52 additions and 12 deletions
|
|
@ -118,7 +118,6 @@ from copy import copy, deepcopy
|
||||||
from textwrap import TextWrapper
|
from textwrap import TextWrapper
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from evennia.utils.ansi import ANSIString
|
from evennia.utils.ansi import ANSIString
|
||||||
from evennia.utils.utils import display_len as d_len
|
from evennia.utils.utils import display_len as d_len
|
||||||
from evennia.utils.utils import is_iter, justify
|
from evennia.utils.utils import is_iter, justify
|
||||||
|
|
|
||||||
|
|
@ -330,3 +330,20 @@ class TestEvTable(EvenniaTestCase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._validate(expected, str(table))
|
self._validate(expected, str(table))
|
||||||
|
|
||||||
|
def test_color_transfer(self):
|
||||||
|
"""
|
||||||
|
Testing https://github.com/evennia/evennia/issues/2986
|
||||||
|
|
||||||
|
EvTable swallowing color tags.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from evennia.utils.ansi import ANSI_CYAN, ANSI_RED
|
||||||
|
|
||||||
|
row1 = "|cAn entire colored row|n"
|
||||||
|
row2 = "A single |rred|n word"
|
||||||
|
|
||||||
|
table = evtable.EvTable(table=[[row1, row2]])
|
||||||
|
|
||||||
|
self.assertIn(ANSI_RED, str(table))
|
||||||
|
self.assertIn(ANSI_CYAN, str(table))
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,11 @@ from datetime import datetime, timedelta
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from parameterized import parameterized
|
|
||||||
from twisted.internet import task
|
|
||||||
|
|
||||||
from evennia.utils import utils
|
from evennia.utils import utils
|
||||||
from evennia.utils.ansi import ANSIString
|
from evennia.utils.ansi import ANSIString
|
||||||
from evennia.utils.test_resources import BaseEvenniaTest
|
from evennia.utils.test_resources import BaseEvenniaTest
|
||||||
|
from parameterized import parameterized
|
||||||
|
from twisted.internet import task
|
||||||
|
|
||||||
|
|
||||||
class TestIsIter(TestCase):
|
class TestIsIter(TestCase):
|
||||||
|
|
@ -759,3 +758,17 @@ class TestJustify(TestCase):
|
||||||
def test_center_justify_small(self, width, expected):
|
def test_center_justify_small(self, width, expected):
|
||||||
result = utils.justify("Task ID", width, align="c", indent=0, fillchar=" ")
|
result = utils.justify("Task ID", width, align="c", indent=0, fillchar=" ")
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
|
def test_justify_ansi(self):
|
||||||
|
"""
|
||||||
|
Justify ansistring
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from evennia.utils.ansi import ANSI_RED
|
||||||
|
|
||||||
|
line = ANSIString("This is a |rred|n word")
|
||||||
|
|
||||||
|
result = utils.justify(line, align="c", width=30)
|
||||||
|
|
||||||
|
self.assertIn(ANSI_RED, str(result))
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,12 @@ from django.core.validators import validate_email as django_validate_email
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
from evennia.utils import logger
|
||||||
from simpleeval import simple_eval
|
from simpleeval import simple_eval
|
||||||
from twisted.internet import reactor, threads
|
from twisted.internet import reactor, threads
|
||||||
from twisted.internet.defer import returnValue # noqa - used as import target
|
from twisted.internet.defer import returnValue # noqa - used as import target
|
||||||
from twisted.internet.task import deferLater
|
from twisted.internet.task import deferLater
|
||||||
|
|
||||||
from evennia.utils import logger
|
|
||||||
|
|
||||||
_MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE
|
_MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE
|
||||||
_EVENNIA_DIR = settings.EVENNIA_DIR
|
_EVENNIA_DIR = settings.EVENNIA_DIR
|
||||||
_GAME_DIR = settings.GAME_DIR
|
_GAME_DIR = settings.GAME_DIR
|
||||||
|
|
@ -51,6 +50,7 @@ ENCODINGS = settings.ENCODINGS
|
||||||
_TASK_HANDLER = None
|
_TASK_HANDLER = None
|
||||||
_TICKER_HANDLER = None
|
_TICKER_HANDLER = None
|
||||||
_STRIP_UNSAFE_TOKENS = None
|
_STRIP_UNSAFE_TOKENS = None
|
||||||
|
_ANSISTRING = None
|
||||||
|
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
|
|
@ -236,6 +236,13 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
|
||||||
justified (str): The justified and indented block of text.
|
justified (str): The justified and indented block of text.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# we need to retain ansitrings
|
||||||
|
global _ANSISTRING
|
||||||
|
if not _ANSISTRING:
|
||||||
|
from evennia.utils.ansi import ANSIString as _ANSISTRING
|
||||||
|
|
||||||
|
is_ansi = isinstance(text, _ANSISTRING)
|
||||||
|
lb = _ANSISTRING("\n") if is_ansi else "\n"
|
||||||
|
|
||||||
def _process_line(line):
|
def _process_line(line):
|
||||||
"""
|
"""
|
||||||
|
|
@ -244,7 +251,9 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
|
||||||
distribute odd spaces to one of the gaps.
|
distribute odd spaces to one of the gaps.
|
||||||
"""
|
"""
|
||||||
line_rest = width - (wlen + ngaps)
|
line_rest = width - (wlen + ngaps)
|
||||||
gap = " " # minimum gap between words
|
|
||||||
|
gap = _ANSISTRING(" ") if is_ansi else " "
|
||||||
|
|
||||||
if line_rest > 0:
|
if line_rest > 0:
|
||||||
if align == "l":
|
if align == "l":
|
||||||
if line[-1] == "\n\n":
|
if line[-1] == "\n\n":
|
||||||
|
|
@ -284,23 +293,25 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
|
||||||
else:
|
else:
|
||||||
line = crop(line, width=width, suffix="")
|
line = crop(line, width=width, suffix="")
|
||||||
abs_lines.append(line)
|
abs_lines.append(line)
|
||||||
return "\n".join(abs_lines)
|
return lb.join(abs_lines)
|
||||||
|
|
||||||
# all other aligns requires splitting into paragraphs and words
|
# all other aligns requires splitting into paragraphs and words
|
||||||
|
|
||||||
# split into paragraphs and words
|
# split into paragraphs and words
|
||||||
paragraphs = re.split("\n\s*?\n", text, re.MULTILINE)
|
paragraphs = [text] # re.split("\n\s*?\n", text, re.MULTILINE)
|
||||||
words = []
|
words = []
|
||||||
for ip, paragraph in enumerate(paragraphs):
|
for ip, paragraph in enumerate(paragraphs):
|
||||||
if ip > 0:
|
if ip > 0:
|
||||||
words.append(("\n", 0))
|
words.append(("\n", 0))
|
||||||
words.extend((word, len(word)) for word in paragraph.split())
|
words.extend((word, len(word)) for word in paragraph.split())
|
||||||
ngaps, wlen, line = 0, 0, []
|
|
||||||
|
|
||||||
if not words:
|
if not words:
|
||||||
# Just whitespace!
|
# Just whitespace!
|
||||||
return sp * width
|
return sp * width
|
||||||
|
|
||||||
|
ngaps = 0
|
||||||
|
wlen = 0
|
||||||
|
line = []
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
while words:
|
while words:
|
||||||
|
|
@ -328,8 +339,8 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
|
||||||
if line: # catch any line left behind
|
if line: # catch any line left behind
|
||||||
lines.append(_process_line(line))
|
lines.append(_process_line(line))
|
||||||
indentstring = sp * indent
|
indentstring = sp * indent
|
||||||
out = "\n".join([indentstring + line for line in lines])
|
out = lb.join([indentstring + line for line in lines])
|
||||||
return "\n".join([indentstring + line for line in lines])
|
return lb.join([indentstring + line for line in lines])
|
||||||
|
|
||||||
|
|
||||||
def columnize(string, columns=2, spacing=4, align="l", width=None):
|
def columnize(string, columns=2, spacing=4, align="l", width=None):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue