##// END OF EJS Templates
minirst: add a helper function to build an RST table from an array
Matt Mackall -
r15039:c981f4a9 default
parent child Browse files
Show More
@@ -1,555 +1,571 b''
1 1 # minirst.py - minimal reStructuredText parser
2 2 #
3 3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """simplified reStructuredText parser.
9 9
10 10 This parser knows just enough about reStructuredText to parse the
11 11 Mercurial docstrings.
12 12
13 13 It cheats in a major way: nested blocks are not really nested. They
14 14 are just indented blocks that look like they are nested. This relies
15 15 on the user to keep the right indentation for the blocks.
16 16
17 17 Remember to update http://mercurial.selenic.com/wiki/HelpStyleGuide
18 18 when adding support for new constructs.
19 19 """
20 20
21 21 import re
22 22 import util, encoding
23 23 from i18n import _
24 24
25 25
26 26 def replace(text, substs):
27 27 utext = text.decode(encoding.encoding)
28 28 for f, t in substs:
29 29 utext = utext.replace(f, t)
30 30 return utext.encode(encoding.encoding)
31 31
32 32
33 33 _blockre = re.compile(r"\n(?:\s*\n)+")
34 34
35 35 def findblocks(text):
36 36 """Find continuous blocks of lines in text.
37 37
38 38 Returns a list of dictionaries representing the blocks. Each block
39 39 has an 'indent' field and a 'lines' field.
40 40 """
41 41 blocks = []
42 42 for b in _blockre.split(text.lstrip('\n').rstrip()):
43 43 lines = b.splitlines()
44 44 indent = min((len(l) - len(l.lstrip())) for l in lines)
45 45 lines = [l[indent:] for l in lines]
46 46 blocks.append(dict(indent=indent, lines=lines))
47 47 return blocks
48 48
49 49
50 50 def findliteralblocks(blocks):
51 51 """Finds literal blocks and adds a 'type' field to the blocks.
52 52
53 53 Literal blocks are given the type 'literal', all other blocks are
54 54 given type the 'paragraph'.
55 55 """
56 56 i = 0
57 57 while i < len(blocks):
58 58 # Searching for a block that looks like this:
59 59 #
60 60 # +------------------------------+
61 61 # | paragraph |
62 62 # | (ends with "::") |
63 63 # +------------------------------+
64 64 # +---------------------------+
65 65 # | indented literal block |
66 66 # +---------------------------+
67 67 blocks[i]['type'] = 'paragraph'
68 68 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
69 69 indent = blocks[i]['indent']
70 70 adjustment = blocks[i + 1]['indent'] - indent
71 71
72 72 if blocks[i]['lines'] == ['::']:
73 73 # Expanded form: remove block
74 74 del blocks[i]
75 75 i -= 1
76 76 elif blocks[i]['lines'][-1].endswith(' ::'):
77 77 # Partially minimized form: remove space and both
78 78 # colons.
79 79 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
80 80 else:
81 81 # Fully minimized form: remove just one colon.
82 82 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
83 83
84 84 # List items are formatted with a hanging indent. We must
85 85 # correct for this here while we still have the original
86 86 # information on the indentation of the subsequent literal
87 87 # blocks available.
88 88 m = _bulletre.match(blocks[i]['lines'][0])
89 89 if m:
90 90 indent += m.end()
91 91 adjustment -= m.end()
92 92
93 93 # Mark the following indented blocks.
94 94 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
95 95 blocks[i + 1]['type'] = 'literal'
96 96 blocks[i + 1]['indent'] -= adjustment
97 97 i += 1
98 98 i += 1
99 99 return blocks
100 100
101 101 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
102 102 _optionre = re.compile(r'^(-([a-zA-Z0-9]), )?(--[a-z0-9-]+)'
103 103 r'((.*) +)(.*)$')
104 104 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
105 105 _definitionre = re.compile(r'[^ ]')
106 106 _tablere = re.compile(r'(=+\s+)*=+')
107 107
108 108 def splitparagraphs(blocks):
109 109 """Split paragraphs into lists."""
110 110 # Tuples with (list type, item regexp, single line items?). Order
111 111 # matters: definition lists has the least specific regexp and must
112 112 # come last.
113 113 listtypes = [('bullet', _bulletre, True),
114 114 ('option', _optionre, True),
115 115 ('field', _fieldre, True),
116 116 ('definition', _definitionre, False)]
117 117
118 118 def match(lines, i, itemre, singleline):
119 119 """Does itemre match an item at line i?
120 120
121 121 A list item can be followed by an idented line or another list
122 122 item (but only if singleline is True).
123 123 """
124 124 line1 = lines[i]
125 125 line2 = i + 1 < len(lines) and lines[i + 1] or ''
126 126 if not itemre.match(line1):
127 127 return False
128 128 if singleline:
129 129 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
130 130 else:
131 131 return line2.startswith(' ')
132 132
133 133 i = 0
134 134 while i < len(blocks):
135 135 if blocks[i]['type'] == 'paragraph':
136 136 lines = blocks[i]['lines']
137 137 for type, itemre, singleline in listtypes:
138 138 if match(lines, 0, itemre, singleline):
139 139 items = []
140 140 for j, line in enumerate(lines):
141 141 if match(lines, j, itemre, singleline):
142 142 items.append(dict(type=type, lines=[],
143 143 indent=blocks[i]['indent']))
144 144 items[-1]['lines'].append(line)
145 145 blocks[i:i + 1] = items
146 146 break
147 147 i += 1
148 148 return blocks
149 149
150 150
151 151 _fieldwidth = 12
152 152
153 153 def updatefieldlists(blocks):
154 154 """Find key and maximum key width for field lists."""
155 155 i = 0
156 156 while i < len(blocks):
157 157 if blocks[i]['type'] != 'field':
158 158 i += 1
159 159 continue
160 160
161 161 keywidth = 0
162 162 j = i
163 163 while j < len(blocks) and blocks[j]['type'] == 'field':
164 164 m = _fieldre.match(blocks[j]['lines'][0])
165 165 key, rest = m.groups()
166 166 blocks[j]['lines'][0] = rest
167 167 blocks[j]['key'] = key
168 168 keywidth = max(keywidth, len(key))
169 169 j += 1
170 170
171 171 for block in blocks[i:j]:
172 172 block['keywidth'] = keywidth
173 173 i = j + 1
174 174
175 175 return blocks
176 176
177 177
178 178 def updateoptionlists(blocks):
179 179 i = 0
180 180 while i < len(blocks):
181 181 if blocks[i]['type'] != 'option':
182 182 i += 1
183 183 continue
184 184
185 185 optstrwidth = 0
186 186 j = i
187 187 while j < len(blocks) and blocks[j]['type'] == 'option':
188 188 m = _optionre.match(blocks[j]['lines'][0])
189 189
190 190 shortoption = m.group(2)
191 191 group3 = m.group(3)
192 192 longoption = group3[2:].strip()
193 193 desc = m.group(6).strip()
194 194 longoptionarg = m.group(5).strip()
195 195 blocks[j]['lines'][0] = desc
196 196
197 197 noshortop = ''
198 198 if not shortoption:
199 199 noshortop = ' '
200 200
201 201 opt = "%s%s" % (shortoption and "-%s " % shortoption or '',
202 202 ("%s--%s %s") % (noshortop, longoption,
203 203 longoptionarg))
204 204 opt = opt.rstrip()
205 205 blocks[j]['optstr'] = opt
206 206 optstrwidth = max(optstrwidth, encoding.colwidth(opt))
207 207 j += 1
208 208
209 209 for block in blocks[i:j]:
210 210 block['optstrwidth'] = optstrwidth
211 211 i = j + 1
212 212 return blocks
213 213
214 214 def prunecontainers(blocks, keep):
215 215 """Prune unwanted containers.
216 216
217 217 The blocks must have a 'type' field, i.e., they should have been
218 218 run through findliteralblocks first.
219 219 """
220 220 pruned = []
221 221 i = 0
222 222 while i + 1 < len(blocks):
223 223 # Searching for a block that looks like this:
224 224 #
225 225 # +-------+---------------------------+
226 226 # | ".. container ::" type |
227 227 # +---+ |
228 228 # | blocks |
229 229 # +-------------------------------+
230 230 if (blocks[i]['type'] == 'paragraph' and
231 231 blocks[i]['lines'][0].startswith('.. container::')):
232 232 indent = blocks[i]['indent']
233 233 adjustment = blocks[i + 1]['indent'] - indent
234 234 containertype = blocks[i]['lines'][0][15:]
235 235 prune = containertype not in keep
236 236 if prune:
237 237 pruned.append(containertype)
238 238
239 239 # Always delete "..container:: type" block
240 240 del blocks[i]
241 241 j = i
242 242 while j < len(blocks) and blocks[j]['indent'] > indent:
243 243 if prune:
244 244 del blocks[j]
245 245 i -= 1 # adjust outer index
246 246 else:
247 247 blocks[j]['indent'] -= adjustment
248 248 j += 1
249 249 i += 1
250 250 return blocks, pruned
251 251
252 252
253 253 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
254 254
255 255 def findtables(blocks):
256 256 '''Find simple tables
257 257
258 258 Only simple one-line table elements are supported
259 259 '''
260 260
261 261 for block in blocks:
262 262 # Searching for a block that looks like this:
263 263 #
264 264 # === ==== ===
265 265 # A B C
266 266 # === ==== === <- optional
267 267 # 1 2 3
268 268 # x y z
269 269 # === ==== ===
270 270 if (block['type'] == 'paragraph' and
271 271 len(block['lines']) > 4 and
272 272 _tablere.match(block['lines'][0]) and
273 273 block['lines'][0] == block['lines'][-1]):
274 274 block['type'] = 'table'
275 275 block['header'] = False
276 276 div = block['lines'][0]
277 277 columns = [x for x in xrange(len(div))
278 278 if div[x] == '=' and (x == 0 or div[x - 1] == ' ')]
279 279 rows = []
280 280 for l in block['lines'][1:-1]:
281 281 if l == div:
282 282 block['header'] = True
283 283 continue
284 284 row = []
285 285 for n, start in enumerate(columns):
286 286 if n + 1 < len(columns):
287 287 row.append(l[start:columns[n + 1]].strip())
288 288 else:
289 289 row.append(l[start:].strip())
290 290 rows.append(row)
291 291 block['table'] = rows
292 292
293 293 return blocks
294 294
295 295 def findsections(blocks):
296 296 """Finds sections.
297 297
298 298 The blocks must have a 'type' field, i.e., they should have been
299 299 run through findliteralblocks first.
300 300 """
301 301 for block in blocks:
302 302 # Searching for a block that looks like this:
303 303 #
304 304 # +------------------------------+
305 305 # | Section title |
306 306 # | ------------- |
307 307 # +------------------------------+
308 308 if (block['type'] == 'paragraph' and
309 309 len(block['lines']) == 2 and
310 310 encoding.colwidth(block['lines'][0]) == len(block['lines'][1]) and
311 311 _sectionre.match(block['lines'][1])):
312 312 block['underline'] = block['lines'][1][0]
313 313 block['type'] = 'section'
314 314 del block['lines'][1]
315 315 return blocks
316 316
317 317
318 318 def inlineliterals(blocks):
319 319 substs = [('``', '"')]
320 320 for b in blocks:
321 321 if b['type'] in ('paragraph', 'section'):
322 322 b['lines'] = [replace(l, substs) for l in b['lines']]
323 323 return blocks
324 324
325 325
326 326 def hgrole(blocks):
327 327 substs = [(':hg:`', '"hg '), ('`', '"')]
328 328 for b in blocks:
329 329 if b['type'] in ('paragraph', 'section'):
330 330 # Turn :hg:`command` into "hg command". This also works
331 331 # when there is a line break in the command and relies on
332 332 # the fact that we have no stray back-quotes in the input
333 333 # (run the blocks through inlineliterals first).
334 334 b['lines'] = [replace(l, substs) for l in b['lines']]
335 335 return blocks
336 336
337 337
338 338 def addmargins(blocks):
339 339 """Adds empty blocks for vertical spacing.
340 340
341 341 This groups bullets, options, and definitions together with no vertical
342 342 space between them, and adds an empty block between all other blocks.
343 343 """
344 344 i = 1
345 345 while i < len(blocks):
346 346 if (blocks[i]['type'] == blocks[i - 1]['type'] and
347 347 blocks[i]['type'] in ('bullet', 'option', 'field')):
348 348 i += 1
349 349 else:
350 350 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
351 351 i += 2
352 352 return blocks
353 353
354 354 def prunecomments(blocks):
355 355 """Remove comments."""
356 356 i = 0
357 357 while i < len(blocks):
358 358 b = blocks[i]
359 359 if b['type'] == 'paragraph' and (b['lines'][0].startswith('.. ') or
360 360 b['lines'] == ['..']):
361 361 del blocks[i]
362 362 if i < len(blocks) and blocks[i]['type'] == 'margin':
363 363 del blocks[i]
364 364 else:
365 365 i += 1
366 366 return blocks
367 367
368 368 _admonitionre = re.compile(r"\.\. (admonition|attention|caution|danger|"
369 369 r"error|hint|important|note|tip|warning)::",
370 370 flags=re.IGNORECASE)
371 371
372 372 def findadmonitions(blocks):
373 373 """
374 374 Makes the type of the block an admonition block if
375 375 the first line is an admonition directive
376 376 """
377 377 i = 0
378 378 while i < len(blocks):
379 379 m = _admonitionre.match(blocks[i]['lines'][0])
380 380 if m:
381 381 blocks[i]['type'] = 'admonition'
382 382 admonitiontitle = blocks[i]['lines'][0][3:m.end() - 2].lower()
383 383
384 384 firstline = blocks[i]['lines'][0][m.end() + 1:]
385 385 if firstline:
386 386 blocks[i]['lines'].insert(1, ' ' + firstline)
387 387
388 388 blocks[i]['admonitiontitle'] = admonitiontitle
389 389 del blocks[i]['lines'][0]
390 390 i = i + 1
391 391 return blocks
392 392
393 393 _admonitiontitles = {'attention': _('Attention:'),
394 394 'caution': _('Caution:'),
395 395 'danger': _('!Danger!') ,
396 396 'error': _('Error:'),
397 397 'hint': _('Hint:'),
398 398 'important': _('Important:'),
399 399 'note': _('Note:'),
400 400 'tip': _('Tip:'),
401 401 'warning': _('Warning!')}
402 402
403 403 def formatoption(block, width):
404 404 desc = ' '.join(map(str.strip, block['lines']))
405 405 colwidth = encoding.colwidth(block['optstr'])
406 406 usablewidth = width - 1
407 407 hanging = block['optstrwidth']
408 408 initindent = '%s%s ' % (block['optstr'], ' ' * ((hanging - colwidth)))
409 409 hangindent = ' ' * (encoding.colwidth(initindent) + 1)
410 410 return ' %s' % (util.wrap(desc, usablewidth,
411 411 initindent=initindent,
412 412 hangindent=hangindent))
413 413
414 414 def formatblock(block, width):
415 415 """Format a block according to width."""
416 416 if width <= 0:
417 417 width = 78
418 418 indent = ' ' * block['indent']
419 419 if block['type'] == 'admonition':
420 420 admonition = _admonitiontitles[block['admonitiontitle']]
421 421 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
422 422
423 423 defindent = indent + hang * ' '
424 424 text = ' '.join(map(str.strip, block['lines']))
425 425 return '%s\n%s' % (indent + admonition, util.wrap(text, width=width,
426 426 initindent=defindent,
427 427 hangindent=defindent))
428 428 if block['type'] == 'margin':
429 429 return ''
430 430 if block['type'] == 'literal':
431 431 indent += ' '
432 432 return indent + ('\n' + indent).join(block['lines'])
433 433 if block['type'] == 'section':
434 434 underline = encoding.colwidth(block['lines'][0]) * block['underline']
435 435 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
436 436 if block['type'] == 'table':
437 437 table = block['table']
438 438 # compute column widths
439 439 widths = [max([encoding.colwidth(e) for e in c]) for c in zip(*table)]
440 440 text = ''
441 441 span = sum(widths) + len(widths) - 1
442 442 indent = ' ' * block['indent']
443 443 hang = ' ' * (len(indent) + span - widths[-1])
444 444 f = ' '.join('%%-%ds' % n for n in widths)
445 445
446 446 for row in table:
447 447 l = f % tuple(row)
448 448 l = util.wrap(l, width=width, initindent=indent, hangindent=hang)
449 449 if not text and block['header']:
450 450 text = l + '\n' + indent + '-' * (min(width, span)) + '\n'
451 451 else:
452 452 text += l + "\n"
453 453 return text
454 454 if block['type'] == 'definition':
455 455 term = indent + block['lines'][0]
456 456 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
457 457 defindent = indent + hang * ' '
458 458 text = ' '.join(map(str.strip, block['lines'][1:]))
459 459 return '%s\n%s' % (term, util.wrap(text, width=width,
460 460 initindent=defindent,
461 461 hangindent=defindent))
462 462 subindent = indent
463 463 if block['type'] == 'bullet':
464 464 if block['lines'][0].startswith('| '):
465 465 # Remove bullet for line blocks and add no extra
466 466 # indention.
467 467 block['lines'][0] = block['lines'][0][2:]
468 468 else:
469 469 m = _bulletre.match(block['lines'][0])
470 470 subindent = indent + m.end() * ' '
471 471 elif block['type'] == 'field':
472 472 keywidth = block['keywidth']
473 473 key = block['key']
474 474
475 475 subindent = indent + _fieldwidth * ' '
476 476 if len(key) + 2 > _fieldwidth:
477 477 # key too large, use full line width
478 478 key = key.ljust(width)
479 479 elif keywidth + 2 < _fieldwidth:
480 480 # all keys are small, add only two spaces
481 481 key = key.ljust(keywidth + 2)
482 482 subindent = indent + (keywidth + 2) * ' '
483 483 else:
484 484 # mixed sizes, use fieldwidth for this one
485 485 key = key.ljust(_fieldwidth)
486 486 block['lines'][0] = key + block['lines'][0]
487 487 elif block['type'] == 'option':
488 488 return formatoption(block, width)
489 489
490 490 text = ' '.join(map(str.strip, block['lines']))
491 491 return util.wrap(text, width=width,
492 492 initindent=indent,
493 493 hangindent=subindent)
494 494
495 495 def parse(text, indent=0, keep=None):
496 496 """Parse text into a list of blocks"""
497 497 pruned = []
498 498 blocks = findblocks(text)
499 499 for b in blocks:
500 500 b['indent'] += indent
501 501 blocks = findliteralblocks(blocks)
502 502 blocks = findtables(blocks)
503 503 blocks, pruned = prunecontainers(blocks, keep or [])
504 504 blocks = findsections(blocks)
505 505 blocks = inlineliterals(blocks)
506 506 blocks = hgrole(blocks)
507 507 blocks = splitparagraphs(blocks)
508 508 blocks = updatefieldlists(blocks)
509 509 blocks = updateoptionlists(blocks)
510 510 blocks = addmargins(blocks)
511 511 blocks = prunecomments(blocks)
512 512 blocks = findadmonitions(blocks)
513 513 return blocks, pruned
514 514
515 515 def formatblocks(blocks, width):
516 516 text = '\n'.join(formatblock(b, width) for b in blocks)
517 517 return text
518 518
519 519 def format(text, width, indent=0, keep=None):
520 520 """Parse and format the text according to width."""
521 521 blocks, pruned = parse(text, indent, keep or [])
522 522 text = '\n'.join(formatblock(b, width) for b in blocks)
523 523 if keep is None:
524 524 return text
525 525 else:
526 526 return text, pruned
527 527
528 528 def getsections(blocks):
529 529 '''return a list of (section name, nesting level, blocks) tuples'''
530 530 nest = ""
531 531 level = 0
532 532 secs = []
533 533 for b in blocks:
534 534 if b['type'] == 'section':
535 535 i = b['underline']
536 536 if i not in nest:
537 537 nest += i
538 538 level = nest.index(i) + 1
539 539 nest = nest[:level]
540 540 secs.append((b['lines'][0], level, [b]))
541 541 else:
542 542 if not secs:
543 543 # add an initial empty section
544 544 secs = [('', 0, [])]
545 545 secs[-1][2].append(b)
546 546 return secs
547 547
548 548 def decorateblocks(blocks, width):
549 549 '''generate a list of (section name, line text) pairs for search'''
550 550 lines = []
551 551 for s in getsections(blocks):
552 552 section = s[0]
553 553 text = formatblocks(s[2], width)
554 554 lines.append([(section, l) for l in text.splitlines(True)])
555 555 return lines
556
557 def maketable(data, indent=0, header=False):
558 '''Generate an RST table for the given table data'''
559
560 widths = [max(encoding.colwidth(e) for e in c) for c in zip(*data)]
561 indent = ' ' * indent
562 f = indent + ' '.join('%%-%ds' % w for w in widths) + '\n'
563 div = indent + ' '.join('=' * w for w in widths) + '\n'
564
565 out = [div]
566 for row in data:
567 out.append(f % tuple(row))
568 if header and len(data) > 1:
569 out.insert(2, div)
570 out.append(div)
571 return ''.join(out)
@@ -1,245 +1,244 b''
1 1 from pprint import pprint
2 2 from mercurial import minirst
3 3
4 4 def debugformat(title, text, width, **kwargs):
5 5 print "%s formatted to fit within %d characters:" % (title, width)
6 6 print "-" * 70
7 7 formatted = minirst.format(text, width, **kwargs)
8 8 if type(formatted) == tuple:
9 9 print formatted[0]
10 10 print "-" * 70
11 11 pprint(formatted[1])
12 12 else:
13 13 print formatted
14 14 print "-" * 70
15 15 print
16 16
17 17 paragraphs = """
18 18 This is some text in the first paragraph.
19 19
20 20 A small indented paragraph.
21 21 It is followed by some lines
22 22 containing random whitespace.
23 23 \n \n \nThe third and final paragraph.
24 24 """
25 25
26 26 debugformat('paragraphs', paragraphs, 60)
27 27 debugformat('paragraphs', paragraphs, 30)
28 28
29 29
30 30 definitions = """
31 31 A Term
32 32 Definition. The indented
33 33 lines make up the definition.
34 34 Another Term
35 35 Another definition. The final line in the
36 36 definition determines the indentation, so
37 37 this will be indented with four spaces.
38 38
39 39 A Nested/Indented Term
40 40 Definition.
41 41 """
42 42
43 43 debugformat('definitions', definitions, 60)
44 44 debugformat('definitions', definitions, 30)
45 45
46 46
47 47 literals = r"""
48 48 The fully minimized form is the most
49 49 convenient form::
50 50
51 51 Hello
52 52 literal
53 53 world
54 54
55 55 In the partially minimized form a paragraph
56 56 simply ends with space-double-colon. ::
57 57
58 58 ////////////////////////////////////////
59 59 long un-wrapped line in a literal block
60 60 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
61 61
62 62 ::
63 63
64 64 This literal block is started with '::',
65 65 the so-called expanded form. The paragraph
66 66 with '::' disappears in the final output.
67 67 """
68 68
69 69 debugformat('literals', literals, 60)
70 70 debugformat('literals', literals, 30)
71 71
72 72
73 73 lists = """
74 74 - This is the first list item.
75 75
76 76 Second paragraph in the first list item.
77 77
78 78 - List items need not be separated
79 79 by a blank line.
80 80 - And will be rendered without
81 81 one in any case.
82 82
83 83 We can have indented lists:
84 84
85 85 - This is an indented list item
86 86
87 87 - Another indented list item::
88 88
89 89 - A literal block in the middle
90 90 of an indented list.
91 91
92 92 (The above is not a list item since we are in the literal block.)
93 93
94 94 ::
95 95
96 96 Literal block with no indentation (apart from
97 97 the two spaces added to all literal blocks).
98 98
99 99 1. This is an enumerated list (first item).
100 100 2. Continuing with the second item.
101 101
102 102 (1) foo
103 103 (2) bar
104 104
105 105 1) Another
106 106 2) List
107 107
108 108 Line blocks are also a form of list:
109 109
110 110 | This is the first line.
111 111 The line continues here.
112 112 | This is the second line.
113 113 """
114 114
115 115 debugformat('lists', lists, 60)
116 116 debugformat('lists', lists, 30)
117 117
118 118
119 119 options = """
120 120 There is support for simple option lists,
121 121 but only with long options:
122 122
123 123 -X, --exclude filter an option with a short and long option with an argument
124 124 -I, --include an option with both a short option and a long option
125 125 --all Output all.
126 126 --both Output both (this description is
127 127 quite long).
128 128 --long Output all day long.
129 129
130 130 --par This option has two paragraphs in its description.
131 131 This is the first.
132 132
133 133 This is the second. Blank lines may be omitted between
134 134 options (as above) or left in (as here).
135 135
136 136
137 137 The next paragraph looks like an option list, but lacks the two-space
138 138 marker after the option. It is treated as a normal paragraph:
139 139
140 140 --foo bar baz
141 141 """
142 142
143 143 debugformat('options', options, 60)
144 144 debugformat('options', options, 30)
145 145
146 146
147 147 fields = """
148 148 :a: First item.
149 149 :ab: Second item. Indentation and wrapping
150 150 is handled automatically.
151 151
152 152 Next list:
153 153
154 154 :small: The larger key below triggers full indentation here.
155 155 :much too large: This key is big enough to get its own line.
156 156 """
157 157
158 158 debugformat('fields', fields, 60)
159 159 debugformat('fields', fields, 30)
160 160
161 161 containers = """
162 162 Normal output.
163 163
164 164 .. container:: debug
165 165
166 166 Initial debug output.
167 167
168 168 .. container:: verbose
169 169
170 170 Verbose output.
171 171
172 172 .. container:: debug
173 173
174 174 Debug output.
175 175 """
176 176
177 177 debugformat('containers (normal)', containers, 60)
178 178 debugformat('containers (verbose)', containers, 60, keep=['verbose'])
179 179 debugformat('containers (debug)', containers, 60, keep=['debug'])
180 180 debugformat('containers (verbose debug)', containers, 60,
181 181 keep=['verbose', 'debug'])
182 182
183 183 roles = """Please see :hg:`add`."""
184 184 debugformat('roles', roles, 60)
185 185
186 186
187 187 sections = """
188 188 Title
189 189 =====
190 190
191 191 Section
192 192 -------
193 193
194 194 Subsection
195 195 ''''''''''
196 196
197 197 Markup: ``foo`` and :hg:`help`
198 198 ------------------------------
199 199 """
200 200 debugformat('sections', sections, 20)
201 201
202 202
203 203 admonitions = """
204 204 .. note::
205 205 This is a note
206 206
207 207 - Bullet 1
208 208 - Bullet 2
209 209
210 210 .. warning:: This is a warning Second
211 211 input line of warning
212 212
213 213 .. danger::
214 214 This is danger
215 215 """
216 216
217 217 debugformat('admonitions', admonitions, 30)
218 218
219 219 comments = """
220 220 Some text.
221 221
222 222 .. A comment
223 223
224 224 .. An indented comment
225 225
226 226 Some indented text.
227 227
228 228 ..
229 229
230 230 Empty comment above
231 231 """
232 232
233 233 debugformat('comments', comments, 30)
234 234
235 table = """
236 === === ===
237 a b c
238 === === ===
239 1 2 3
240 foo bar baz
241 aa bb sdfsdfsdf this line is way too long for this cell.
242 === === ===
243 """
235
236 data = [['a', 'b', 'c'],
237 ['1', '2', '3'],
238 ['foo', 'bar', 'baz this list is very very very long man']]
239
240 table = minirst.maketable(data, 2, True)
241
242 print table
244 243
245 244 debugformat('table', table, 30)
@@ -1,402 +1,408 b''
1 1 paragraphs formatted to fit within 60 characters:
2 2 ----------------------------------------------------------------------
3 3 This is some text in the first paragraph.
4 4
5 5 A small indented paragraph. It is followed by some lines
6 6 containing random whitespace.
7 7
8 8 The third and final paragraph.
9 9 ----------------------------------------------------------------------
10 10
11 11 paragraphs formatted to fit within 30 characters:
12 12 ----------------------------------------------------------------------
13 13 This is some text in the first
14 14 paragraph.
15 15
16 16 A small indented paragraph.
17 17 It is followed by some lines
18 18 containing random
19 19 whitespace.
20 20
21 21 The third and final paragraph.
22 22 ----------------------------------------------------------------------
23 23
24 24 definitions formatted to fit within 60 characters:
25 25 ----------------------------------------------------------------------
26 26 A Term
27 27 Definition. The indented lines make up the definition.
28 28
29 29 Another Term
30 30 Another definition. The final line in the definition
31 31 determines the indentation, so this will be indented
32 32 with four spaces.
33 33
34 34 A Nested/Indented Term
35 35 Definition.
36 36 ----------------------------------------------------------------------
37 37
38 38 definitions formatted to fit within 30 characters:
39 39 ----------------------------------------------------------------------
40 40 A Term
41 41 Definition. The indented
42 42 lines make up the
43 43 definition.
44 44
45 45 Another Term
46 46 Another definition. The
47 47 final line in the
48 48 definition determines the
49 49 indentation, so this will
50 50 be indented with four
51 51 spaces.
52 52
53 53 A Nested/Indented Term
54 54 Definition.
55 55 ----------------------------------------------------------------------
56 56
57 57 literals formatted to fit within 60 characters:
58 58 ----------------------------------------------------------------------
59 59 The fully minimized form is the most convenient form:
60 60
61 61 Hello
62 62 literal
63 63 world
64 64
65 65 In the partially minimized form a paragraph simply ends with
66 66 space-double-colon.
67 67
68 68 ////////////////////////////////////////
69 69 long un-wrapped line in a literal block
70 70 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
71 71
72 72 This literal block is started with '::',
73 73 the so-called expanded form. The paragraph
74 74 with '::' disappears in the final output.
75 75 ----------------------------------------------------------------------
76 76
77 77 literals formatted to fit within 30 characters:
78 78 ----------------------------------------------------------------------
79 79 The fully minimized form is
80 80 the most convenient form:
81 81
82 82 Hello
83 83 literal
84 84 world
85 85
86 86 In the partially minimized
87 87 form a paragraph simply ends
88 88 with space-double-colon.
89 89
90 90 ////////////////////////////////////////
91 91 long un-wrapped line in a literal block
92 92 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
93 93
94 94 This literal block is started with '::',
95 95 the so-called expanded form. The paragraph
96 96 with '::' disappears in the final output.
97 97 ----------------------------------------------------------------------
98 98
99 99 lists formatted to fit within 60 characters:
100 100 ----------------------------------------------------------------------
101 101 - This is the first list item.
102 102
103 103 Second paragraph in the first list item.
104 104
105 105 - List items need not be separated by a blank line.
106 106 - And will be rendered without one in any case.
107 107
108 108 We can have indented lists:
109 109
110 110 - This is an indented list item
111 111 - Another indented list item:
112 112
113 113 - A literal block in the middle
114 114 of an indented list.
115 115
116 116 (The above is not a list item since we are in the literal block.)
117 117
118 118 Literal block with no indentation (apart from
119 119 the two spaces added to all literal blocks).
120 120
121 121 1. This is an enumerated list (first item).
122 122 2. Continuing with the second item.
123 123 (1) foo
124 124 (2) bar
125 125 1) Another
126 126 2) List
127 127
128 128 Line blocks are also a form of list:
129 129
130 130 This is the first line. The line continues here.
131 131 This is the second line.
132 132 ----------------------------------------------------------------------
133 133
134 134 lists formatted to fit within 30 characters:
135 135 ----------------------------------------------------------------------
136 136 - This is the first list item.
137 137
138 138 Second paragraph in the
139 139 first list item.
140 140
141 141 - List items need not be
142 142 separated by a blank line.
143 143 - And will be rendered without
144 144 one in any case.
145 145
146 146 We can have indented lists:
147 147
148 148 - This is an indented list
149 149 item
150 150 - Another indented list
151 151 item:
152 152
153 153 - A literal block in the middle
154 154 of an indented list.
155 155
156 156 (The above is not a list item since we are in the literal block.)
157 157
158 158 Literal block with no indentation (apart from
159 159 the two spaces added to all literal blocks).
160 160
161 161 1. This is an enumerated list
162 162 (first item).
163 163 2. Continuing with the second
164 164 item.
165 165 (1) foo
166 166 (2) bar
167 167 1) Another
168 168 2) List
169 169
170 170 Line blocks are also a form of
171 171 list:
172 172
173 173 This is the first line. The
174 174 line continues here.
175 175 This is the second line.
176 176 ----------------------------------------------------------------------
177 177
178 178 options formatted to fit within 60 characters:
179 179 ----------------------------------------------------------------------
180 180 There is support for simple option lists, but only with long
181 181 options:
182 182
183 183 -X --exclude filter an option with a short and long option
184 184 with an argument
185 185 -I --include an option with both a short option and
186 186 a long option
187 187 --all Output all.
188 188 --both Output both (this description is quite
189 189 long).
190 190 --long Output all day long.
191 191 --par This option has two paragraphs in its
192 192 description. This is the first.
193 193
194 194 This is the second. Blank lines may
195 195 be omitted between options (as above)
196 196 or left in (as here).
197 197
198 198 The next paragraph looks like an option list, but lacks the
199 199 two-space marker after the option. It is treated as a normal
200 200 paragraph:
201 201
202 202 --foo bar baz
203 203 ----------------------------------------------------------------------
204 204
205 205 options formatted to fit within 30 characters:
206 206 ----------------------------------------------------------------------
207 207 There is support for simple
208 208 option lists, but only with
209 209 long options:
210 210
211 211 -X --exclude filter an
212 212 option
213 213 with a
214 214 short
215 215 and
216 216 long
217 217 option
218 218 with an
219 219 argumen
220 220 t
221 221 -I --include an
222 222 option
223 223 with
224 224 both a
225 225 short
226 226 option
227 227 and a
228 228 long
229 229 option
230 230 --all Output
231 231 all.
232 232 --both Output
233 233 both
234 234 (this d
235 235 escript
236 236 ion is
237 237 quite
238 238 long).
239 239 --long Output
240 240 all day
241 241 long.
242 242 --par This
243 243 option
244 244 has two
245 245 paragra
246 246 phs in
247 247 its des
248 248 criptio
249 249 n. This
250 250 is the
251 251 first.
252 252
253 253 This is
254 254 the
255 255 second.
256 256 Blank
257 257 lines
258 258 may be
259 259 omitted
260 260 between
261 261 options
262 262 (as
263 263 above)
264 264 or left
265 265 in (as
266 266 here).
267 267
268 268 The next paragraph looks like
269 269 an option list, but lacks the
270 270 two-space marker after the
271 271 option. It is treated as a
272 272 normal paragraph:
273 273
274 274 --foo bar baz
275 275 ----------------------------------------------------------------------
276 276
277 277 fields formatted to fit within 60 characters:
278 278 ----------------------------------------------------------------------
279 279 a First item.
280 280 ab Second item. Indentation and wrapping is handled
281 281 automatically.
282 282
283 283 Next list:
284 284
285 285 small The larger key below triggers full indentation
286 286 here.
287 287 much too large
288 288 This key is big enough to get its own line.
289 289 ----------------------------------------------------------------------
290 290
291 291 fields formatted to fit within 30 characters:
292 292 ----------------------------------------------------------------------
293 293 a First item.
294 294 ab Second item. Indentation
295 295 and wrapping is handled
296 296 automatically.
297 297
298 298 Next list:
299 299
300 300 small The larger key
301 301 below triggers
302 302 full indentation
303 303 here.
304 304 much too large
305 305 This key is big
306 306 enough to get its
307 307 own line.
308 308 ----------------------------------------------------------------------
309 309
310 310 containers (normal) formatted to fit within 60 characters:
311 311 ----------------------------------------------------------------------
312 312 Normal output.
313 313 ----------------------------------------------------------------------
314 314
315 315 containers (verbose) formatted to fit within 60 characters:
316 316 ----------------------------------------------------------------------
317 317 Normal output.
318 318
319 319 Verbose output.
320 320 ----------------------------------------------------------------------
321 321 ['debug', 'debug']
322 322 ----------------------------------------------------------------------
323 323
324 324 containers (debug) formatted to fit within 60 characters:
325 325 ----------------------------------------------------------------------
326 326 Normal output.
327 327
328 328 Initial debug output.
329 329 ----------------------------------------------------------------------
330 330 ['verbose']
331 331 ----------------------------------------------------------------------
332 332
333 333 containers (verbose debug) formatted to fit within 60 characters:
334 334 ----------------------------------------------------------------------
335 335 Normal output.
336 336
337 337 Initial debug output.
338 338
339 339 Verbose output.
340 340
341 341 Debug output.
342 342 ----------------------------------------------------------------------
343 343 []
344 344 ----------------------------------------------------------------------
345 345
346 346 roles formatted to fit within 60 characters:
347 347 ----------------------------------------------------------------------
348 348 Please see "hg add".
349 349 ----------------------------------------------------------------------
350 350
351 351 sections formatted to fit within 20 characters:
352 352 ----------------------------------------------------------------------
353 353 Title
354 354 =====
355 355
356 356 Section
357 357 -------
358 358
359 359 Subsection
360 360 ''''''''''
361 361
362 362 Markup: "foo" and "hg help"
363 363 ---------------------------
364 364 ----------------------------------------------------------------------
365 365
366 366 admonitions formatted to fit within 30 characters:
367 367 ----------------------------------------------------------------------
368 368 Note:
369 369 This is a note
370 370
371 371 - Bullet 1
372 372 - Bullet 2
373 373
374 374 Warning!
375 375 This is a warning Second
376 376 input line of warning
377 377
378 378 !Danger!
379 379 This is danger
380 380 ----------------------------------------------------------------------
381 381
382 382 comments formatted to fit within 30 characters:
383 383 ----------------------------------------------------------------------
384 384 Some text.
385 385
386 386 Some indented text.
387 387
388 388 Empty comment above
389 389 ----------------------------------------------------------------------
390 390
391 === === ========================================
392 a b c
393 === === ========================================
394 1 2 3
395 foo bar baz this list is very very very long man
396 === === ========================================
397
391 398 table formatted to fit within 30 characters:
392 399 ----------------------------------------------------------------------
393 400 a b c
394 401 ------------------------------
395 402 1 2 3
396 foo bar baz
397 aa bb sdfsdfsdf this line
398 is way too long for
399 this cell.
403 foo bar baz this list is
404 very very very long
405 man
400 406
401 407 ----------------------------------------------------------------------
402 408
General Comments 0
You need to be logged in to leave comments. Login now