Skip to content

Commit 9787bda

Browse files
committed
Many improvements dure to Fred Drake
1 parent d2560b0 commit 9787bda

File tree

1 file changed

+153
-57
lines changed

1 file changed

+153
-57
lines changed

Lib/formatter.py

Lines changed: 153 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import regsub
33
import string
44
import sys
5+
from types import StringType
56

67

78
AS_IS = None
@@ -12,66 +13,94 @@ class NullFormatter:
1213
def __init__(self): pass
1314
def end_paragraph(self, blankline): pass
1415
def add_line_break(self): pass
15-
def add_hor_rule(self): pass
16+
def add_hor_rule(self, abswidth=None, percentwidth=1.0,
17+
height=None, align=None): pass
1618
def add_label_data(self, format, counter): pass
1719
def add_flowing_data(self, data): pass
1820
def add_literal_data(self, data): pass
1921
def flush_softspace(self): pass
22+
def push_alignment(self, align): pass
23+
def pop_alignment(self): pass
2024
def push_font(self, x): pass
2125
def pop_font(self): pass
2226
def push_margin(self, margin): pass
2327
def pop_margin(self): pass
2428
def set_spacing(self, spacing): pass
25-
def push_style(self, style): pass
26-
def pop_style(self): pass
29+
def push_style(self, *styles): pass
30+
def pop_style(self, n=1): pass
31+
def assert_line_data(self, flag=1): pass
2732

2833

2934
class AbstractFormatter:
3035

3136
def __init__(self, writer):
3237
self.writer = writer # Output device
38+
self.align = None # Current alignment
39+
self.align_stack = [] # Alignment stack
3340
self.font_stack = [] # Font state
3441
self.margin_stack = [] # Margin state
3542
self.spacing = None # Vertical spacing state
3643
self.style_stack = [] # Other state, e.g. color
3744
self.nospace = 1 # Should leading space be suppressed
3845
self.softspace = 0 # Should a space be inserted
46+
self.para_end = 1 # Just ended a paragraph
47+
self.parskip = 0 # Skipped space between paragraphs?
48+
self.hard_break = 1 # Have a hard break
49+
self.have_label = 0
3950

4051
def end_paragraph(self, blankline):
41-
if not self.nospace:
42-
self.writer.send_paragraph(blankline)
43-
self.nospace = 1
52+
if not self.hard_break:
53+
self.writer.send_line_break()
54+
self.have_label = 0
55+
if self.parskip < blankline and not self.have_label:
56+
self.writer.send_paragraph(blankline - self.parskip)
57+
self.parskip = blankline
58+
self.have_label = 0
59+
self.hard_break = self.nospace = self.para_end = 1
4460
self.softspace = 0
4561

4662
def add_line_break(self):
47-
self.writer.send_line_break()
48-
self.nospace = 1
63+
if not (self.hard_break or self.para_end):
64+
self.writer.send_line_break()
65+
self.have_label = self.parskip = 0
66+
self.hard_break = self.nospace = 1
4967
self.softspace = 0
5068

51-
def add_hor_rule(self):
52-
self.writer.send_hor_rule()
53-
self.nospace = 1
54-
self.softspace = 0
55-
56-
def add_label_data(self, format, counter):
57-
data = self.format_counter(format, counter)
58-
self.writer.send_label_data(data)
69+
def add_hor_rule(self, *args, **kw):
70+
if not self.hard_break:
71+
self.writer.send_line_break()
72+
apply(self.writer.send_hor_rule, args, kw)
73+
self.hard_break = self.nospace = 1
74+
self.have_label = self.para_end = self.softspace = self.parskip = 0
75+
76+
def add_label_data(self, format, counter, blankline = None):
77+
if self.have_label or not self.hard_break:
78+
self.writer.send_line_break()
79+
if not self.para_end:
80+
self.writer.send_paragraph((blankline and 1) or 0)
81+
if type(format) is StringType:
82+
self.writer.send_label_data(self.format_counter(format, counter))
83+
else:
84+
self.writer.send_label_data(format)
85+
self.nospace = self.have_label = self.hard_break = self.para_end = 1
86+
self.softspace = self.parskip = 0
5987

6088
def format_counter(self, format, counter):
61-
if counter <= 0:
62-
return format
6389
label = ''
6490
for c in format:
6591
try:
6692
if c == '1':
67-
c = '%d' % counter
93+
label = label + ('%d' % counter)
6894
elif c in 'aA':
69-
c = self.format_letter(c, counter)
95+
if counter > 0:
96+
label = label + self.format_letter(c, counter)
7097
elif c in 'iI':
71-
c = self.format_roman(c, counter)
98+
if counter > 0:
99+
label = label + self.format_roman(c, counter)
100+
else:
101+
label = label + c
72102
except:
73-
pass
74-
label = label + c
103+
label = label + c
75104
return label
76105

77106
def format_letter(self, case, counter):
@@ -85,57 +114,88 @@ def format_letter(self, case, counter):
85114
def format_roman(self, case, counter):
86115
ones = ['i', 'x', 'c', 'm']
87116
fives = ['v', 'l', 'd']
88-
label = ''
89-
index = 0
117+
label, index = '', 0
90118
# This will die of IndexError when counter is too big
91119
while counter > 0:
92120
counter, x = divmod(counter, 10)
93121
if x == 9:
94-
s = ones[index] + ones[index+1]
122+
label = ones[index] + ones[index+1] + label
95123
elif x == 4:
96-
s = ones[index] + fives[index]
124+
label = ones[index] + fives[index] + label
97125
else:
98126
if x >= 5:
99127
s = fives[index]
100128
x = x-5
101129
else:
102130
s = ''
103131
s = s + ones[index]*x
104-
label = s + label
132+
label = s + label
105133
index = index + 1
106-
if case == 'I': label = string.upper(label)
134+
if case == 'I':
135+
return string.upper(label)
107136
return label
108137

109-
def add_flowing_data(self, data):
138+
def add_flowing_data(self, data,
139+
# These are only here to load them into locals:
140+
whitespace = string.whitespace,
141+
join = string.join, split = string.split):
110142
if not data: return
111143
# The following looks a bit convoluted but is a great improvement over
112144
# data = regsub.gsub('[' + string.whitespace + ']+', ' ', data)
113-
prespace = data[0] in string.whitespace
114-
postspace = data[-1] in string.whitespace
115-
data = string.join(string.split(data))
116-
if self.nospace and prespace:
117-
if not data: return
118-
prespace = 0
119-
elif self.softspace:
120-
prespace = 1
121-
self.nospace = self.softspace = 0
122-
if postspace:
123-
self.softspace = 1
124-
if prespace: data = ' ' + data
145+
prespace = data[:1] in whitespace
146+
postspace = data[-1:] in whitespace
147+
data = join(split(data))
148+
if self.nospace and not data:
149+
return
150+
elif prespace or self.softspace:
151+
if not data:
152+
if not self.nospace:
153+
self.softspace = 1
154+
self.parskip = 0
155+
return
156+
if not self.nospace:
157+
data = ' ' + data
158+
self.hard_break = self.nospace = self.para_end = \
159+
self.parskip = self.have_label = 0
160+
self.softspace = postspace
125161
self.writer.send_flowing_data(data)
126162

127163
def add_literal_data(self, data):
128-
if self.softspace and data[:1] != '\n':
129-
data = ' ' + data
130-
self.nospace = self.softspace = 0
164+
if not data: return
165+
# Caller is expected to cause flush_softspace() if needed.
166+
self.hard_break = data[-1:] == '\n'
167+
self.nospace = self.para_end = self.softspace = \
168+
self.parskip = self.have_label = 0
131169
self.writer.send_literal_data(data)
132170

133171
def flush_softspace(self):
134172
if self.softspace:
135-
self.nospace = self.softspace = 0
173+
self.hard_break = self.nospace = self.para_end = self.parskip = \
174+
self.have_label = self.softspace = 0
136175
self.writer.send_flowing_data(' ')
137176

177+
def push_alignment(self, align):
178+
if align and align != self.align:
179+
self.writer.new_alignment(align)
180+
self.align = align
181+
self.align_stack.append(align)
182+
else:
183+
self.align_stack.append(self.align)
184+
185+
def pop_alignment(self):
186+
if self.align_stack:
187+
del self.align_stack[-1]
188+
if self.align_stack:
189+
self.align = align = self.align_stack[-1]
190+
self.writer.new_alignment(align)
191+
else:
192+
self.align = None
193+
self.writer.new_alignment(None)
194+
138195
def push_font(self, (size, i, b, tt)):
196+
if self.softspace:
197+
self.hard_break = self.nospace = self.para_end = self.softspace = 0
198+
self.writer.send_flowing_data(' ')
139199
if self.font_stack:
140200
csize, ci, cb, ctt = self.font_stack[-1]
141201
if size is AS_IS: size = csize
@@ -147,6 +207,9 @@ def push_font(self, (size, i, b, tt)):
147207
self.writer.new_font(font)
148208

149209
def pop_font(self):
210+
if self.softspace:
211+
self.hard_break = self.nospace = self.para_end = self.softspace = 0
212+
self.writer.send_flowing_data(' ')
150213
if self.font_stack:
151214
del self.font_stack[-1]
152215
if self.font_stack:
@@ -157,36 +220,69 @@ def pop_font(self):
157220

158221
def push_margin(self, margin):
159222
self.margin_stack.append(margin)
160-
self.writer.new_margin(margin, len(self.margin_stack))
223+
fstack = filter(None, self.margin_stack)
224+
if not margin and fstack:
225+
margin = fstack[-1]
226+
self.writer.new_margin(margin, len(fstack))
161227

162228
def pop_margin(self):
163229
if self.margin_stack:
164230
del self.margin_stack[-1]
165-
if self.margin_stack:
166-
margin = self.margin_stack[-1]
231+
fstack = filter(None, self.margin_stack)
232+
if fstack:
233+
margin = fstack[-1]
167234
else:
168235
margin = None
169-
self.writer.new_margin(margin, len(self.margin_stack))
236+
self.writer.new_margin(margin, len(fstack))
170237

171238
def set_spacing(self, spacing):
172239
self.spacing = spacing
173240
self.writer.new_spacing(spacing)
174241

175-
def push_style(self, style):
176-
self.style_stack.append(style)
242+
def push_style(self, *styles):
243+
if self.softspace:
244+
self.hard_break = self.nospace = self.para_end = self.softspace = 0
245+
self.writer.send_flowing_data(' ')
246+
for style in styles:
247+
self.style_stack.append(style)
177248
self.writer.new_styles(tuple(self.style_stack))
178249

179-
def pop_style(self):
180-
if self.style_stack:
181-
del self.style_stack[-1]
250+
def pop_style(self, n=1):
251+
if self.softspace:
252+
self.hard_break = self.nospace = self.para_end = self.softspace = 0
253+
self.writer.send_flowing_data(' ')
254+
del self.style_stack[-n:]
182255
self.writer.new_styles(tuple(self.style_stack))
183256

257+
def assert_line_data(self, flag=1):
258+
self.nospace = self.hard_break = not flag
259+
self.para_end = self.have_label = 0
260+
261+
262+
class NullWriter:
263+
"""Minimal writer interface to use in testing.
264+
"""
265+
def new_alignment(self, align): pass
266+
def new_font(self, font): pass
267+
def new_margin(self, margin, level): pass
268+
def new_spacing(self, spacing): pass
269+
def new_styles(self, styles): pass
270+
def send_paragraph(self, blankline): pass
271+
def send_line_break(self): pass
272+
def send_hor_rule(self, *args, **kw): pass
273+
def send_label_data(self, data): pass
274+
def send_flowing_data(self, data): pass
275+
def send_literal_data(self, data): pass
276+
184277

185278
class AbstractWriter:
186279

187280
def __init__(self):
188281
pass
189282

283+
def new_alignment(self, align):
284+
print "new_alignment(%s)" % `align`
285+
190286
def new_font(self, font):
191287
print "new_font(%s)" % `font`
192288

@@ -205,7 +301,7 @@ def send_paragraph(self, blankline):
205301
def send_line_break(self):
206302
print "send_line_break()"
207303

208-
def send_hor_rule(self):
304+
def send_hor_rule(self, *args, **kw):
209305
print "send_hor_rule()"
210306

211307
def send_label_data(self, data):
@@ -240,7 +336,7 @@ def send_line_break(self):
240336
self.col = 0
241337
self.atbreak = 0
242338

243-
def send_hor_rule(self):
339+
def send_hor_rule(self, *args, **kw):
244340
self.file.write('\n')
245341
self.file.write('-'*self.maxcol)
246342
self.file.write('\n')

0 commit comments

Comments
 (0)