##// END OF EJS Templates
minirst: support all recommended title adornments
Martin Geisler -
r10984:68b7d2d6 default
parent child Browse files
Show More
@@ -1,379 +1,382 b''
1 # minirst.py - minimal reStructuredText parser
1 # minirst.py - minimal reStructuredText parser
2 #
2 #
3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """simplified reStructuredText parser.
8 """simplified reStructuredText parser.
9
9
10 This parser knows just enough about reStructuredText to parse the
10 This parser knows just enough about reStructuredText to parse the
11 Mercurial docstrings.
11 Mercurial docstrings.
12
12
13 It cheats in a major way: nested blocks are not really nested. They
13 It cheats in a major way: nested blocks are not really nested. They
14 are just indented blocks that look like they are nested. This relies
14 are just indented blocks that look like they are nested. This relies
15 on the user to keep the right indentation for the blocks.
15 on the user to keep the right indentation for the blocks.
16
16
17 It only supports a small subset of reStructuredText:
17 It only supports a small subset of reStructuredText:
18
18
19 - sections
19 - sections
20
20
21 - paragraphs
21 - paragraphs
22
22
23 - literal blocks
23 - literal blocks
24
24
25 - definition lists
25 - definition lists
26
26
27 - bullet lists (items must start with '-')
27 - bullet lists (items must start with '-')
28
28
29 - enumerated lists (no autonumbering)
29 - enumerated lists (no autonumbering)
30
30
31 - field lists (colons cannot be escaped)
31 - field lists (colons cannot be escaped)
32
32
33 - option lists (supports only long options without arguments)
33 - option lists (supports only long options without arguments)
34
34
35 - inline literals (no other inline markup is not recognized)
35 - inline literals (no other inline markup is not recognized)
36 """
36 """
37
37
38 import re, sys, textwrap
38 import re, sys, textwrap
39
39
40
40
41 def findblocks(text):
41 def findblocks(text):
42 """Find continuous blocks of lines in text.
42 """Find continuous blocks of lines in text.
43
43
44 Returns a list of dictionaries representing the blocks. Each block
44 Returns a list of dictionaries representing the blocks. Each block
45 has an 'indent' field and a 'lines' field.
45 has an 'indent' field and a 'lines' field.
46 """
46 """
47 blocks = [[]]
47 blocks = [[]]
48 lines = text.splitlines()
48 lines = text.splitlines()
49 for line in lines:
49 for line in lines:
50 if line.strip():
50 if line.strip():
51 blocks[-1].append(line)
51 blocks[-1].append(line)
52 elif blocks[-1]:
52 elif blocks[-1]:
53 blocks.append([])
53 blocks.append([])
54 if not blocks[-1]:
54 if not blocks[-1]:
55 del blocks[-1]
55 del blocks[-1]
56
56
57 for i, block in enumerate(blocks):
57 for i, block in enumerate(blocks):
58 indent = min((len(l) - len(l.lstrip())) for l in block)
58 indent = min((len(l) - len(l.lstrip())) for l in block)
59 blocks[i] = dict(indent=indent, lines=[l[indent:] for l in block])
59 blocks[i] = dict(indent=indent, lines=[l[indent:] for l in block])
60 return blocks
60 return blocks
61
61
62
62
63 def findliteralblocks(blocks):
63 def findliteralblocks(blocks):
64 """Finds literal blocks and adds a 'type' field to the blocks.
64 """Finds literal blocks and adds a 'type' field to the blocks.
65
65
66 Literal blocks are given the type 'literal', all other blocks are
66 Literal blocks are given the type 'literal', all other blocks are
67 given type the 'paragraph'.
67 given type the 'paragraph'.
68 """
68 """
69 i = 0
69 i = 0
70 while i < len(blocks):
70 while i < len(blocks):
71 # Searching for a block that looks like this:
71 # Searching for a block that looks like this:
72 #
72 #
73 # +------------------------------+
73 # +------------------------------+
74 # | paragraph |
74 # | paragraph |
75 # | (ends with "::") |
75 # | (ends with "::") |
76 # +------------------------------+
76 # +------------------------------+
77 # +---------------------------+
77 # +---------------------------+
78 # | indented literal block |
78 # | indented literal block |
79 # +---------------------------+
79 # +---------------------------+
80 blocks[i]['type'] = 'paragraph'
80 blocks[i]['type'] = 'paragraph'
81 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
81 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
82 indent = blocks[i]['indent']
82 indent = blocks[i]['indent']
83 adjustment = blocks[i + 1]['indent'] - indent
83 adjustment = blocks[i + 1]['indent'] - indent
84
84
85 if blocks[i]['lines'] == ['::']:
85 if blocks[i]['lines'] == ['::']:
86 # Expanded form: remove block
86 # Expanded form: remove block
87 del blocks[i]
87 del blocks[i]
88 i -= 1
88 i -= 1
89 elif blocks[i]['lines'][-1].endswith(' ::'):
89 elif blocks[i]['lines'][-1].endswith(' ::'):
90 # Partially minimized form: remove space and both
90 # Partially minimized form: remove space and both
91 # colons.
91 # colons.
92 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
92 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
93 else:
93 else:
94 # Fully minimized form: remove just one colon.
94 # Fully minimized form: remove just one colon.
95 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
95 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
96
96
97 # List items are formatted with a hanging indent. We must
97 # List items are formatted with a hanging indent. We must
98 # correct for this here while we still have the original
98 # correct for this here while we still have the original
99 # information on the indentation of the subsequent literal
99 # information on the indentation of the subsequent literal
100 # blocks available.
100 # blocks available.
101 m = _bulletre.match(blocks[i]['lines'][0])
101 m = _bulletre.match(blocks[i]['lines'][0])
102 if m:
102 if m:
103 indent += m.end()
103 indent += m.end()
104 adjustment -= m.end()
104 adjustment -= m.end()
105
105
106 # Mark the following indented blocks.
106 # Mark the following indented blocks.
107 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
107 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
108 blocks[i + 1]['type'] = 'literal'
108 blocks[i + 1]['type'] = 'literal'
109 blocks[i + 1]['indent'] -= adjustment
109 blocks[i + 1]['indent'] -= adjustment
110 i += 1
110 i += 1
111 i += 1
111 i += 1
112 return blocks
112 return blocks
113
113
114 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
114 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
115 _optionre = re.compile(r'^(--[a-z-]+)((?:[ =][a-zA-Z][\w-]*)? +)(.*)$')
115 _optionre = re.compile(r'^(--[a-z-]+)((?:[ =][a-zA-Z][\w-]*)? +)(.*)$')
116 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
116 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
117 _definitionre = re.compile(r'[^ ]')
117 _definitionre = re.compile(r'[^ ]')
118
118
119 def splitparagraphs(blocks):
119 def splitparagraphs(blocks):
120 """Split paragraphs into lists."""
120 """Split paragraphs into lists."""
121 # Tuples with (list type, item regexp, single line items?). Order
121 # Tuples with (list type, item regexp, single line items?). Order
122 # matters: definition lists has the least specific regexp and must
122 # matters: definition lists has the least specific regexp and must
123 # come last.
123 # come last.
124 listtypes = [('bullet', _bulletre, True),
124 listtypes = [('bullet', _bulletre, True),
125 ('option', _optionre, True),
125 ('option', _optionre, True),
126 ('field', _fieldre, True),
126 ('field', _fieldre, True),
127 ('definition', _definitionre, False)]
127 ('definition', _definitionre, False)]
128
128
129 def match(lines, i, itemre, singleline):
129 def match(lines, i, itemre, singleline):
130 """Does itemre match an item at line i?
130 """Does itemre match an item at line i?
131
131
132 A list item can be followed by an idented line or another list
132 A list item can be followed by an idented line or another list
133 item (but only if singleline is True).
133 item (but only if singleline is True).
134 """
134 """
135 line1 = lines[i]
135 line1 = lines[i]
136 line2 = i + 1 < len(lines) and lines[i + 1] or ''
136 line2 = i + 1 < len(lines) and lines[i + 1] or ''
137 if not itemre.match(line1):
137 if not itemre.match(line1):
138 return False
138 return False
139 if singleline:
139 if singleline:
140 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
140 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
141 else:
141 else:
142 return line2.startswith(' ')
142 return line2.startswith(' ')
143
143
144 i = 0
144 i = 0
145 while i < len(blocks):
145 while i < len(blocks):
146 if blocks[i]['type'] == 'paragraph':
146 if blocks[i]['type'] == 'paragraph':
147 lines = blocks[i]['lines']
147 lines = blocks[i]['lines']
148 for type, itemre, singleline in listtypes:
148 for type, itemre, singleline in listtypes:
149 if match(lines, 0, itemre, singleline):
149 if match(lines, 0, itemre, singleline):
150 items = []
150 items = []
151 for j, line in enumerate(lines):
151 for j, line in enumerate(lines):
152 if match(lines, j, itemre, singleline):
152 if match(lines, j, itemre, singleline):
153 items.append(dict(type=type, lines=[],
153 items.append(dict(type=type, lines=[],
154 indent=blocks[i]['indent']))
154 indent=blocks[i]['indent']))
155 items[-1]['lines'].append(line)
155 items[-1]['lines'].append(line)
156 blocks[i:i + 1] = items
156 blocks[i:i + 1] = items
157 break
157 break
158 i += 1
158 i += 1
159 return blocks
159 return blocks
160
160
161
161
162 _fieldwidth = 12
162 _fieldwidth = 12
163
163
164 def updatefieldlists(blocks):
164 def updatefieldlists(blocks):
165 """Find key and maximum key width for field lists."""
165 """Find key and maximum key width for field lists."""
166 i = 0
166 i = 0
167 while i < len(blocks):
167 while i < len(blocks):
168 if blocks[i]['type'] != 'field':
168 if blocks[i]['type'] != 'field':
169 i += 1
169 i += 1
170 continue
170 continue
171
171
172 keywidth = 0
172 keywidth = 0
173 j = i
173 j = i
174 while j < len(blocks) and blocks[j]['type'] == 'field':
174 while j < len(blocks) and blocks[j]['type'] == 'field':
175 m = _fieldre.match(blocks[j]['lines'][0])
175 m = _fieldre.match(blocks[j]['lines'][0])
176 key, rest = m.groups()
176 key, rest = m.groups()
177 blocks[j]['lines'][0] = rest
177 blocks[j]['lines'][0] = rest
178 blocks[j]['key'] = key
178 blocks[j]['key'] = key
179 keywidth = max(keywidth, len(key))
179 keywidth = max(keywidth, len(key))
180 j += 1
180 j += 1
181
181
182 for block in blocks[i:j]:
182 for block in blocks[i:j]:
183 block['keywidth'] = keywidth
183 block['keywidth'] = keywidth
184 i = j + 1
184 i = j + 1
185
185
186 return blocks
186 return blocks
187
187
188
188
189 def prunecontainers(blocks, keep):
189 def prunecontainers(blocks, keep):
190 """Prune unwanted containers.
190 """Prune unwanted containers.
191
191
192 The blocks must have a 'type' field, i.e., they should have been
192 The blocks must have a 'type' field, i.e., they should have been
193 run through findliteralblocks first.
193 run through findliteralblocks first.
194 """
194 """
195 pruned = []
195 pruned = []
196 i = 0
196 i = 0
197 while i + 1 < len(blocks):
197 while i + 1 < len(blocks):
198 # Searching for a block that looks like this:
198 # Searching for a block that looks like this:
199 #
199 #
200 # +-------+---------------------------+
200 # +-------+---------------------------+
201 # | ".. container ::" type |
201 # | ".. container ::" type |
202 # +---+ |
202 # +---+ |
203 # | blocks |
203 # | blocks |
204 # +-------------------------------+
204 # +-------------------------------+
205 if (blocks[i]['type'] == 'paragraph' and
205 if (blocks[i]['type'] == 'paragraph' and
206 blocks[i]['lines'][0].startswith('.. container::')):
206 blocks[i]['lines'][0].startswith('.. container::')):
207 indent = blocks[i]['indent']
207 indent = blocks[i]['indent']
208 adjustment = blocks[i + 1]['indent'] - indent
208 adjustment = blocks[i + 1]['indent'] - indent
209 containertype = blocks[i]['lines'][0][15:]
209 containertype = blocks[i]['lines'][0][15:]
210 prune = containertype not in keep
210 prune = containertype not in keep
211 if prune:
211 if prune:
212 pruned.append(containertype)
212 pruned.append(containertype)
213
213
214 # Always delete "..container:: type" block
214 # Always delete "..container:: type" block
215 del blocks[i]
215 del blocks[i]
216 j = i
216 j = i
217 while j < len(blocks) and blocks[j]['indent'] > indent:
217 while j < len(blocks) and blocks[j]['indent'] > indent:
218 if prune:
218 if prune:
219 del blocks[j]
219 del blocks[j]
220 i -= 1 # adjust outer index
220 i -= 1 # adjust outer index
221 else:
221 else:
222 blocks[j]['indent'] -= adjustment
222 blocks[j]['indent'] -= adjustment
223 j += 1
223 j += 1
224 i += 1
224 i += 1
225 return blocks, pruned
225 return blocks, pruned
226
226
227
227
228 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
229
228 def findsections(blocks):
230 def findsections(blocks):
229 """Finds sections.
231 """Finds sections.
230
232
231 The blocks must have a 'type' field, i.e., they should have been
233 The blocks must have a 'type' field, i.e., they should have been
232 run through findliteralblocks first.
234 run through findliteralblocks first.
233 """
235 """
234 for block in blocks:
236 for block in blocks:
235 # Searching for a block that looks like this:
237 # Searching for a block that looks like this:
236 #
238 #
237 # +------------------------------+
239 # +------------------------------+
238 # | Section title |
240 # | Section title |
239 # | ------------- |
241 # | ------------- |
240 # +------------------------------+
242 # +------------------------------+
241 if (block['type'] == 'paragraph' and
243 if (block['type'] == 'paragraph' and
242 len(block['lines']) == 2 and
244 len(block['lines']) == 2 and
243 block['lines'][1] == '-' * len(block['lines'][0])):
245 len(block['lines'][0]) == len(block['lines'][1]) and
246 _sectionre.match(block['lines'][1])):
244 block['underline'] = block['lines'][1][0]
247 block['underline'] = block['lines'][1][0]
245 block['type'] = 'section'
248 block['type'] = 'section'
246 del block['lines'][1]
249 del block['lines'][1]
247 return blocks
250 return blocks
248
251
249
252
250 def inlineliterals(blocks):
253 def inlineliterals(blocks):
251 for b in blocks:
254 for b in blocks:
252 if b['type'] in ('paragraph', 'section'):
255 if b['type'] in ('paragraph', 'section'):
253 b['lines'] = [l.replace('``', '"') for l in b['lines']]
256 b['lines'] = [l.replace('``', '"') for l in b['lines']]
254 return blocks
257 return blocks
255
258
256
259
257 _hgrolere = re.compile(r':hg:`([^`]+)`')
260 _hgrolere = re.compile(r':hg:`([^`]+)`')
258
261
259 def hgrole(blocks):
262 def hgrole(blocks):
260 for b in blocks:
263 for b in blocks:
261 if b['type'] in ('paragraph', 'section'):
264 if b['type'] in ('paragraph', 'section'):
262 b['lines'] = [_hgrolere.sub(r'"hg \1"', l) for l in b['lines']]
265 b['lines'] = [_hgrolere.sub(r'"hg \1"', l) for l in b['lines']]
263 return blocks
266 return blocks
264
267
265
268
266 def addmargins(blocks):
269 def addmargins(blocks):
267 """Adds empty blocks for vertical spacing.
270 """Adds empty blocks for vertical spacing.
268
271
269 This groups bullets, options, and definitions together with no vertical
272 This groups bullets, options, and definitions together with no vertical
270 space between them, and adds an empty block between all other blocks.
273 space between them, and adds an empty block between all other blocks.
271 """
274 """
272 i = 1
275 i = 1
273 while i < len(blocks):
276 while i < len(blocks):
274 if (blocks[i]['type'] == blocks[i - 1]['type'] and
277 if (blocks[i]['type'] == blocks[i - 1]['type'] and
275 blocks[i]['type'] in ('bullet', 'option', 'field')):
278 blocks[i]['type'] in ('bullet', 'option', 'field')):
276 i += 1
279 i += 1
277 else:
280 else:
278 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
281 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
279 i += 2
282 i += 2
280 return blocks
283 return blocks
281
284
282
285
283 def formatblock(block, width):
286 def formatblock(block, width):
284 """Format a block according to width."""
287 """Format a block according to width."""
285 if width <= 0:
288 if width <= 0:
286 width = 78
289 width = 78
287 indent = ' ' * block['indent']
290 indent = ' ' * block['indent']
288 if block['type'] == 'margin':
291 if block['type'] == 'margin':
289 return ''
292 return ''
290 if block['type'] == 'literal':
293 if block['type'] == 'literal':
291 indent += ' '
294 indent += ' '
292 return indent + ('\n' + indent).join(block['lines'])
295 return indent + ('\n' + indent).join(block['lines'])
293 if block['type'] == 'section':
296 if block['type'] == 'section':
294 underline = len(block['lines'][0]) * block['underline']
297 underline = len(block['lines'][0]) * block['underline']
295 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
298 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
296 if block['type'] == 'definition':
299 if block['type'] == 'definition':
297 term = indent + block['lines'][0]
300 term = indent + block['lines'][0]
298 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
301 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
299 defindent = indent + hang * ' '
302 defindent = indent + hang * ' '
300 text = ' '.join(map(str.strip, block['lines'][1:]))
303 text = ' '.join(map(str.strip, block['lines'][1:]))
301 return "%s\n%s" % (term, textwrap.fill(text, width=width,
304 return "%s\n%s" % (term, textwrap.fill(text, width=width,
302 initial_indent=defindent,
305 initial_indent=defindent,
303 subsequent_indent=defindent))
306 subsequent_indent=defindent))
304 subindent = indent
307 subindent = indent
305 if block['type'] == 'bullet':
308 if block['type'] == 'bullet':
306 if block['lines'][0].startswith('| '):
309 if block['lines'][0].startswith('| '):
307 # Remove bullet for line blocks and add no extra
310 # Remove bullet for line blocks and add no extra
308 # indention.
311 # indention.
309 block['lines'][0] = block['lines'][0][2:]
312 block['lines'][0] = block['lines'][0][2:]
310 else:
313 else:
311 m = _bulletre.match(block['lines'][0])
314 m = _bulletre.match(block['lines'][0])
312 subindent = indent + m.end() * ' '
315 subindent = indent + m.end() * ' '
313 elif block['type'] == 'field':
316 elif block['type'] == 'field':
314 keywidth = block['keywidth']
317 keywidth = block['keywidth']
315 key = block['key']
318 key = block['key']
316
319
317 subindent = indent + _fieldwidth * ' '
320 subindent = indent + _fieldwidth * ' '
318 if len(key) + 2 > _fieldwidth:
321 if len(key) + 2 > _fieldwidth:
319 # key too large, use full line width
322 # key too large, use full line width
320 key = key.ljust(width)
323 key = key.ljust(width)
321 elif keywidth + 2 < _fieldwidth:
324 elif keywidth + 2 < _fieldwidth:
322 # all keys are small, add only two spaces
325 # all keys are small, add only two spaces
323 key = key.ljust(keywidth + 2)
326 key = key.ljust(keywidth + 2)
324 subindent = indent + (keywidth + 2) * ' '
327 subindent = indent + (keywidth + 2) * ' '
325 else:
328 else:
326 # mixed sizes, use fieldwidth for this one
329 # mixed sizes, use fieldwidth for this one
327 key = key.ljust(_fieldwidth)
330 key = key.ljust(_fieldwidth)
328 block['lines'][0] = key + block['lines'][0]
331 block['lines'][0] = key + block['lines'][0]
329 elif block['type'] == 'option':
332 elif block['type'] == 'option':
330 m = _optionre.match(block['lines'][0])
333 m = _optionre.match(block['lines'][0])
331 option, arg, rest = m.groups()
334 option, arg, rest = m.groups()
332 subindent = indent + (len(option) + len(arg)) * ' '
335 subindent = indent + (len(option) + len(arg)) * ' '
333
336
334 text = ' '.join(map(str.strip, block['lines']))
337 text = ' '.join(map(str.strip, block['lines']))
335 return textwrap.fill(text, width=width,
338 return textwrap.fill(text, width=width,
336 initial_indent=indent,
339 initial_indent=indent,
337 subsequent_indent=subindent)
340 subsequent_indent=subindent)
338
341
339
342
340 def format(text, width, indent=0, keep=None):
343 def format(text, width, indent=0, keep=None):
341 """Parse and format the text according to width."""
344 """Parse and format the text according to width."""
342 blocks = findblocks(text)
345 blocks = findblocks(text)
343 for b in blocks:
346 for b in blocks:
344 b['indent'] += indent
347 b['indent'] += indent
345 blocks = findliteralblocks(blocks)
348 blocks = findliteralblocks(blocks)
346 blocks, pruned = prunecontainers(blocks, keep or [])
349 blocks, pruned = prunecontainers(blocks, keep or [])
347 blocks = findsections(blocks)
350 blocks = findsections(blocks)
348 blocks = inlineliterals(blocks)
351 blocks = inlineliterals(blocks)
349 blocks = hgrole(blocks)
352 blocks = hgrole(blocks)
350 blocks = splitparagraphs(blocks)
353 blocks = splitparagraphs(blocks)
351 blocks = updatefieldlists(blocks)
354 blocks = updatefieldlists(blocks)
352 blocks = addmargins(blocks)
355 blocks = addmargins(blocks)
353 text = '\n'.join(formatblock(b, width) for b in blocks)
356 text = '\n'.join(formatblock(b, width) for b in blocks)
354 if keep is None:
357 if keep is None:
355 return text
358 return text
356 else:
359 else:
357 return text, pruned
360 return text, pruned
358
361
359
362
360 if __name__ == "__main__":
363 if __name__ == "__main__":
361 from pprint import pprint
364 from pprint import pprint
362
365
363 def debug(func, *args):
366 def debug(func, *args):
364 blocks = func(*args)
367 blocks = func(*args)
365 print "*** after %s:" % func.__name__
368 print "*** after %s:" % func.__name__
366 pprint(blocks)
369 pprint(blocks)
367 print
370 print
368 return blocks
371 return blocks
369
372
370 text = open(sys.argv[1]).read()
373 text = open(sys.argv[1]).read()
371 blocks = debug(findblocks, text)
374 blocks = debug(findblocks, text)
372 blocks = debug(findliteralblocks, blocks)
375 blocks = debug(findliteralblocks, blocks)
373 blocks = debug(prunecontainers, blocks, sys.argv[2:])
376 blocks = debug(prunecontainers, blocks, sys.argv[2:])
374 blocks = debug(inlineliterals, blocks)
377 blocks = debug(inlineliterals, blocks)
375 blocks = debug(splitparagraphs, blocks)
378 blocks = debug(splitparagraphs, blocks)
376 blocks = debug(updatefieldlists, blocks)
379 blocks = debug(updatefieldlists, blocks)
377 blocks = debug(findsections, blocks)
380 blocks = debug(findsections, blocks)
378 blocks = debug(addmargins, blocks)
381 blocks = debug(addmargins, blocks)
379 print '\n'.join(formatblock(b, 30) for b in blocks)
382 print '\n'.join(formatblock(b, 30) for b in blocks)
@@ -1,193 +1,199 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 from pprint import pprint
3 from pprint import pprint
4 from mercurial import minirst
4 from mercurial import minirst
5
5
6 def debugformat(title, text, width, **kwargs):
6 def debugformat(title, text, width, **kwargs):
7 print "%s formatted to fit within %d characters:" % (title, width)
7 print "%s formatted to fit within %d characters:" % (title, width)
8 print "-" * 70
8 print "-" * 70
9 formatted = minirst.format(text, width, **kwargs)
9 formatted = minirst.format(text, width, **kwargs)
10 if type(formatted) == tuple:
10 if type(formatted) == tuple:
11 print formatted[0]
11 print formatted[0]
12 print "-" * 70
12 print "-" * 70
13 pprint(formatted[1])
13 pprint(formatted[1])
14 else:
14 else:
15 print formatted
15 print formatted
16 print "-" * 70
16 print "-" * 70
17 print
17 print
18
18
19 paragraphs = """
19 paragraphs = """
20 This is some text in the first paragraph.
20 This is some text in the first paragraph.
21
21
22 A small indented paragraph.
22 A small indented paragraph.
23 It is followed by some lines
23 It is followed by some lines
24 containing random whitespace.
24 containing random whitespace.
25 \n \n \nThe third and final paragraph.
25 \n \n \nThe third and final paragraph.
26 """
26 """
27
27
28 debugformat('paragraphs', paragraphs, 60)
28 debugformat('paragraphs', paragraphs, 60)
29 debugformat('paragraphs', paragraphs, 30)
29 debugformat('paragraphs', paragraphs, 30)
30
30
31
31
32 definitions = """
32 definitions = """
33 A Term
33 A Term
34 Definition. The indented
34 Definition. The indented
35 lines make up the definition.
35 lines make up the definition.
36 Another Term
36 Another Term
37 Another definition. The final line in the
37 Another definition. The final line in the
38 definition determines the indentation, so
38 definition determines the indentation, so
39 this will be indented with four spaces.
39 this will be indented with four spaces.
40
40
41 A Nested/Indented Term
41 A Nested/Indented Term
42 Definition.
42 Definition.
43 """
43 """
44
44
45 debugformat('definitions', definitions, 60)
45 debugformat('definitions', definitions, 60)
46 debugformat('definitions', definitions, 30)
46 debugformat('definitions', definitions, 30)
47
47
48
48
49 literals = r"""
49 literals = r"""
50 The fully minimized form is the most
50 The fully minimized form is the most
51 convenient form::
51 convenient form::
52
52
53 Hello
53 Hello
54 literal
54 literal
55 world
55 world
56
56
57 In the partially minimized form a paragraph
57 In the partially minimized form a paragraph
58 simply ends with space-double-colon. ::
58 simply ends with space-double-colon. ::
59
59
60 ////////////////////////////////////////
60 ////////////////////////////////////////
61 long un-wrapped line in a literal block
61 long un-wrapped line in a literal block
62 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
62 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
63
63
64 ::
64 ::
65
65
66 This literal block is started with '::',
66 This literal block is started with '::',
67 the so-called expanded form. The paragraph
67 the so-called expanded form. The paragraph
68 with '::' disappears in the final output.
68 with '::' disappears in the final output.
69 """
69 """
70
70
71 debugformat('literals', literals, 60)
71 debugformat('literals', literals, 60)
72 debugformat('literals', literals, 30)
72 debugformat('literals', literals, 30)
73
73
74
74
75 lists = """
75 lists = """
76 - This is the first list item.
76 - This is the first list item.
77
77
78 Second paragraph in the first list item.
78 Second paragraph in the first list item.
79
79
80 - List items need not be separated
80 - List items need not be separated
81 by a blank line.
81 by a blank line.
82 - And will be rendered without
82 - And will be rendered without
83 one in any case.
83 one in any case.
84
84
85 We can have indented lists:
85 We can have indented lists:
86
86
87 - This is an indented list item
87 - This is an indented list item
88
88
89 - Another indented list item::
89 - Another indented list item::
90
90
91 - A literal block in the middle
91 - A literal block in the middle
92 of an indented list.
92 of an indented list.
93
93
94 (The above is not a list item since we are in the literal block.)
94 (The above is not a list item since we are in the literal block.)
95
95
96 ::
96 ::
97
97
98 Literal block with no indentation (apart from
98 Literal block with no indentation (apart from
99 the two spaces added to all literal blocks).
99 the two spaces added to all literal blocks).
100
100
101 1. This is an enumerated list (first item).
101 1. This is an enumerated list (first item).
102 2. Continuing with the second item.
102 2. Continuing with the second item.
103
103
104 (1) foo
104 (1) foo
105 (2) bar
105 (2) bar
106
106
107 1) Another
107 1) Another
108 2) List
108 2) List
109
109
110 Line blocks are also a form of list:
110 Line blocks are also a form of list:
111
111
112 | This is the first line.
112 | This is the first line.
113 The line continues here.
113 The line continues here.
114 | This is the second line.
114 | This is the second line.
115 """
115 """
116
116
117 debugformat('lists', lists, 60)
117 debugformat('lists', lists, 60)
118 debugformat('lists', lists, 30)
118 debugformat('lists', lists, 30)
119
119
120
120
121 options = """
121 options = """
122 There is support for simple option lists,
122 There is support for simple option lists,
123 but only with long options:
123 but only with long options:
124
124
125 --all Output all.
125 --all Output all.
126 --both Output both (this description is
126 --both Output both (this description is
127 quite long).
127 quite long).
128 --long Output all day long.
128 --long Output all day long.
129
129
130 --par This option has two paragraphs in its description.
130 --par This option has two paragraphs in its description.
131 This is the first.
131 This is the first.
132
132
133 This is the second. Blank lines may be omitted between
133 This is the second. Blank lines may be omitted between
134 options (as above) or left in (as here).
134 options (as above) or left in (as here).
135
135
136 The next paragraph looks like an option list, but lacks the two-space
136 The next paragraph looks like an option list, but lacks the two-space
137 marker after the option. It is treated as a normal paragraph:
137 marker after the option. It is treated as a normal paragraph:
138
138
139 --foo bar baz
139 --foo bar baz
140 """
140 """
141
141
142 debugformat('options', options, 60)
142 debugformat('options', options, 60)
143 debugformat('options', options, 30)
143 debugformat('options', options, 30)
144
144
145
145
146 fields = """
146 fields = """
147 :a: First item.
147 :a: First item.
148 :ab: Second item. Indentation and wrapping
148 :ab: Second item. Indentation and wrapping
149 is handled automatically.
149 is handled automatically.
150
150
151 Next list:
151 Next list:
152
152
153 :small: The larger key below triggers full indentation here.
153 :small: The larger key below triggers full indentation here.
154 :much too large: This key is big enough to get its own line.
154 :much too large: This key is big enough to get its own line.
155 """
155 """
156
156
157 debugformat('fields', fields, 60)
157 debugformat('fields', fields, 60)
158 debugformat('fields', fields, 30)
158 debugformat('fields', fields, 30)
159
159
160 containers = """
160 containers = """
161 Normal output.
161 Normal output.
162
162
163 .. container:: debug
163 .. container:: debug
164
164
165 Initial debug output.
165 Initial debug output.
166
166
167 .. container:: verbose
167 .. container:: verbose
168
168
169 Verbose output.
169 Verbose output.
170
170
171 .. container:: debug
171 .. container:: debug
172
172
173 Debug output.
173 Debug output.
174 """
174 """
175
175
176 debugformat('containers (normal)', containers, 60)
176 debugformat('containers (normal)', containers, 60)
177 debugformat('containers (verbose)', containers, 60, keep=['verbose'])
177 debugformat('containers (verbose)', containers, 60, keep=['verbose'])
178 debugformat('containers (debug)', containers, 60, keep=['debug'])
178 debugformat('containers (debug)', containers, 60, keep=['debug'])
179 debugformat('containers (verbose debug)', containers, 60,
179 debugformat('containers (verbose debug)', containers, 60,
180 keep=['verbose', 'debug'])
180 keep=['verbose', 'debug'])
181
181
182 roles = """Please see :hg:`add`."""
182 roles = """Please see :hg:`add`."""
183 debugformat('roles', roles, 60)
183 debugformat('roles', roles, 60)
184
184
185
185
186 sections = """
186 sections = """
187 A Somewhat Wide Section Header
187 Title
188 ------------------------------
188 =====
189
190 Section
191 -------
192
193 Subsection
194 ''''''''''
189
195
190 Markup: ``foo`` and :hg:`help`
196 Markup: ``foo`` and :hg:`help`
191 ------------------------------
197 ------------------------------
192 """
198 """
193 debugformat('sections', sections, 20)
199 debugformat('sections', sections, 20)
@@ -1,314 +1,320 b''
1 paragraphs formatted to fit within 60 characters:
1 paragraphs formatted to fit within 60 characters:
2 ----------------------------------------------------------------------
2 ----------------------------------------------------------------------
3 This is some text in the first paragraph.
3 This is some text in the first paragraph.
4
4
5 A small indented paragraph. It is followed by some lines
5 A small indented paragraph. It is followed by some lines
6 containing random whitespace.
6 containing random whitespace.
7
7
8 The third and final paragraph.
8 The third and final paragraph.
9 ----------------------------------------------------------------------
9 ----------------------------------------------------------------------
10
10
11 paragraphs formatted to fit within 30 characters:
11 paragraphs formatted to fit within 30 characters:
12 ----------------------------------------------------------------------
12 ----------------------------------------------------------------------
13 This is some text in the first
13 This is some text in the first
14 paragraph.
14 paragraph.
15
15
16 A small indented paragraph.
16 A small indented paragraph.
17 It is followed by some lines
17 It is followed by some lines
18 containing random
18 containing random
19 whitespace.
19 whitespace.
20
20
21 The third and final paragraph.
21 The third and final paragraph.
22 ----------------------------------------------------------------------
22 ----------------------------------------------------------------------
23
23
24 definitions formatted to fit within 60 characters:
24 definitions formatted to fit within 60 characters:
25 ----------------------------------------------------------------------
25 ----------------------------------------------------------------------
26 A Term
26 A Term
27 Definition. The indented lines make up the definition.
27 Definition. The indented lines make up the definition.
28
28
29 Another Term
29 Another Term
30 Another definition. The final line in the definition
30 Another definition. The final line in the definition
31 determines the indentation, so this will be indented
31 determines the indentation, so this will be indented
32 with four spaces.
32 with four spaces.
33
33
34 A Nested/Indented Term
34 A Nested/Indented Term
35 Definition.
35 Definition.
36 ----------------------------------------------------------------------
36 ----------------------------------------------------------------------
37
37
38 definitions formatted to fit within 30 characters:
38 definitions formatted to fit within 30 characters:
39 ----------------------------------------------------------------------
39 ----------------------------------------------------------------------
40 A Term
40 A Term
41 Definition. The indented
41 Definition. The indented
42 lines make up the
42 lines make up the
43 definition.
43 definition.
44
44
45 Another Term
45 Another Term
46 Another definition. The
46 Another definition. The
47 final line in the
47 final line in the
48 definition determines the
48 definition determines the
49 indentation, so this will
49 indentation, so this will
50 be indented with four
50 be indented with four
51 spaces.
51 spaces.
52
52
53 A Nested/Indented Term
53 A Nested/Indented Term
54 Definition.
54 Definition.
55 ----------------------------------------------------------------------
55 ----------------------------------------------------------------------
56
56
57 literals formatted to fit within 60 characters:
57 literals formatted to fit within 60 characters:
58 ----------------------------------------------------------------------
58 ----------------------------------------------------------------------
59 The fully minimized form is the most convenient form:
59 The fully minimized form is the most convenient form:
60
60
61 Hello
61 Hello
62 literal
62 literal
63 world
63 world
64
64
65 In the partially minimized form a paragraph simply ends with
65 In the partially minimized form a paragraph simply ends with
66 space-double-colon.
66 space-double-colon.
67
67
68 ////////////////////////////////////////
68 ////////////////////////////////////////
69 long un-wrapped line in a literal block
69 long un-wrapped line in a literal block
70 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
70 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
71
71
72 This literal block is started with '::',
72 This literal block is started with '::',
73 the so-called expanded form. The paragraph
73 the so-called expanded form. The paragraph
74 with '::' disappears in the final output.
74 with '::' disappears in the final output.
75 ----------------------------------------------------------------------
75 ----------------------------------------------------------------------
76
76
77 literals formatted to fit within 30 characters:
77 literals formatted to fit within 30 characters:
78 ----------------------------------------------------------------------
78 ----------------------------------------------------------------------
79 The fully minimized form is
79 The fully minimized form is
80 the most convenient form:
80 the most convenient form:
81
81
82 Hello
82 Hello
83 literal
83 literal
84 world
84 world
85
85
86 In the partially minimized
86 In the partially minimized
87 form a paragraph simply ends
87 form a paragraph simply ends
88 with space-double-colon.
88 with space-double-colon.
89
89
90 ////////////////////////////////////////
90 ////////////////////////////////////////
91 long un-wrapped line in a literal block
91 long un-wrapped line in a literal block
92 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
92 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
93
93
94 This literal block is started with '::',
94 This literal block is started with '::',
95 the so-called expanded form. The paragraph
95 the so-called expanded form. The paragraph
96 with '::' disappears in the final output.
96 with '::' disappears in the final output.
97 ----------------------------------------------------------------------
97 ----------------------------------------------------------------------
98
98
99 lists formatted to fit within 60 characters:
99 lists formatted to fit within 60 characters:
100 ----------------------------------------------------------------------
100 ----------------------------------------------------------------------
101 - This is the first list item.
101 - This is the first list item.
102
102
103 Second paragraph in the first list item.
103 Second paragraph in the first list item.
104
104
105 - List items need not be separated by a blank line.
105 - List items need not be separated by a blank line.
106 - And will be rendered without one in any case.
106 - And will be rendered without one in any case.
107
107
108 We can have indented lists:
108 We can have indented lists:
109
109
110 - This is an indented list item
110 - This is an indented list item
111 - Another indented list item:
111 - Another indented list item:
112
112
113 - A literal block in the middle
113 - A literal block in the middle
114 of an indented list.
114 of an indented list.
115
115
116 (The above is not a list item since we are in the literal block.)
116 (The above is not a list item since we are in the literal block.)
117
117
118 Literal block with no indentation (apart from
118 Literal block with no indentation (apart from
119 the two spaces added to all literal blocks).
119 the two spaces added to all literal blocks).
120
120
121 1. This is an enumerated list (first item).
121 1. This is an enumerated list (first item).
122 2. Continuing with the second item.
122 2. Continuing with the second item.
123 (1) foo
123 (1) foo
124 (2) bar
124 (2) bar
125 1) Another
125 1) Another
126 2) List
126 2) List
127
127
128 Line blocks are also a form of list:
128 Line blocks are also a form of list:
129
129
130 This is the first line. The line continues here.
130 This is the first line. The line continues here.
131 This is the second line.
131 This is the second line.
132 ----------------------------------------------------------------------
132 ----------------------------------------------------------------------
133
133
134 lists formatted to fit within 30 characters:
134 lists formatted to fit within 30 characters:
135 ----------------------------------------------------------------------
135 ----------------------------------------------------------------------
136 - This is the first list item.
136 - This is the first list item.
137
137
138 Second paragraph in the
138 Second paragraph in the
139 first list item.
139 first list item.
140
140
141 - List items need not be
141 - List items need not be
142 separated by a blank line.
142 separated by a blank line.
143 - And will be rendered without
143 - And will be rendered without
144 one in any case.
144 one in any case.
145
145
146 We can have indented lists:
146 We can have indented lists:
147
147
148 - This is an indented list
148 - This is an indented list
149 item
149 item
150 - Another indented list
150 - Another indented list
151 item:
151 item:
152
152
153 - A literal block in the middle
153 - A literal block in the middle
154 of an indented list.
154 of an indented list.
155
155
156 (The above is not a list item since we are in the literal block.)
156 (The above is not a list item since we are in the literal block.)
157
157
158 Literal block with no indentation (apart from
158 Literal block with no indentation (apart from
159 the two spaces added to all literal blocks).
159 the two spaces added to all literal blocks).
160
160
161 1. This is an enumerated list
161 1. This is an enumerated list
162 (first item).
162 (first item).
163 2. Continuing with the second
163 2. Continuing with the second
164 item.
164 item.
165 (1) foo
165 (1) foo
166 (2) bar
166 (2) bar
167 1) Another
167 1) Another
168 2) List
168 2) List
169
169
170 Line blocks are also a form of
170 Line blocks are also a form of
171 list:
171 list:
172
172
173 This is the first line. The
173 This is the first line. The
174 line continues here.
174 line continues here.
175 This is the second line.
175 This is the second line.
176 ----------------------------------------------------------------------
176 ----------------------------------------------------------------------
177
177
178 options formatted to fit within 60 characters:
178 options formatted to fit within 60 characters:
179 ----------------------------------------------------------------------
179 ----------------------------------------------------------------------
180 There is support for simple option lists, but only with long
180 There is support for simple option lists, but only with long
181 options:
181 options:
182
182
183 --all Output all.
183 --all Output all.
184 --both Output both (this description is quite long).
184 --both Output both (this description is quite long).
185 --long Output all day long.
185 --long Output all day long.
186 --par This option has two paragraphs in its
186 --par This option has two paragraphs in its
187 description. This is the first.
187 description. This is the first.
188
188
189 This is the second. Blank lines may be omitted
189 This is the second. Blank lines may be omitted
190 between options (as above) or left in (as here).
190 between options (as above) or left in (as here).
191
191
192 The next paragraph looks like an option list, but lacks the
192 The next paragraph looks like an option list, but lacks the
193 two-space marker after the option. It is treated as a normal
193 two-space marker after the option. It is treated as a normal
194 paragraph:
194 paragraph:
195
195
196 --foo bar baz
196 --foo bar baz
197 ----------------------------------------------------------------------
197 ----------------------------------------------------------------------
198
198
199 options formatted to fit within 30 characters:
199 options formatted to fit within 30 characters:
200 ----------------------------------------------------------------------
200 ----------------------------------------------------------------------
201 There is support for simple
201 There is support for simple
202 option lists, but only with
202 option lists, but only with
203 long options:
203 long options:
204
204
205 --all Output all.
205 --all Output all.
206 --both Output both (this
206 --both Output both (this
207 description is
207 description is
208 quite long).
208 quite long).
209 --long Output all day
209 --long Output all day
210 long.
210 long.
211 --par This option has two
211 --par This option has two
212 paragraphs in its
212 paragraphs in its
213 description. This
213 description. This
214 is the first.
214 is the first.
215
215
216 This is the second.
216 This is the second.
217 Blank lines may be
217 Blank lines may be
218 omitted between
218 omitted between
219 options (as above)
219 options (as above)
220 or left in (as
220 or left in (as
221 here).
221 here).
222
222
223 The next paragraph looks like
223 The next paragraph looks like
224 an option list, but lacks the
224 an option list, but lacks the
225 two-space marker after the
225 two-space marker after the
226 option. It is treated as a
226 option. It is treated as a
227 normal paragraph:
227 normal paragraph:
228
228
229 --foo bar baz
229 --foo bar baz
230 ----------------------------------------------------------------------
230 ----------------------------------------------------------------------
231
231
232 fields formatted to fit within 60 characters:
232 fields formatted to fit within 60 characters:
233 ----------------------------------------------------------------------
233 ----------------------------------------------------------------------
234 a First item.
234 a First item.
235 ab Second item. Indentation and wrapping is handled
235 ab Second item. Indentation and wrapping is handled
236 automatically.
236 automatically.
237
237
238 Next list:
238 Next list:
239
239
240 small The larger key below triggers full indentation
240 small The larger key below triggers full indentation
241 here.
241 here.
242 much too large
242 much too large
243 This key is big enough to get its own line.
243 This key is big enough to get its own line.
244 ----------------------------------------------------------------------
244 ----------------------------------------------------------------------
245
245
246 fields formatted to fit within 30 characters:
246 fields formatted to fit within 30 characters:
247 ----------------------------------------------------------------------
247 ----------------------------------------------------------------------
248 a First item.
248 a First item.
249 ab Second item. Indentation
249 ab Second item. Indentation
250 and wrapping is handled
250 and wrapping is handled
251 automatically.
251 automatically.
252
252
253 Next list:
253 Next list:
254
254
255 small The larger key
255 small The larger key
256 below triggers
256 below triggers
257 full indentation
257 full indentation
258 here.
258 here.
259 much too large
259 much too large
260 This key is big
260 This key is big
261 enough to get its
261 enough to get its
262 own line.
262 own line.
263 ----------------------------------------------------------------------
263 ----------------------------------------------------------------------
264
264
265 containers (normal) formatted to fit within 60 characters:
265 containers (normal) formatted to fit within 60 characters:
266 ----------------------------------------------------------------------
266 ----------------------------------------------------------------------
267 Normal output.
267 Normal output.
268 ----------------------------------------------------------------------
268 ----------------------------------------------------------------------
269
269
270 containers (verbose) formatted to fit within 60 characters:
270 containers (verbose) formatted to fit within 60 characters:
271 ----------------------------------------------------------------------
271 ----------------------------------------------------------------------
272 Normal output.
272 Normal output.
273
273
274 Verbose output.
274 Verbose output.
275 ----------------------------------------------------------------------
275 ----------------------------------------------------------------------
276 ['debug', 'debug']
276 ['debug', 'debug']
277 ----------------------------------------------------------------------
277 ----------------------------------------------------------------------
278
278
279 containers (debug) formatted to fit within 60 characters:
279 containers (debug) formatted to fit within 60 characters:
280 ----------------------------------------------------------------------
280 ----------------------------------------------------------------------
281 Normal output.
281 Normal output.
282
282
283 Initial debug output.
283 Initial debug output.
284 ----------------------------------------------------------------------
284 ----------------------------------------------------------------------
285 ['verbose']
285 ['verbose']
286 ----------------------------------------------------------------------
286 ----------------------------------------------------------------------
287
287
288 containers (verbose debug) formatted to fit within 60 characters:
288 containers (verbose debug) formatted to fit within 60 characters:
289 ----------------------------------------------------------------------
289 ----------------------------------------------------------------------
290 Normal output.
290 Normal output.
291
291
292 Initial debug output.
292 Initial debug output.
293
293
294 Verbose output.
294 Verbose output.
295
295
296 Debug output.
296 Debug output.
297 ----------------------------------------------------------------------
297 ----------------------------------------------------------------------
298 []
298 []
299 ----------------------------------------------------------------------
299 ----------------------------------------------------------------------
300
300
301 roles formatted to fit within 60 characters:
301 roles formatted to fit within 60 characters:
302 ----------------------------------------------------------------------
302 ----------------------------------------------------------------------
303 Please see "hg add".
303 Please see "hg add".
304 ----------------------------------------------------------------------
304 ----------------------------------------------------------------------
305
305
306 sections formatted to fit within 20 characters:
306 sections formatted to fit within 20 characters:
307 ----------------------------------------------------------------------
307 ----------------------------------------------------------------------
308 A Somewhat Wide Section Header
308 Title
309 ------------------------------
309 =====
310
311 Section
312 -------
313
314 Subsection
315 ''''''''''
310
316
311 Markup: "foo" and "hg help"
317 Markup: "foo" and "hg help"
312 ---------------------------
318 ---------------------------
313 ----------------------------------------------------------------------
319 ----------------------------------------------------------------------
314
320
General Comments 0
You need to be logged in to leave comments. Login now