-
Notifications
You must be signed in to change notification settings - Fork 8
/
quotewrap
executable file
·113 lines (93 loc) · 4.18 KB
/
quotewrap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#! /usr/bin/python
# quotewrap (Python script) -- wraps a (multi-part) quote, indenting attribution
import textwrap
import re
import sys
# max line lenght, not including dots / 4 space indent
MAX_LEN = 70
ATTRIBUTION_OVERFLOW = 7
# *** CLASSES ***
class NoAttributionContingency(Exception):
pass
# *** FUNCTIONS ***
# Wrap a paragraph, handling the attribution sensibly (indenting it if necessary).
# Returns a list of lines
def handle_para(p, basic_initial_indent, basic_subsequent_indent, max_line_length):
# If the paragraph starts with a double (but not single) quote character,
# indent the rest of the lines by one more space
if p[0] == "\"":
subsequent_indent = basic_subsequent_indent + " "
extra_attribution_indent = 1
else:
subsequent_indent = basic_subsequent_indent
extra_attribution_indent = 0
w = textwrap.TextWrapper(width=max_line_length,
initial_indent=basic_initial_indent,
subsequent_indent=subsequent_indent)
if len(p) > max_line_length:
try:
# Look for attribution and attempt to split there
# (use a look-behind assertion to ensure the hyphens are preceded
# by a double quote)
try:
(q, a) = re.split(r'(?<=") -- ', p)
except ValueError, e:
raise NoAttributionContingency()
qlines = w.wrap(q)
# Check if the attribution can fit on the last line
# (Allowing the attribution to go a bit past the column limit)
if len(qlines[-1] + " -- " + a) <= max_line_length + ATTRIBUTION_OVERFLOW:
qlines[-1] += " -- " + a
return qlines
else:
longest_line_length = max(len(x) for x in qlines)
# Indent the attribution (make the 2/3 point of the attribution
# line up with the 2/3 point of the longest line in this para)
a_indent = (longest_line_length * 2 / 3) - (len(a) * 2 / 3)
if a_indent <= 4:
a_indent = 6 + extra_attribution_indent
# Handle multi-line attributions
aw = textwrap.TextWrapper(width=max_line_length,
initial_indent=" "*a_indent+"-- ",
subsequent_indent=" "*a_indent+" ")
return qlines + aw.wrap(a)
except NoAttributionContingency, e:
# didn't get more than 1 value to unpack, so there is no attribution
return w.wrap(p)
else:
# No line wrapping needed, but indent has to be added
return w.wrap(p)
# Iterate over the paragraph, shrinking the max_line_length by 1 each time, until
# the line count increases. Then return the penultimate iteration.
def smart_handle_para(p, basic_initial_indent, basic_subsequent_indent):
latest = None
current_count = 0
max_line_length = MAX_LEN
# Attempt prettification by reducing the right margin by up to a quarter
while max_line_length > MAX_LEN / 4 * 3:
prev = latest
latest = handle_para(p, basic_initial_indent, basic_subsequent_indent, max_line_length)
if current_count > 0 and len(latest) > current_count:
return prev
else:
current_count = len(latest)
# no point trying to do prettification if there's two or less lines
if current_count <= 2:
break
max_line_length -= 1
return latest
def handle(s):
# Assumes different paragraphs are separated by double space,
# so long as it's followed by a double quote (which is left intact),
# using a look-ahead assertion
paras=re.split(r' (?=")', s)
## print paras
print "\n".join(smart_handle_para(paras[0], "... ", " "))
# Use a different initial_indent for the remainder of the paras
if len(paras) > 1:
for para in paras[1:]:
print "\n".join(smart_handle_para(para, " ", " "))
# *** MAINLINE ***
for line in sys.stdin.readlines():
# Strip the dots from the start of the line and the newline from the end
handle(re.sub(r"^\.\.\. ", "", line.rstrip()))