Display wide characters correctly.

- Made termstr's length match the on-screen width.
  - Achieved by adding a zero width character after each wide
    character.
  - This fixes ljust and rjust, so that there is correct padding,
    otherwise Portal appearances aren't the correct width.
- When getting a sub-string containing half a wide character,
  the half character is a space.
- Some wide characters are still broken. e.g. country flags
This commit is contained in:
Andrew Hamilton 2021-07-25 22:59:23 +10:00
parent c4fb3fdfe3
commit c91beda9b4
3 changed files with 34 additions and 7 deletions

View file

@ -8,6 +8,7 @@ import os
import weakref
import pygments.formatters.terminal256
import cwcwidth
import eris.ColorMap
import eris.terminal as terminal
@ -130,15 +131,26 @@ def _join_lists(lists):
return list(itertools.chain.from_iterable(lists))
_ZERO_WIDTH_SPACE = chr(0x00ad)
def _pad_wide_chars(str_):
return "".join(f"{char}{_ZERO_WIDTH_SPACE}"
if cwcwidth.wcwidth(char) == 2 else char for char in str_)
class TermStr(collections.UserString):
def __init__(self, data, style=CharStyle()):
if isinstance(style, tuple):
self.data = data
self.style = style
else:
try:
self.data, self.style = data.data, data.style
except AttributeError:
self.data = data
self.style = (style if isinstance(style, tuple)
else (style,) * len(data))
self.data = _pad_wide_chars(data)
self.style = (style,) * len(self.data)
@classmethod
def from_term(cls, data):
@ -249,7 +261,17 @@ class TermStr(collections.UserString):
__rmul__ = __mul__
def __getitem__(self, index):
return self.__class__(self.data[index], self.style[index])
data = self.data[index]
if len(data) == 0:
result = ""
else:
first_char = " " if data[0] == _ZERO_WIDTH_SPACE else data[0]
if len(data) == 1:
result = first_char
else:
end_char = " " if cwcwidth.wcwidth(data[-1]) == 2 else data[-1]
result = first_char + data[1:-1] + end_char
return self.__class__(result, self.style[index])
def join(self, parts):
parts = [TermStr(part) if isinstance(part, str) else part

View file

@ -10,7 +10,7 @@ if [ $DIST_ID != "ubuntu" ]; then
exit 1
fi
echo "Installing the dependencies of the eris script…"
sudo apt --yes install python3-pip python3.9 util-linux
sudo apt --yes install python3-pip python3.9 util-linux python3-cwcwidth
python3.9 -m pip install pyinotify pygments docopt pillow toml
echo
echo "Installing all the tools eris may need…"

View file

@ -101,6 +101,11 @@ class TermStrTests(unittest.TestCase):
self.assertEqual(foo_bold.ljust(5), foo_bold + TermStr(" "))
self.assertEqual(foo_bold.rjust(0), foo_bold)
self.assertEqual(foo_bold.rjust(5), TermStr(" ") + foo_bold)
baz = TermStr("b👋z")
self.assertEqual(len(baz), 4)
self.assertEqual(baz[3:], TermStr("z"))
self.assertEqual(baz[:2], TermStr("b "))
self.assertEqual(baz[2:], TermStr(" z"))
def test_from_term(self):
def test_round_trip(term_str):