Resolve merge conflicts
This commit is contained in:
commit
28bff4ddde
4 changed files with 98 additions and 15 deletions
|
|
@ -76,6 +76,7 @@ without arguments starts a full interactive Python console.
|
||||||
to `spawn` command to extract the raw prototype dict for manual editing.
|
to `spawn` command to extract the raw prototype dict for manual editing.
|
||||||
- `list_to_string` is now `iter_to_string` (but old name still works as legacy alias). It will
|
- `list_to_string` is now `iter_to_string` (but old name still works as legacy alias). It will
|
||||||
now accept any input, including generators and single values.
|
now accept any input, including generators and single values.
|
||||||
|
- EvTable should now correctly handle columns with wider asian-characters in them.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ appear on both sides of the table string.
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from textwrap import TextWrapper
|
from textwrap import TextWrapper
|
||||||
from copy import deepcopy, copy
|
from copy import deepcopy, copy
|
||||||
from evennia.utils.utils import m_len, is_iter
|
from evennia.utils.utils import is_iter, display_len as d_len
|
||||||
from evennia.utils.ansi import ANSIString
|
from evennia.utils.ansi import ANSIString
|
||||||
|
|
||||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||||
|
|
@ -231,7 +231,7 @@ class ANSITextWrapper(TextWrapper):
|
||||||
indent = self.initial_indent
|
indent = self.initial_indent
|
||||||
|
|
||||||
# Maximum width for this line.
|
# Maximum width for this line.
|
||||||
width = self.width - m_len(indent)
|
width = self.width - d_len(indent)
|
||||||
|
|
||||||
# First chunk on line is whitespace -- drop it, unless this
|
# First chunk on line is whitespace -- drop it, unless this
|
||||||
# is the very beginning of the text (ie. no lines started yet).
|
# is the very beginning of the text (ie. no lines started yet).
|
||||||
|
|
@ -239,7 +239,7 @@ class ANSITextWrapper(TextWrapper):
|
||||||
del chunks[-1]
|
del chunks[-1]
|
||||||
|
|
||||||
while chunks:
|
while chunks:
|
||||||
l = m_len(chunks[-1])
|
l = d_len(chunks[-1])
|
||||||
|
|
||||||
# Can at least squeeze this chunk onto the current line.
|
# Can at least squeeze this chunk onto the current line.
|
||||||
if cur_len + l <= width:
|
if cur_len + l <= width:
|
||||||
|
|
@ -252,7 +252,7 @@ class ANSITextWrapper(TextWrapper):
|
||||||
|
|
||||||
# The current line is full, and the next chunk is too big to
|
# The current line is full, and the next chunk is too big to
|
||||||
# fit on *any* line (not just this one).
|
# fit on *any* line (not just this one).
|
||||||
if chunks and m_len(chunks[-1]) > width:
|
if chunks and d_len(chunks[-1]) > width:
|
||||||
self._handle_long_word(chunks, cur_line, cur_len, width)
|
self._handle_long_word(chunks, cur_line, cur_len, width)
|
||||||
|
|
||||||
# If the last chunk on this line is all whitespace, drop it.
|
# If the last chunk on this line is all whitespace, drop it.
|
||||||
|
|
@ -442,7 +442,7 @@ class EvCell(object):
|
||||||
self.valign = kwargs.get("valign", "c")
|
self.valign = kwargs.get("valign", "c")
|
||||||
|
|
||||||
self.data = self._split_lines(_to_ansi(data))
|
self.data = self._split_lines(_to_ansi(data))
|
||||||
self.raw_width = max(m_len(line) for line in self.data)
|
self.raw_width = max(d_len(line) for line in self.data)
|
||||||
self.raw_height = len(self.data)
|
self.raw_height = len(self.data)
|
||||||
|
|
||||||
# this is extra trimming required for cels in the middle of a table only
|
# this is extra trimming required for cels in the middle of a table only
|
||||||
|
|
@ -481,9 +481,9 @@ class EvCell(object):
|
||||||
width (int): The width to crop `text` to.
|
width (int): The width to crop `text` to.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if m_len(text) > width:
|
if d_len(text) > width:
|
||||||
crop_string = self.crop_string
|
crop_string = self.crop_string
|
||||||
return text[: width - m_len(crop_string)] + crop_string
|
return text[: width - d_len(crop_string)] + crop_string
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def _reformat(self):
|
def _reformat(self):
|
||||||
|
|
@ -524,7 +524,7 @@ class EvCell(object):
|
||||||
width = self.width
|
width = self.width
|
||||||
adjusted_data = []
|
adjusted_data = []
|
||||||
for line in data:
|
for line in data:
|
||||||
if 0 < width < m_len(line):
|
if 0 < width < d_len(line):
|
||||||
# replace_whitespace=False, expand_tabs=False is a
|
# replace_whitespace=False, expand_tabs=False is a
|
||||||
# fix for ANSIString not supporting expand_tabs/translate
|
# fix for ANSIString not supporting expand_tabs/translate
|
||||||
adjusted_data.extend(
|
adjusted_data.extend(
|
||||||
|
|
@ -567,7 +567,7 @@ class EvCell(object):
|
||||||
text (str): Centered text.
|
text (str): Centered text.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
excess = width - m_len(text)
|
excess = width - d_len(text)
|
||||||
if excess <= 0:
|
if excess <= 0:
|
||||||
return text
|
return text
|
||||||
if excess % 2:
|
if excess % 2:
|
||||||
|
|
@ -606,13 +606,13 @@ class EvCell(object):
|
||||||
if line.startswith(" ") and not line.startswith(" ")
|
if line.startswith(" ") and not line.startswith(" ")
|
||||||
else line
|
else line
|
||||||
)
|
)
|
||||||
+ hfill_char * (width - m_len(line))
|
+ hfill_char * (width - d_len(line))
|
||||||
for line in data
|
for line in data
|
||||||
]
|
]
|
||||||
return lines
|
return lines
|
||||||
elif align == "r":
|
elif align == "r":
|
||||||
return [
|
return [
|
||||||
hfill_char * (width - m_len(line))
|
hfill_char * (width - d_len(line))
|
||||||
+ (
|
+ (
|
||||||
" " + line.rstrip(" ")
|
" " + line.rstrip(" ")
|
||||||
if line.endswith(" ") and not line.endswith(" ")
|
if line.endswith(" ") and not line.endswith(" ")
|
||||||
|
|
@ -753,7 +753,7 @@ class EvCell(object):
|
||||||
natural_width (int): Width of cell.
|
natural_width (int): Width of cell.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return m_len(self.formatted[0]) # if self.formatted else 0
|
return d_len(self.formatted[0]) # if self.formatted else 0
|
||||||
|
|
||||||
def replace_data(self, data, **kwargs):
|
def replace_data(self, data, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -768,7 +768,7 @@ class EvCell(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.data = self._split_lines(_to_ansi(data))
|
self.data = self._split_lines(_to_ansi(data))
|
||||||
self.raw_width = max(m_len(line) for line in self.data)
|
self.raw_width = max(d_len(line) for line in self.data)
|
||||||
self.raw_height = len(self.data)
|
self.raw_height = len(self.data)
|
||||||
self.reformat(**kwargs)
|
self.reformat(**kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,58 @@ class TestMLen(TestCase):
|
||||||
self.assertEqual(utils.m_len({"hello": True, "Goodbye": False}), 2)
|
self.assertEqual(utils.m_len({"hello": True, "Goodbye": False}), 2)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDisplayLen(TestCase):
|
||||||
|
"""
|
||||||
|
Verifies that display_len behaves like m_len in all situations except those
|
||||||
|
where asian characters are involved.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_non_mxp_string(self):
|
||||||
|
self.assertEqual(utils.display_len("Test_string"), 11)
|
||||||
|
|
||||||
|
def test_mxp_string(self):
|
||||||
|
self.assertEqual(utils.display_len("|lclook|ltat|le"), 2)
|
||||||
|
|
||||||
|
def test_mxp_ansi_string(self):
|
||||||
|
self.assertEqual(utils.display_len(ANSIString("|lcl|gook|ltat|le|n")), 2)
|
||||||
|
|
||||||
|
def test_non_mxp_ansi_string(self):
|
||||||
|
self.assertEqual(utils.display_len(ANSIString("|gHello|n")), 5)
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
self.assertEqual(utils.display_len([None, None]), 2)
|
||||||
|
|
||||||
|
def test_dict(self):
|
||||||
|
self.assertEqual(utils.display_len({"hello": True, "Goodbye": False}), 2)
|
||||||
|
|
||||||
|
def test_east_asian(self):
|
||||||
|
self.assertEqual(utils.display_len("서서서"), 6)
|
||||||
|
|
||||||
|
|
||||||
|
class TestANSIString(TestCase):
|
||||||
|
"""
|
||||||
|
Verifies that ANSIString's string-API works as intended.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.example_raw = "|relectric |cboogaloo|n"
|
||||||
|
self.example_ansi = ANSIString(self.example_raw)
|
||||||
|
self.example_str = "electric boogaloo"
|
||||||
|
self.example_output = "\x1b[1m\x1b[31melectric \x1b[1m\x1b[36mboogaloo\x1b[0m"
|
||||||
|
|
||||||
|
def test_length(self):
|
||||||
|
self.assertEqual(len(self.example_ansi), 17)
|
||||||
|
|
||||||
|
def test_clean(self):
|
||||||
|
self.assertEqual(self.example_ansi.clean(), self.example_str)
|
||||||
|
|
||||||
|
def test_raw(self):
|
||||||
|
self.assertEqual(self.example_ansi.raw(), self.example_output)
|
||||||
|
|
||||||
|
def test_format(self):
|
||||||
|
self.assertEqual(f"{self.example_ansi:0<20}", self.example_output + "000")
|
||||||
|
|
||||||
|
|
||||||
class TestTimeformat(TestCase):
|
class TestTimeformat(TestCase):
|
||||||
"""
|
"""
|
||||||
Default function header from utils.py:
|
Default function header from utils.py:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ be of use when designing your own game.
|
||||||
import os
|
import os
|
||||||
import gc
|
import gc
|
||||||
import sys
|
import sys
|
||||||
import copy
|
import copy
|
||||||
import types
|
import types
|
||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
|
|
@ -20,6 +20,7 @@ import traceback
|
||||||
import importlib
|
import importlib
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import importlib.machinery
|
import importlib.machinery
|
||||||
|
from unicodedata import east_asian_width
|
||||||
from twisted.internet.task import deferLater
|
from twisted.internet.task import deferLater
|
||||||
from twisted.internet.defer import returnValue # noqa - used as import target
|
from twisted.internet.defer import returnValue # noqa - used as import target
|
||||||
from os.path import join as osjoin
|
from os.path import join as osjoin
|
||||||
|
|
@ -2002,7 +2003,7 @@ def m_len(target):
|
||||||
back to normal len for other objects.
|
back to normal len for other objects.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
target (string): A string with potential MXP components
|
target (str): A string with potential MXP components
|
||||||
to search.
|
to search.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -2017,6 +2018,35 @@ def m_len(target):
|
||||||
return len(target)
|
return len(target)
|
||||||
|
|
||||||
|
|
||||||
|
def display_len(target):
|
||||||
|
"""
|
||||||
|
Calculate the 'visible width' of text. This is not necessarily the same as the
|
||||||
|
number of characters in the case of certain asian characters. This will also
|
||||||
|
strip MXP patterns.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target (string): A string with potential MXP components
|
||||||
|
to search.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
int: The visible width of the target.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Would create circular import if in module root.
|
||||||
|
from evennia.utils.ansi import ANSI_PARSER
|
||||||
|
|
||||||
|
if inherits_from(target, str):
|
||||||
|
# str or ANSIString
|
||||||
|
target = ANSI_PARSER.strip_mxp(target)
|
||||||
|
target = ANSI_PARSER.parse_ansi(target, strip_ansi=True)
|
||||||
|
extra_wide = ("F", "W")
|
||||||
|
return sum(2 if east_asian_width(char) in extra_wide else 1 for char in target)
|
||||||
|
else:
|
||||||
|
return len(target)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Search handler function
|
# Search handler function
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue