Consolidate wcwidth-related utils in one module

pull/92/head
Ivan Habunek 2019-02-14 15:45:27 +01:00
rodzic 769ff9e406
commit 8a3ff94e47
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: CDBD63C43A30BB95
3 zmienionych plików z 107 dodań i 109 usunięć

Wyświetl plik

@ -1,5 +1,4 @@
from toot import utils from toot.wcstring import wc_wrap, trunc, pad, fit_text
from toot.wcstring import wc_wrap
def test_pad(): def test_pad():
@ -8,72 +7,72 @@ def test_pad():
text = 'Frank Zappa 🎸' text = 'Frank Zappa 🎸'
# Negative values are basically ignored # Negative values are basically ignored
assert utils.pad(text, -100) is text assert pad(text, -100) is text
# Padding to length smaller than text length does nothing # Padding to length smaller than text length does nothing
assert utils.pad(text, 11) is text assert pad(text, 11) is text
assert utils.pad(text, 12) is text assert pad(text, 12) is text
assert utils.pad(text, 13) is text assert pad(text, 13) is text
assert utils.pad(text, 14) is text assert pad(text, 14) is text
assert utils.pad(text, 15) == 'Frank Zappa 🎸 ' assert pad(text, 15) == 'Frank Zappa 🎸 '
assert utils.pad(text, 16) == 'Frank Zappa 🎸 ' assert pad(text, 16) == 'Frank Zappa 🎸 '
assert utils.pad(text, 17) == 'Frank Zappa 🎸 ' assert pad(text, 17) == 'Frank Zappa 🎸 '
assert utils.pad(text, 18) == 'Frank Zappa 🎸 ' assert pad(text, 18) == 'Frank Zappa 🎸 '
assert utils.pad(text, 19) == 'Frank Zappa 🎸 ' assert pad(text, 19) == 'Frank Zappa 🎸 '
assert utils.pad(text, 20) == 'Frank Zappa 🎸 ' assert pad(text, 20) == 'Frank Zappa 🎸 '
def test_trunc(): def test_trunc():
text = 'Frank Zappa 🎸' text = 'Frank Zappa 🎸'
assert utils.trunc(text, 1) == '' assert trunc(text, 1) == ''
assert utils.trunc(text, 2) == 'F…' assert trunc(text, 2) == 'F…'
assert utils.trunc(text, 3) == 'Fr…' assert trunc(text, 3) == 'Fr…'
assert utils.trunc(text, 4) == 'Fra…' assert trunc(text, 4) == 'Fra…'
assert utils.trunc(text, 5) == 'Fran…' assert trunc(text, 5) == 'Fran…'
assert utils.trunc(text, 6) == 'Frank…' assert trunc(text, 6) == 'Frank…'
assert utils.trunc(text, 7) == 'Frank…' assert trunc(text, 7) == 'Frank…'
assert utils.trunc(text, 8) == 'Frank Z…' assert trunc(text, 8) == 'Frank Z…'
assert utils.trunc(text, 9) == 'Frank Za…' assert trunc(text, 9) == 'Frank Za…'
assert utils.trunc(text, 10) == 'Frank Zap…' assert trunc(text, 10) == 'Frank Zap…'
assert utils.trunc(text, 11) == 'Frank Zapp…' assert trunc(text, 11) == 'Frank Zapp…'
assert utils.trunc(text, 12) == 'Frank Zappa…' assert trunc(text, 12) == 'Frank Zappa…'
assert utils.trunc(text, 13) == 'Frank Zappa…' assert trunc(text, 13) == 'Frank Zappa…'
# Truncating to length larger than text length does nothing # Truncating to length larger than text length does nothing
assert utils.trunc(text, 14) is text assert trunc(text, 14) is text
assert utils.trunc(text, 15) is text assert trunc(text, 15) is text
assert utils.trunc(text, 16) is text assert trunc(text, 16) is text
assert utils.trunc(text, 17) is text assert trunc(text, 17) is text
assert utils.trunc(text, 18) is text assert trunc(text, 18) is text
assert utils.trunc(text, 19) is text assert trunc(text, 19) is text
assert utils.trunc(text, 20) is text assert trunc(text, 20) is text
def test_fit_text(): def test_fit_text():
text = 'Frank Zappa 🎸' text = 'Frank Zappa 🎸'
assert utils.fit_text(text, 1) == '' assert fit_text(text, 1) == ''
assert utils.fit_text(text, 2) == 'F…' assert fit_text(text, 2) == 'F…'
assert utils.fit_text(text, 3) == 'Fr…' assert fit_text(text, 3) == 'Fr…'
assert utils.fit_text(text, 4) == 'Fra…' assert fit_text(text, 4) == 'Fra…'
assert utils.fit_text(text, 5) == 'Fran…' assert fit_text(text, 5) == 'Fran…'
assert utils.fit_text(text, 6) == 'Frank…' assert fit_text(text, 6) == 'Frank…'
assert utils.fit_text(text, 7) == 'Frank…' assert fit_text(text, 7) == 'Frank…'
assert utils.fit_text(text, 8) == 'Frank Z…' assert fit_text(text, 8) == 'Frank Z…'
assert utils.fit_text(text, 9) == 'Frank Za…' assert fit_text(text, 9) == 'Frank Za…'
assert utils.fit_text(text, 10) == 'Frank Zap…' assert fit_text(text, 10) == 'Frank Zap…'
assert utils.fit_text(text, 11) == 'Frank Zapp…' assert fit_text(text, 11) == 'Frank Zapp…'
assert utils.fit_text(text, 12) == 'Frank Zappa…' assert fit_text(text, 12) == 'Frank Zappa…'
assert utils.fit_text(text, 13) == 'Frank Zappa…' assert fit_text(text, 13) == 'Frank Zappa…'
assert utils.fit_text(text, 14) == 'Frank Zappa 🎸' assert fit_text(text, 14) == 'Frank Zappa 🎸'
assert utils.fit_text(text, 15) == 'Frank Zappa 🎸 ' assert fit_text(text, 15) == 'Frank Zappa 🎸 '
assert utils.fit_text(text, 16) == 'Frank Zappa 🎸 ' assert fit_text(text, 16) == 'Frank Zappa 🎸 '
assert utils.fit_text(text, 17) == 'Frank Zappa 🎸 ' assert fit_text(text, 17) == 'Frank Zappa 🎸 '
assert utils.fit_text(text, 18) == 'Frank Zappa 🎸 ' assert fit_text(text, 18) == 'Frank Zappa 🎸 '
assert utils.fit_text(text, 19) == 'Frank Zappa 🎸 ' assert fit_text(text, 19) == 'Frank Zappa 🎸 '
assert utils.fit_text(text, 20) == 'Frank Zappa 🎸 ' assert fit_text(text, 20) == 'Frank Zappa 🎸 '
def test_wc_wrap_plain_text(): def test_wc_wrap_plain_text():

Wyświetl plik

@ -7,7 +7,6 @@ import unicodedata
import warnings import warnings
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from wcwidth import wcwidth, wcswidth
from toot.exceptions import ConsoleError from toot.exceptions import ConsoleError
@ -76,61 +75,6 @@ def assert_domain_exists(domain):
raise ConsoleError("Domain {} not found".format(domain)) raise ConsoleError("Domain {} not found".format(domain))
def trunc(text, length):
"""
Truncates text to given length, taking into account wide characters.
If truncated, the last char is replaced by an elipsis.
"""
if length < 1:
raise ValueError("length should be 1 or larger")
# Remove whitespace first so no unneccesary truncation is done.
text = text.strip()
text_length = wcswidth(text)
if text_length <= length:
return text
# We cannot just remove n characters from the end since we don't know how
# wide these characters are and how it will affect text length.
# Use wcwidth to determine how many characters need to be truncated.
chars_to_truncate = 0
trunc_length = 0
for char in reversed(text):
chars_to_truncate += 1
trunc_length += wcwidth(char)
if text_length - trunc_length <= length:
break
# Additional char to make room for elipsis
n = chars_to_truncate + 1
return text[:-n].strip() + ''
def pad(text, length):
"""Pads text to given length, taking into account wide characters."""
text_length = wcswidth(text)
if text_length < length:
return text + ' ' * (length - text_length)
return text
def fit_text(text, length):
"""Makes text fit the given length by padding or truncating it."""
text_length = wcswidth(text)
if text_length > length:
return trunc(text, length)
if text_length < length:
return pad(text, length)
return text
EOF_KEY = "Ctrl-Z" if os.name == 'nt' else "Ctrl-D" EOF_KEY = "Ctrl-Z" if os.name == 'nt' else "Ctrl-D"

Wyświetl plik

@ -64,3 +64,58 @@ def wc_wrap(text, length):
yield line yield line
else: else:
yield from _wc_hard_wrap(line, length) yield from _wc_hard_wrap(line, length)
def trunc(text, length):
"""
Truncates text to given length, taking into account wide characters.
If truncated, the last char is replaced by an elipsis.
"""
if length < 1:
raise ValueError("length should be 1 or larger")
# Remove whitespace first so no unneccesary truncation is done.
text = text.strip()
text_length = wcswidth(text)
if text_length <= length:
return text
# We cannot just remove n characters from the end since we don't know how
# wide these characters are and how it will affect text length.
# Use wcwidth to determine how many characters need to be truncated.
chars_to_truncate = 0
trunc_length = 0
for char in reversed(text):
chars_to_truncate += 1
trunc_length += wcwidth(char)
if text_length - trunc_length <= length:
break
# Additional char to make room for elipsis
n = chars_to_truncate + 1
return text[:-n].strip() + ''
def pad(text, length):
"""Pads text to given length, taking into account wide characters."""
text_length = wcswidth(text)
if text_length < length:
return text + ' ' * (length - text_length)
return text
def fit_text(text, length):
"""Makes text fit the given length by padding or truncating it."""
text_length = wcswidth(text)
if text_length > length:
return trunc(text, length)
if text_length < length:
return pad(text, length)
return text