mirror of
https://github.com/evennia/evennia.git
synced 2026-03-23 08:16:30 +01:00
Add justify(text,width,align,indent) function to utils. Supports full-,left-,right- and center justification of text to any width. It also has an option to add left-side indentation to the text block.
This commit is contained in:
parent
ecb7d6fcc5
commit
9ec875f29c
1 changed files with 83 additions and 1 deletions
|
|
@ -44,7 +44,6 @@ _DA = object.__delattr__
|
|||
|
||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||
|
||||
|
||||
def is_iter(iterable):
|
||||
"""
|
||||
Checks if an object behaves iterably.
|
||||
|
|
@ -175,6 +174,89 @@ def dedent(text):
|
|||
return textwrap.dedent(text)
|
||||
|
||||
|
||||
def justify(text, width=_DEFAULT_WIDTH, align="f", indent=0):
|
||||
"""
|
||||
Fully justify a text so that it fits inside `width`. When using
|
||||
full justification (default) this will be done by padding between
|
||||
words with extra whitespace where necessary. Paragraphs will
|
||||
be retained.
|
||||
|
||||
Args:
|
||||
text (str): Text to justify.
|
||||
width (int, optional): The length of each line, in characters.
|
||||
align (str, optional): The alignment, 'l', 'c', 'r' or 'f'
|
||||
for left, center, right or full justification respectively.
|
||||
indent (int, optional): Number of characters indentation of
|
||||
entire justified text block.
|
||||
|
||||
Returns:
|
||||
justified (str): The justified and indented block of text.
|
||||
|
||||
"""
|
||||
|
||||
def _process_line(line):
|
||||
"""
|
||||
helper function that distributes extra spaces between words. The number
|
||||
of gaps is nwords - 1 but must be at least 1 for single-word lines. We
|
||||
distribute odd spaces randomly to one of the gaps.
|
||||
"""
|
||||
line_rest = width - (wlen + ngaps)
|
||||
gap = " " # minimum gap between words
|
||||
if line_rest > 0:
|
||||
if align == 'l':
|
||||
line[-1] += " " * line_rest
|
||||
elif align == 'r':
|
||||
line[0] = " " * line_rest + line[0]
|
||||
elif align == 'c':
|
||||
pad = " " * (line_rest // 2)
|
||||
line[0] = pad + line[0]
|
||||
line[-1] = line[-1] + pad + " " * (line_rest % 2)
|
||||
else: # align 'f'
|
||||
gap += " " * (line_rest // max(1, ngaps))
|
||||
rest_gap = line_rest % max(1, ngaps)
|
||||
for i in range(rest_gap):
|
||||
line[i] += " "
|
||||
return gap.join(line)
|
||||
|
||||
# split into paragraphs and words
|
||||
paragraphs = re.split("\n\s*?\n", text, re.MULTILINE)
|
||||
words = []
|
||||
for ip, paragraph in enumerate(paragraphs):
|
||||
if ip > 0:
|
||||
words.append(("\n\n", 0))
|
||||
words.extend((word, len(word)) for word in paragraph.split())
|
||||
ngaps, wlen, line = 0, 0, []
|
||||
|
||||
lines = []
|
||||
while words:
|
||||
if not line:
|
||||
# start a new line
|
||||
word = words.pop(0)
|
||||
wlen = word[1]
|
||||
line.append(word[0])
|
||||
elif (words[0][1] + wlen + ngaps) >= width:
|
||||
# next word would exceed word length of line + smallest gaps
|
||||
lines.append(_process_line(line))
|
||||
ngaps, wlen, line = 0, 0, []
|
||||
else:
|
||||
# put a new word on the line
|
||||
word = words.pop(0)
|
||||
line.append(word[0])
|
||||
if word[1] == 0:
|
||||
# a new paragraph, process immediately
|
||||
lines.append(_process_line(line))
|
||||
ngaps, wlen, line = 0, 0, []
|
||||
else:
|
||||
wlen += word[1]
|
||||
ngaps += 1
|
||||
|
||||
|
||||
if line: # catch any line left behind
|
||||
lines.append(_process_line(line))
|
||||
indentstring = " " * indent
|
||||
return "\n".join([indentstring + line for line in lines])
|
||||
|
||||
|
||||
def list_to_string(inlist, endsep="and", addquote=False):
|
||||
"""
|
||||
This pretty-formats a list as string output, adding an optional
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue