horsay/horsay.py

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()