Make utils.justify handle ANSIString properly. Resolve #2986

This commit is contained in:
Griatch 2022-11-27 18:29:25 +01:00
parent 20520d99d5
commit da03b22e1c
4 changed files with 52 additions and 12 deletions

View file

@ -118,7 +118,6 @@ from copy import copy, deepcopy
from textwrap import TextWrapper
from django.conf import settings
from evennia.utils.ansi import ANSIString
from evennia.utils.utils import display_len as d_len
from evennia.utils.utils import is_iter, justify

View file

@ -330,3 +330,20 @@ class TestEvTable(EvenniaTestCase):
"""
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))

View file

@ -11,12 +11,11 @@ from datetime import datetime, timedelta
import mock
from django.test import TestCase
from parameterized import parameterized
from twisted.internet import task
from evennia.utils import utils
from evennia.utils.ansi import ANSIString
from evennia.utils.test_resources import BaseEvenniaTest
from parameterized import parameterized
from twisted.internet import task
class TestIsIter(TestCase):
@ -759,3 +758,17 @@ class TestJustify(TestCase):
def test_center_justify_small(self, width, expected):
result = utils.justify("Task ID", width, align="c", indent=0, fillchar=" ")
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))

View file

@ -34,13 +34,12 @@ from django.core.validators import validate_email as django_validate_email
from django.utils import timezone
from django.utils.html import strip_tags
from django.utils.translation import gettext as _
from evennia.utils import logger
from simpleeval import simple_eval
from twisted.internet import reactor, threads
from twisted.internet.defer import returnValue # noqa - used as import target
from twisted.internet.task import deferLater
from evennia.utils import logger
_MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE
_EVENNIA_DIR = settings.EVENNIA_DIR
_GAME_DIR = settings.GAME_DIR
@ -51,6 +50,7 @@ ENCODINGS = settings.ENCODINGS
_TASK_HANDLER = None
_TICKER_HANDLER = None
_STRIP_UNSAFE_TOKENS = None
_ANSISTRING = None
_GA = object.__getattribute__
_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.
"""
# 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):
"""
@ -244,7 +251,9 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
distribute odd spaces to one of the gaps.
"""
line_rest = width - (wlen + ngaps)
gap = " " # minimum gap between words
gap = _ANSISTRING(" ") if is_ansi else " "
if line_rest > 0:
if align == "l":
if line[-1] == "\n\n":
@ -284,23 +293,25 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
else:
line = crop(line, width=width, suffix="")
abs_lines.append(line)
return "\n".join(abs_lines)
return lb.join(abs_lines)
# all other aligns requires splitting 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 = []
for ip, paragraph in enumerate(paragraphs):
if ip > 0:
words.append(("\n", 0))
words.extend((word, len(word)) for word in paragraph.split())
ngaps, wlen, line = 0, 0, []
if not words:
# Just whitespace!
return sp * width
ngaps = 0
wlen = 0
line = []
lines = []
while words:
@ -328,8 +339,8 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
if line: # catch any line left behind
lines.append(_process_line(line))
indentstring = sp * indent
out = "\n".join([indentstring + line for line in lines])
return "\n".join([indentstring + line for line in lines])
out = lb.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):