Fix left column padding in timeline with wide characters

When the left column contains wide characters (which occupy more than
one cell when printed to screen), padding to 30-characters with
"{:30}".format() does not work well. This happens for instance when the
display name contains unicode characters such as emojis.

We fix this by introducing a pad() function in utils module which uses
the wcwidth library (https://pypi.org/project/wcwidth/) to compute the
length of the text for the column. trunc() function is also adjusted to
optionally compute the length of the text to be truncated since, when
called from pad(), we now pre-compute this value.

We update test for timeline rendering so that the display name now
includes an emoji. (Without the fix, the test would not pass as left
column would be misaligned.)
pull/92/head
Denis Laxalde 2019-01-02 13:22:06 +01:00 zatwierdzone przez Ivan Habunek
rodzic 9d6cd87202
commit 0bf4b2a21a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: CDBD63C43A30BB95
6 zmienionych plików z 32 dodań i 6 usunięć

Wyświetl plik

@ -1,2 +1,3 @@
requests>=2.13,<3.0
beautifulsoup4>=4.5.0,<5.0
wcwidth>=0.1.7,<2.0

Wyświetl plik

@ -40,6 +40,7 @@ setup(
install_requires=[
"requests>=2.13,<3.0",
"beautifulsoup4>=4.5.0,<5.0",
"wcwidth>=0.1.7,<2.0",
],
entry_points={
'console_scripts': [

Wyświetl plik

@ -122,7 +122,7 @@ def test_timeline(mock_get, monkeypatch, capsys):
mock_get.return_value = MockResponse([{
'id': '111111111111111111',
'account': {
'display_name': 'Frank Zappa',
'display_name': 'Frank Zappa 🎸',
'username': 'fz'
},
'created_at': '2017-04-12T15:53:18.174Z',
@ -139,7 +139,7 @@ def test_timeline(mock_get, monkeypatch, capsys):
expected = (
"───────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────\n"
"Frank Zappa │ The computer can't tell you the emotional story. It can give you the exact\n"
"Frank Zappa 🎸 │ The computer can't tell you the emotional story. It can give you the exact\n"
"@fz │ mathematical design, but what's missing is the eyebrows.\n"
"2017-04-12 15:53 │ \n"
"id: 111111111111111111 │ \n"

Wyświetl plik

@ -0,0 +1,14 @@
from toot import utils
def test_pad():
text = 'Frank Zappa 🎸'
padded = utils.pad(text, 14)
assert padded == 'Frank Zappa 🎸'
# guitar symbol will occupy two cells, so padded text should be 1
# character shorter
assert len(padded) == 13
# when truncated, … occupies one cell, so we get full length
padded = utils.pad(text, 13)
assert padded == 'Frank Zappa …'
assert len(padded) == 13

Wyświetl plik

@ -9,7 +9,7 @@ from itertools import chain
from itertools import zip_longest
from textwrap import wrap, TextWrapper
from toot.utils import format_content, get_text, trunc
from toot.utils import format_content, get_text, pad
START_CODES = {
'red': '\033[31m',
@ -147,7 +147,7 @@ def print_timeline(items):
return zip_longest(left_column, right_column, fillvalue="")
for left, right in timeline_rows(item):
print_out("{:30}{}".format(trunc(left, 30), right))
print_out("{}{}".format(pad(left, 30), right))
def _parse_item(item):
content = item['reblog']['content'] if item['reblog'] else item['content']

Wyświetl plik

@ -7,6 +7,7 @@ import unicodedata
import warnings
from bs4 import BeautifulSoup
from wcwidth import wcswidth
from toot.exceptions import ConsoleError
@ -75,14 +76,23 @@ def assert_domain_exists(domain):
raise ConsoleError("Domain {} not found".format(domain))
def trunc(text, length):
def trunc(text, length, text_length=None):
"""Trims text to given length, if trimmed appends ellipsis."""
if len(text) <= length:
if text_length is None:
text_length = len(text)
if text_length <= length:
return text
return text[:length - 1] + ''
def pad(text, length, fill=' '):
text_length = wcswidth(text)
text = trunc(text, length, text_length)
assert len(text) <= length
return text + fill * (length - text_length)
EOF_KEY = "Ctrl-Z" if os.name == 'nt' else "Ctrl-D"