122 lines
3.3 KiB
Python
Executable File
122 lines
3.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
|
|
from argparse import ArgumentParser
|
|
from sys import stdin, stdout
|
|
|
|
|
|
def parse_args():
|
|
parser = ArgumentParser(description="Displays input straight from the horse's mouth.")
|
|
parser.add_argument("-u", "--unicode", action="store_true",
|
|
help="Use fancier lines for the text bubble")
|
|
parser.add_argument("-w", "--max-width", type=int, default=80,
|
|
help="Maximum width of output (default: 80)")
|
|
parser.add_argument("-b", "--break-at", type=str, default=" -",
|
|
help="String of characters to break words at (default: space and -)")
|
|
return parser.parse_args()
|
|
|
|
|
|
def segment_line(line, max_width, break_at):
|
|
"""
|
|
Breaks a line of text into multiple lines on word boundaries where possible.
|
|
"""
|
|
# The horse and text box take up 19 characters' worth of space
|
|
limit = max_width - 19
|
|
segments = []
|
|
while len(line) > limit:
|
|
# Find the first breakable character.
|
|
# Assume we'll find one for simplicity.
|
|
i = limit
|
|
while limit > 0 and line[i] not in break_at:
|
|
i -= 1
|
|
# Break off a segment
|
|
segments.append(line[:i])
|
|
line = line[i:].lstrip()
|
|
# Include the remainder once the line is short enough
|
|
segments.append(line)
|
|
return segments
|
|
|
|
|
|
def get_bubble_chars(use_unicode):
|
|
"""
|
|
Gets the characters to use for the text bubble.
|
|
"""
|
|
if use_unicode:
|
|
tl_corner = "\u256D"
|
|
tr_corner = "\u256E"
|
|
bl_corner = "\u2570"
|
|
br_corner = "\u256F"
|
|
horizontal = "\u2500"
|
|
vertical = "\u2502"
|
|
tail = "\u2524"
|
|
else:
|
|
tl_corner = "+"
|
|
tr_corner = "+"
|
|
bl_corner = "+"
|
|
br_corner = "+"
|
|
horizontal = "-"
|
|
vertical = "|"
|
|
tail = "|"
|
|
return tl_corner, tr_corner, bl_corner, br_corner, horizontal, vertical, tail
|
|
|
|
|
|
def build_bubble_lines(segments, bubble_chars):
|
|
"""
|
|
Builds a text bubble containing the given text segments.
|
|
"""
|
|
tl, tr, bl, br, horiz, vert, tail = bubble_chars
|
|
max_width = max(map(len, segments))
|
|
prefix = "{}{}{}{}{}".format(tl, horiz, horiz * max_width, horiz, tr)
|
|
suffix = "{}{}{}{}{}".format(bl, horiz, horiz * max_width, horiz, br)
|
|
mid = ["{0} {1:{2}} {0}".format(vert, seg, max_width) for seg in segments]
|
|
return [prefix, *mid, suffix]
|
|
|
|
|
|
def build_horse_lines(bubble_chars):
|
|
"""
|
|
Returns the lines making up the horse.
|
|
"""
|
|
_, _, _, _, horiz, _, _ = bubble_chars
|
|
return [
|
|
" ./|,,/| ",
|
|
" < o o) ",
|
|
" <\ ( | {0}{0}".format(horiz),
|
|
" <\\\ |\ | ",
|
|
" <\\\\\ |(__) ",
|
|
"<\\\\\\\ | "
|
|
]
|
|
|
|
|
|
def combine_lines(horse, bubble, bubble_chars):
|
|
"""
|
|
Aligns the text bubble with the horse based on the bubble's size.
|
|
"""
|
|
_, _, _, _, horiz, _, tail = bubble_chars
|
|
# Align the horse and text bubble
|
|
if len(bubble) < 4:
|
|
bubble = [""] + bubble
|
|
if len(bubble) > len(horse):
|
|
horse = [" " * len(horse[0])] * (len(bubble) - len(horse)) + horse
|
|
elif len(bubble) < len(horse):
|
|
bubble = bubble + [""] * (len(horse) - len(bubble))
|
|
combined = []
|
|
for h, b in zip(horse, bubble):
|
|
if h[-1] == horiz:
|
|
b = tail + b[1:]
|
|
combined.append(h + b)
|
|
return combined
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
segments = segment_line(stdin.readline().strip(), args.max_width, args.break_at)
|
|
chars = get_bubble_chars(args.unicode)
|
|
text_half = build_bubble_lines(segments, chars)
|
|
horse_half = build_horse_lines(chars)
|
|
combined = combine_lines(horse_half, text_half, chars)
|
|
for line in combined:
|
|
stdout.write(line + "\n")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|