##// END OF EJS Templates
minirst: simplify and standardize field list formatting...
Olav Reinert -
r15861:ee8f5e4c default
parent child Browse files
Show More
@@ -1,687 +1,677
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 def replace(text, substs):
26 26 '''
27 27 Apply a list of (find, replace) pairs to a text.
28 28
29 29 >>> replace("foo bar", [('f', 'F'), ('b', 'B')])
30 30 'Foo Bar'
31 31 >>> encoding.encoding = 'latin1'
32 32 >>> replace('\\x81\\\\', [('\\\\', '/')])
33 33 '\\x81/'
34 34 >>> encoding.encoding = 'shiftjis'
35 35 >>> replace('\\x81\\\\', [('\\\\', '/')])
36 36 '\\x81\\\\'
37 37 '''
38 38
39 39 # some character encodings (cp932 for Japanese, at least) use
40 40 # ASCII characters other than control/alphabet/digit as a part of
41 41 # multi-bytes characters, so direct replacing with such characters
42 42 # on strings in local encoding causes invalid byte sequences.
43 43 utext = text.decode(encoding.encoding)
44 44 for f, t in substs:
45 45 utext = utext.replace(f, t)
46 46 return utext.encode(encoding.encoding)
47 47
48 48 _blockre = re.compile(r"\n(?:\s*\n)+")
49 49
50 50 def findblocks(text):
51 51 """Find continuous blocks of lines in text.
52 52
53 53 Returns a list of dictionaries representing the blocks. Each block
54 54 has an 'indent' field and a 'lines' field.
55 55 """
56 56 blocks = []
57 57 for b in _blockre.split(text.lstrip('\n').rstrip()):
58 58 lines = b.splitlines()
59 59 if lines:
60 60 indent = min((len(l) - len(l.lstrip())) for l in lines)
61 61 lines = [l[indent:] for l in lines]
62 62 blocks.append(dict(indent=indent, lines=lines))
63 63 return blocks
64 64
65 65 def findliteralblocks(blocks):
66 66 """Finds literal blocks and adds a 'type' field to the blocks.
67 67
68 68 Literal blocks are given the type 'literal', all other blocks are
69 69 given type the 'paragraph'.
70 70 """
71 71 i = 0
72 72 while i < len(blocks):
73 73 # Searching for a block that looks like this:
74 74 #
75 75 # +------------------------------+
76 76 # | paragraph |
77 77 # | (ends with "::") |
78 78 # +------------------------------+
79 79 # +---------------------------+
80 80 # | indented literal block |
81 81 # +---------------------------+
82 82 blocks[i]['type'] = 'paragraph'
83 83 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
84 84 indent = blocks[i]['indent']
85 85 adjustment = blocks[i + 1]['indent'] - indent
86 86
87 87 if blocks[i]['lines'] == ['::']:
88 88 # Expanded form: remove block
89 89 del blocks[i]
90 90 i -= 1
91 91 elif blocks[i]['lines'][-1].endswith(' ::'):
92 92 # Partially minimized form: remove space and both
93 93 # colons.
94 94 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
95 95 else:
96 96 # Fully minimized form: remove just one colon.
97 97 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
98 98
99 99 # List items are formatted with a hanging indent. We must
100 100 # correct for this here while we still have the original
101 101 # information on the indentation of the subsequent literal
102 102 # blocks available.
103 103 m = _bulletre.match(blocks[i]['lines'][0])
104 104 if m:
105 105 indent += m.end()
106 106 adjustment -= m.end()
107 107
108 108 # Mark the following indented blocks.
109 109 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
110 110 blocks[i + 1]['type'] = 'literal'
111 111 blocks[i + 1]['indent'] -= adjustment
112 112 i += 1
113 113 i += 1
114 114 return blocks
115 115
116 116 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
117 117 _optionre = re.compile(r'^(-([a-zA-Z0-9]), )?(--[a-z0-9-]+)'
118 118 r'((.*) +)(.*)$')
119 119 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
120 120 _definitionre = re.compile(r'[^ ]')
121 121 _tablere = re.compile(r'(=+\s+)*=+')
122 122
123 123 def splitparagraphs(blocks):
124 124 """Split paragraphs into lists."""
125 125 # Tuples with (list type, item regexp, single line items?). Order
126 126 # matters: definition lists has the least specific regexp and must
127 127 # come last.
128 128 listtypes = [('bullet', _bulletre, True),
129 129 ('option', _optionre, True),
130 130 ('field', _fieldre, True),
131 131 ('definition', _definitionre, False)]
132 132
133 133 def match(lines, i, itemre, singleline):
134 134 """Does itemre match an item at line i?
135 135
136 136 A list item can be followed by an idented line or another list
137 137 item (but only if singleline is True).
138 138 """
139 139 line1 = lines[i]
140 140 line2 = i + 1 < len(lines) and lines[i + 1] or ''
141 141 if not itemre.match(line1):
142 142 return False
143 143 if singleline:
144 144 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
145 145 else:
146 146 return line2.startswith(' ')
147 147
148 148 i = 0
149 149 while i < len(blocks):
150 150 if blocks[i]['type'] == 'paragraph':
151 151 lines = blocks[i]['lines']
152 152 for type, itemre, singleline in listtypes:
153 153 if match(lines, 0, itemre, singleline):
154 154 items = []
155 155 for j, line in enumerate(lines):
156 156 if match(lines, j, itemre, singleline):
157 157 items.append(dict(type=type, lines=[],
158 158 indent=blocks[i]['indent']))
159 159 items[-1]['lines'].append(line)
160 160 blocks[i:i + 1] = items
161 161 break
162 162 i += 1
163 163 return blocks
164 164
165 _fieldwidth = 12
165 _fieldwidth = 14
166 166
167 167 def updatefieldlists(blocks):
168 """Find key and maximum key width for field lists."""
168 """Find key for field lists."""
169 169 i = 0
170 170 while i < len(blocks):
171 171 if blocks[i]['type'] != 'field':
172 172 i += 1
173 173 continue
174 174
175 keywidth = 0
176 175 j = i
177 176 while j < len(blocks) and blocks[j]['type'] == 'field':
178 177 m = _fieldre.match(blocks[j]['lines'][0])
179 178 key, rest = m.groups()
180 179 blocks[j]['lines'][0] = rest
181 180 blocks[j]['key'] = key
182 keywidth = max(keywidth, len(key))
183 181 j += 1
184 182
185 for block in blocks[i:j]:
186 block['keywidth'] = keywidth
187 183 i = j + 1
188 184
189 185 return blocks
190 186
191 187 def updateoptionlists(blocks):
192 188 i = 0
193 189 while i < len(blocks):
194 190 if blocks[i]['type'] != 'option':
195 191 i += 1
196 192 continue
197 193
198 194 optstrwidth = 0
199 195 j = i
200 196 while j < len(blocks) and blocks[j]['type'] == 'option':
201 197 m = _optionre.match(blocks[j]['lines'][0])
202 198
203 199 shortoption = m.group(2)
204 200 group3 = m.group(3)
205 201 longoption = group3[2:].strip()
206 202 desc = m.group(6).strip()
207 203 longoptionarg = m.group(5).strip()
208 204 blocks[j]['lines'][0] = desc
209 205
210 206 noshortop = ''
211 207 if not shortoption:
212 208 noshortop = ' '
213 209
214 210 opt = "%s%s" % (shortoption and "-%s " % shortoption or '',
215 211 ("%s--%s %s") % (noshortop, longoption,
216 212 longoptionarg))
217 213 opt = opt.rstrip()
218 214 blocks[j]['optstr'] = opt
219 215 optstrwidth = max(optstrwidth, encoding.colwidth(opt))
220 216 j += 1
221 217
222 218 for block in blocks[i:j]:
223 219 block['optstrwidth'] = optstrwidth
224 220 i = j + 1
225 221 return blocks
226 222
227 223 def prunecontainers(blocks, keep):
228 224 """Prune unwanted containers.
229 225
230 226 The blocks must have a 'type' field, i.e., they should have been
231 227 run through findliteralblocks first.
232 228 """
233 229 pruned = []
234 230 i = 0
235 231 while i + 1 < len(blocks):
236 232 # Searching for a block that looks like this:
237 233 #
238 234 # +-------+---------------------------+
239 235 # | ".. container ::" type |
240 236 # +---+ |
241 237 # | blocks |
242 238 # +-------------------------------+
243 239 if (blocks[i]['type'] == 'paragraph' and
244 240 blocks[i]['lines'][0].startswith('.. container::')):
245 241 indent = blocks[i]['indent']
246 242 adjustment = blocks[i + 1]['indent'] - indent
247 243 containertype = blocks[i]['lines'][0][15:]
248 244 prune = containertype not in keep
249 245 if prune:
250 246 pruned.append(containertype)
251 247
252 248 # Always delete "..container:: type" block
253 249 del blocks[i]
254 250 j = i
255 251 i -= 1
256 252 while j < len(blocks) and blocks[j]['indent'] > indent:
257 253 if prune:
258 254 del blocks[j]
259 255 else:
260 256 blocks[j]['indent'] -= adjustment
261 257 j += 1
262 258 i += 1
263 259 return blocks, pruned
264 260
265 261 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
266 262
267 263 def findtables(blocks):
268 264 '''Find simple tables
269 265
270 266 Only simple one-line table elements are supported
271 267 '''
272 268
273 269 for block in blocks:
274 270 # Searching for a block that looks like this:
275 271 #
276 272 # === ==== ===
277 273 # A B C
278 274 # === ==== === <- optional
279 275 # 1 2 3
280 276 # x y z
281 277 # === ==== ===
282 278 if (block['type'] == 'paragraph' and
283 279 len(block['lines']) > 2 and
284 280 _tablere.match(block['lines'][0]) and
285 281 block['lines'][0] == block['lines'][-1]):
286 282 block['type'] = 'table'
287 283 block['header'] = False
288 284 div = block['lines'][0]
289 285
290 286 # column markers are ASCII so we can calculate column
291 287 # position in bytes
292 288 columns = [x for x in xrange(len(div))
293 289 if div[x] == '=' and (x == 0 or div[x - 1] == ' ')]
294 290 rows = []
295 291 for l in block['lines'][1:-1]:
296 292 if l == div:
297 293 block['header'] = True
298 294 continue
299 295 row = []
300 296 # we measure columns not in bytes or characters but in
301 297 # colwidth which makes things tricky
302 298 pos = columns[0] # leading whitespace is bytes
303 299 for n, start in enumerate(columns):
304 300 if n + 1 < len(columns):
305 301 width = columns[n + 1] - start
306 302 v = encoding.getcols(l, pos, width) # gather columns
307 303 pos += len(v) # calculate byte position of end
308 304 row.append(v.strip())
309 305 else:
310 306 row.append(l[pos:].strip())
311 307 rows.append(row)
312 308
313 309 block['table'] = rows
314 310
315 311 return blocks
316 312
317 313 def findsections(blocks):
318 314 """Finds sections.
319 315
320 316 The blocks must have a 'type' field, i.e., they should have been
321 317 run through findliteralblocks first.
322 318 """
323 319 for block in blocks:
324 320 # Searching for a block that looks like this:
325 321 #
326 322 # +------------------------------+
327 323 # | Section title |
328 324 # | ------------- |
329 325 # +------------------------------+
330 326 if (block['type'] == 'paragraph' and
331 327 len(block['lines']) == 2 and
332 328 encoding.colwidth(block['lines'][0]) == len(block['lines'][1]) and
333 329 _sectionre.match(block['lines'][1])):
334 330 block['underline'] = block['lines'][1][0]
335 331 block['type'] = 'section'
336 332 del block['lines'][1]
337 333 return blocks
338 334
339 335 def inlineliterals(blocks):
340 336 substs = [('``', '"')]
341 337 for b in blocks:
342 338 if b['type'] in ('paragraph', 'section'):
343 339 b['lines'] = [replace(l, substs) for l in b['lines']]
344 340 return blocks
345 341
346 342 def hgrole(blocks):
347 343 substs = [(':hg:`', '"hg '), ('`', '"')]
348 344 for b in blocks:
349 345 if b['type'] in ('paragraph', 'section'):
350 346 # Turn :hg:`command` into "hg command". This also works
351 347 # when there is a line break in the command and relies on
352 348 # the fact that we have no stray back-quotes in the input
353 349 # (run the blocks through inlineliterals first).
354 350 b['lines'] = [replace(l, substs) for l in b['lines']]
355 351 return blocks
356 352
357 353 def addmargins(blocks):
358 354 """Adds empty blocks for vertical spacing.
359 355
360 356 This groups bullets, options, and definitions together with no vertical
361 357 space between them, and adds an empty block between all other blocks.
362 358 """
363 359 i = 1
364 360 while i < len(blocks):
365 361 if (blocks[i]['type'] == blocks[i - 1]['type'] and
366 362 blocks[i]['type'] in ('bullet', 'option', 'field')):
367 363 i += 1
368 364 else:
369 365 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
370 366 i += 2
371 367 return blocks
372 368
373 369 def prunecomments(blocks):
374 370 """Remove comments."""
375 371 i = 0
376 372 while i < len(blocks):
377 373 b = blocks[i]
378 374 if b['type'] == 'paragraph' and (b['lines'][0].startswith('.. ') or
379 375 b['lines'] == ['..']):
380 376 del blocks[i]
381 377 if i < len(blocks) and blocks[i]['type'] == 'margin':
382 378 del blocks[i]
383 379 else:
384 380 i += 1
385 381 return blocks
386 382
387 383 _admonitionre = re.compile(r"\.\. (admonition|attention|caution|danger|"
388 384 r"error|hint|important|note|tip|warning)::",
389 385 flags=re.IGNORECASE)
390 386
391 387 def findadmonitions(blocks):
392 388 """
393 389 Makes the type of the block an admonition block if
394 390 the first line is an admonition directive
395 391 """
396 392 i = 0
397 393 while i < len(blocks):
398 394 m = _admonitionre.match(blocks[i]['lines'][0])
399 395 if m:
400 396 blocks[i]['type'] = 'admonition'
401 397 admonitiontitle = blocks[i]['lines'][0][3:m.end() - 2].lower()
402 398
403 399 firstline = blocks[i]['lines'][0][m.end() + 1:]
404 400 if firstline:
405 401 blocks[i]['lines'].insert(1, ' ' + firstline)
406 402
407 403 blocks[i]['admonitiontitle'] = admonitiontitle
408 404 del blocks[i]['lines'][0]
409 405 i = i + 1
410 406 return blocks
411 407
412 408 _admonitiontitles = {'attention': _('Attention:'),
413 409 'caution': _('Caution:'),
414 410 'danger': _('!Danger!') ,
415 411 'error': _('Error:'),
416 412 'hint': _('Hint:'),
417 413 'important': _('Important:'),
418 414 'note': _('Note:'),
419 415 'tip': _('Tip:'),
420 416 'warning': _('Warning!')}
421 417
422 418 def formatoption(block, width):
423 419 desc = ' '.join(map(str.strip, block['lines']))
424 420 colwidth = encoding.colwidth(block['optstr'])
425 421 usablewidth = width - 1
426 422 hanging = block['optstrwidth']
427 423 initindent = '%s%s ' % (block['optstr'], ' ' * ((hanging - colwidth)))
428 424 hangindent = ' ' * (encoding.colwidth(initindent) + 1)
429 425 return ' %s\n' % (util.wrap(desc, usablewidth,
430 426 initindent=initindent,
431 427 hangindent=hangindent))
432 428
433 429 def formatblock(block, width):
434 430 """Format a block according to width."""
435 431 if width <= 0:
436 432 width = 78
437 433 indent = ' ' * block['indent']
438 434 if block['type'] == 'admonition':
439 435 admonition = _admonitiontitles[block['admonitiontitle']]
440 436 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
441 437
442 438 defindent = indent + hang * ' '
443 439 text = ' '.join(map(str.strip, block['lines']))
444 440 return '%s\n%s\n' % (indent + admonition,
445 441 util.wrap(text, width=width,
446 442 initindent=defindent,
447 443 hangindent=defindent))
448 444 if block['type'] == 'margin':
449 445 return '\n'
450 446 if block['type'] == 'literal':
451 447 indent += ' '
452 448 return indent + ('\n' + indent).join(block['lines']) + '\n'
453 449 if block['type'] == 'section':
454 450 underline = encoding.colwidth(block['lines'][0]) * block['underline']
455 451 return "%s%s\n%s%s\n" % (indent, block['lines'][0],indent, underline)
456 452 if block['type'] == 'table':
457 453 table = block['table']
458 454 # compute column widths
459 455 widths = [max([encoding.colwidth(e) for e in c]) for c in zip(*table)]
460 456 text = ''
461 457 span = sum(widths) + len(widths) - 1
462 458 indent = ' ' * block['indent']
463 459 hang = ' ' * (len(indent) + span - widths[-1])
464 460
465 461 for row in table:
466 462 l = []
467 463 for w, v in zip(widths, row):
468 464 pad = ' ' * (w - encoding.colwidth(v))
469 465 l.append(v + pad)
470 466 l = ' '.join(l)
471 467 l = util.wrap(l, width=width, initindent=indent, hangindent=hang)
472 468 if not text and block['header']:
473 469 text = l + '\n' + indent + '-' * (min(width, span)) + '\n'
474 470 else:
475 471 text += l + "\n"
476 472 return text
477 473 if block['type'] == 'definition':
478 474 term = indent + block['lines'][0]
479 475 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
480 476 defindent = indent + hang * ' '
481 477 text = ' '.join(map(str.strip, block['lines'][1:]))
482 478 return '%s\n%s\n' % (term, util.wrap(text, width=width,
483 479 initindent=defindent,
484 480 hangindent=defindent))
485 481 subindent = indent
486 482 if block['type'] == 'bullet':
487 483 if block['lines'][0].startswith('| '):
488 484 # Remove bullet for line blocks and add no extra
489 485 # indention.
490 486 block['lines'][0] = block['lines'][0][2:]
491 487 else:
492 488 m = _bulletre.match(block['lines'][0])
493 489 subindent = indent + m.end() * ' '
494 490 elif block['type'] == 'field':
495 keywidth = block['keywidth']
496 491 key = block['key']
497
498 492 subindent = indent + _fieldwidth * ' '
499 493 if len(key) + 2 > _fieldwidth:
500 494 # key too large, use full line width
501 495 key = key.ljust(width)
502 elif keywidth + 2 < _fieldwidth:
503 # all keys are small, add only two spaces
504 key = key.ljust(keywidth + 2)
505 subindent = indent + (keywidth + 2) * ' '
506 496 else:
507 # mixed sizes, use fieldwidth for this one
497 # key fits within field width
508 498 key = key.ljust(_fieldwidth)
509 499 block['lines'][0] = key + block['lines'][0]
510 500 elif block['type'] == 'option':
511 501 return formatoption(block, width)
512 502
513 503 text = ' '.join(map(str.strip, block['lines']))
514 504 return util.wrap(text, width=width,
515 505 initindent=indent,
516 506 hangindent=subindent) + '\n'
517 507
518 508 def formathtml(blocks):
519 509 """Format RST blocks as HTML"""
520 510
521 511 out = []
522 512 headernest = ''
523 513 listnest = []
524 514
525 515 def openlist(start, level):
526 516 if not listnest or listnest[-1][0] != start:
527 517 listnest.append((start, level))
528 518 out.append('<%s>\n' % start)
529 519
530 520 blocks = [b for b in blocks if b['type'] != 'margin']
531 521
532 522 for pos, b in enumerate(blocks):
533 523 btype = b['type']
534 524 level = b['indent']
535 525 lines = b['lines']
536 526
537 527 if btype == 'admonition':
538 528 admonition = _admonitiontitles[b['admonitiontitle']]
539 529 text = ' '.join(map(str.strip, lines))
540 530 out.append('<p>\n<b>%s</b> %s\n</p>\n' % (admonition, text))
541 531 elif btype == 'paragraph':
542 532 out.append('<p>\n%s\n</p>\n' % '\n'.join(lines))
543 533 elif btype == 'margin':
544 534 pass
545 535 elif btype == 'literal':
546 536 out.append('<pre>\n%s\n</pre>\n' % '\n'.join(lines))
547 537 elif btype == 'section':
548 538 i = b['underline']
549 539 if i not in headernest:
550 540 headernest += i
551 541 level = headernest.index(i) + 1
552 542 out.append('<h%d>%s</h%d>\n' % (level, lines[0], level))
553 543 elif btype == 'table':
554 544 table = b['table']
555 545 t = []
556 546 for row in table:
557 547 l = []
558 548 for v in zip(row):
559 549 if not t:
560 550 l.append('<th>%s</th>' % v)
561 551 else:
562 552 l.append('<td>%s</td>' % v)
563 553 t.append(' <tr>%s</tr>\n' % ''.join(l))
564 554 out.append('<table>\n%s</table>\n' % ''.join(t))
565 555 elif btype == 'definition':
566 556 openlist('dl', level)
567 557 term = lines[0]
568 558 text = ' '.join(map(str.strip, lines[1:]))
569 559 out.append(' <dt>%s\n <dd>%s\n' % (term, text))
570 560 elif btype == 'bullet':
571 561 bullet, head = lines[0].split(' ', 1)
572 562 if bullet == '-':
573 563 openlist('ul', level)
574 564 else:
575 565 openlist('ol', level)
576 566 out.append(' <li> %s\n' % ' '.join([head] + lines[1:]))
577 567 elif btype == 'field':
578 568 openlist('dl', level)
579 569 key = b['key']
580 570 text = ' '.join(map(str.strip, lines))
581 571 out.append(' <dt>%s\n <dd>%s\n' % (key, text))
582 572 elif btype == 'option':
583 573 openlist('dl', level)
584 574 opt = b['optstr']
585 575 desc = ' '.join(map(str.strip, lines))
586 576 out.append(' <dt>%s\n <dd>%s\n' % (opt, desc))
587 577
588 578 # close lists if indent level of next block is lower
589 579 if listnest:
590 580 start, level = listnest[-1]
591 581 if pos == len(blocks) - 1:
592 582 out.append('</%s>\n' % start)
593 583 listnest.pop()
594 584 else:
595 585 nb = blocks[pos + 1]
596 586 ni = nb['indent']
597 587 if (ni < level or
598 588 (ni == level and
599 589 nb['type'] not in 'definition bullet field option')):
600 590 out.append('</%s>\n' % start)
601 591 listnest.pop()
602 592
603 593 return ''.join(out)
604 594
605 595 def parse(text, indent=0, keep=None):
606 596 """Parse text into a list of blocks"""
607 597 pruned = []
608 598 blocks = findblocks(text)
609 599 for b in blocks:
610 600 b['indent'] += indent
611 601 blocks = findliteralblocks(blocks)
612 602 blocks = findtables(blocks)
613 603 blocks, pruned = prunecontainers(blocks, keep or [])
614 604 blocks = findsections(blocks)
615 605 blocks = inlineliterals(blocks)
616 606 blocks = hgrole(blocks)
617 607 blocks = splitparagraphs(blocks)
618 608 blocks = updatefieldlists(blocks)
619 609 blocks = updateoptionlists(blocks)
620 610 blocks = addmargins(blocks)
621 611 blocks = prunecomments(blocks)
622 612 blocks = findadmonitions(blocks)
623 613 return blocks, pruned
624 614
625 615 def formatblocks(blocks, width):
626 616 text = ''.join(formatblock(b, width) for b in blocks)
627 617 return text
628 618
629 619 def format(text, width=80, indent=0, keep=None, style='plain'):
630 620 """Parse and format the text according to width."""
631 621 blocks, pruned = parse(text, indent, keep or [])
632 622 if style == 'html':
633 623 text = formathtml(blocks)
634 624 else:
635 625 text = ''.join(formatblock(b, width) for b in blocks)
636 626 if keep is None:
637 627 return text
638 628 else:
639 629 return text, pruned
640 630
641 631 def getsections(blocks):
642 632 '''return a list of (section name, nesting level, blocks) tuples'''
643 633 nest = ""
644 634 level = 0
645 635 secs = []
646 636 for b in blocks:
647 637 if b['type'] == 'section':
648 638 i = b['underline']
649 639 if i not in nest:
650 640 nest += i
651 641 level = nest.index(i) + 1
652 642 nest = nest[:level]
653 643 secs.append((b['lines'][0], level, [b]))
654 644 else:
655 645 if not secs:
656 646 # add an initial empty section
657 647 secs = [('', 0, [])]
658 648 secs[-1][2].append(b)
659 649 return secs
660 650
661 651 def decorateblocks(blocks, width):
662 652 '''generate a list of (section name, line text) pairs for search'''
663 653 lines = []
664 654 for s in getsections(blocks):
665 655 section = s[0]
666 656 text = formatblocks(s[2], width)
667 657 lines.append([(section, l) for l in text.splitlines(True)])
668 658 return lines
669 659
670 660 def maketable(data, indent=0, header=False):
671 661 '''Generate an RST table for the given table data'''
672 662
673 663 widths = [max(encoding.colwidth(e) for e in c) for c in zip(*data)]
674 664 indent = ' ' * indent
675 665 div = indent + ' '.join('=' * w for w in widths) + '\n'
676 666
677 667 out = [div]
678 668 for row in data:
679 669 l = []
680 670 for w, v in zip(widths, row):
681 671 pad = ' ' * (w - encoding.colwidth(v))
682 672 l.append(v + pad)
683 673 out.append(indent + ' '.join(l) + "\n")
684 674 if header and len(data) > 1:
685 675 out.insert(2, div)
686 676 out.append(div)
687 677 return ''.join(out)
@@ -1,393 +1,395
1 1 $ "$TESTDIR/hghave" unix-permissions || exit 80
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > convert=
6 6 > [convert]
7 7 > hg.saverev=False
8 8 > EOF
9 9 $ hg help convert
10 10 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
11 11
12 12 convert a foreign SCM repository to a Mercurial one.
13 13
14 14 Accepted source formats [identifiers]:
15 15
16 16 - Mercurial [hg]
17 17 - CVS [cvs]
18 18 - Darcs [darcs]
19 19 - git [git]
20 20 - Subversion [svn]
21 21 - Monotone [mtn]
22 22 - GNU Arch [gnuarch]
23 23 - Bazaar [bzr]
24 24 - Perforce [p4]
25 25
26 26 Accepted destination formats [identifiers]:
27 27
28 28 - Mercurial [hg]
29 29 - Subversion [svn] (history on branches is not preserved)
30 30
31 31 If no revision is given, all revisions will be converted. Otherwise,
32 32 convert will only import up to the named revision (given in a format
33 33 understood by the source).
34 34
35 35 If no destination directory name is specified, it defaults to the basename
36 36 of the source with "-hg" appended. If the destination repository doesn't
37 37 exist, it will be created.
38 38
39 39 By default, all sources except Mercurial will use --branchsort. Mercurial
40 40 uses --sourcesort to preserve original revision numbers order. Sort modes
41 41 have the following effects:
42 42
43 43 --branchsort convert from parent to child revision when possible, which
44 44 means branches are usually converted one after the other.
45 45 It generates more compact repositories.
46 46 --datesort sort revisions by date. Converted repositories have good-
47 47 looking changelogs but are often an order of magnitude
48 48 larger than the same ones generated by --branchsort.
49 49 --sourcesort try to preserve source revisions order, only supported by
50 50 Mercurial sources.
51 51
52 52 If "REVMAP" isn't given, it will be put in a default location
53 53 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
54 54 maps each source commit ID to the destination ID for that revision, like
55 55 so:
56 56
57 57 <source ID> <destination ID>
58 58
59 59 If the file doesn't exist, it's automatically created. It's updated on
60 60 each commit copied, so "hg convert" can be interrupted and can be run
61 61 repeatedly to copy new commits.
62 62
63 63 The authormap is a simple text file that maps each source commit author to
64 64 a destination commit author. It is handy for source SCMs that use unix
65 65 logins to identify authors (eg: CVS). One line per author mapping and the
66 66 line format is:
67 67
68 68 source author = destination author
69 69
70 70 Empty lines and lines starting with a "#" are ignored.
71 71
72 72 The filemap is a file that allows filtering and remapping of files and
73 73 directories. Each line can contain one of the following directives:
74 74
75 75 include path/to/file-or-dir
76 76
77 77 exclude path/to/file-or-dir
78 78
79 79 rename path/to/source path/to/destination
80 80
81 81 Comment lines start with "#". A specified path matches if it equals the
82 82 full relative name of a file or one of its parent directories. The
83 83 "include" or "exclude" directive with the longest matching path applies,
84 84 so line order does not matter.
85 85
86 86 The "include" directive causes a file, or all files under a directory, to
87 87 be included in the destination repository, and the exclusion of all other
88 88 files and directories not explicitly included. The "exclude" directive
89 89 causes files or directories to be omitted. The "rename" directive renames
90 90 a file or directory if it is converted. To rename from a subdirectory into
91 91 the root of the repository, use "." as the path to rename to.
92 92
93 93 The splicemap is a file that allows insertion of synthetic history,
94 94 letting you specify the parents of a revision. This is useful if you want
95 95 to e.g. give a Subversion merge two parents, or graft two disconnected
96 96 series of history together. Each entry contains a key, followed by a
97 97 space, followed by one or two comma-separated values:
98 98
99 99 key parent1, parent2
100 100
101 101 The key is the revision ID in the source revision control system whose
102 102 parents should be modified (same format as a key in .hg/shamap). The
103 103 values are the revision IDs (in either the source or destination revision
104 104 control system) that should be used as the new parents for that node. For
105 105 example, if you have merged "release-1.0" into "trunk", then you should
106 106 specify the revision on "trunk" as the first parent and the one on the
107 107 "release-1.0" branch as the second.
108 108
109 109 The branchmap is a file that allows you to rename a branch when it is
110 110 being brought in from whatever external repository. When used in
111 111 conjunction with a splicemap, it allows for a powerful combination to help
112 112 fix even the most badly mismanaged repositories and turn them into nicely
113 113 structured Mercurial repositories. The branchmap contains lines of the
114 114 form:
115 115
116 116 original_branch_name new_branch_name
117 117
118 118 where "original_branch_name" is the name of the branch in the source
119 119 repository, and "new_branch_name" is the name of the branch is the
120 120 destination repository. No whitespace is allowed in the branch names. This
121 121 can be used to (for instance) move code in one repository from "default"
122 122 to a named branch.
123 123
124 124 Mercurial Source
125 125 ''''''''''''''''
126 126
127 127 The Mercurial source recognizes the following configuration options, which
128 128 you can set on the command line with "--config":
129 129
130 130 convert.hg.ignoreerrors
131 ignore integrity errors when reading. Use it to fix Mercurial
132 repositories with missing revlogs, by converting from and to
133 Mercurial. Default is False.
131 ignore integrity errors when reading. Use it to fix
132 Mercurial repositories with missing revlogs, by converting
133 from and to Mercurial. Default is False.
134 134 convert.hg.saverev
135 store original revision ID in changeset (forces target IDs to
136 change). It takes a boolean argument and defaults to False.
135 store original revision ID in changeset (forces target IDs
136 to change). It takes a boolean argument and defaults to
137 False.
137 138 convert.hg.startrev
138 139 convert start revision and its descendants. It takes a hg
139 140 revision identifier and defaults to 0.
140 141
141 142 CVS Source
142 143 ''''''''''
143 144
144 145 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
145 146 indicate the starting point of what will be converted. Direct access to
146 147 the repository files is not needed, unless of course the repository is
147 148 ":local:". The conversion uses the top level directory in the sandbox to
148 149 find the CVS repository, and then uses CVS rlog commands to find files to
149 150 convert. This means that unless a filemap is given, all files under the
150 151 starting directory will be converted, and that any directory
151 152 reorganization in the CVS sandbox is ignored.
152 153
153 154 The following options can be used with "--config":
154 155
155 156 convert.cvsps.cache
156 157 Set to False to disable remote log caching, for testing and
157 158 debugging purposes. Default is True.
158 159 convert.cvsps.fuzz
159 Specify the maximum time (in seconds) that is allowed between
160 commits with identical user and log message in a single
161 changeset. When very large files were checked in as part of a
162 changeset then the default may not be long enough. The default
163 is 60.
160 Specify the maximum time (in seconds) that is allowed
161 between commits with identical user and log message in a
162 single changeset. When very large files were checked in as
163 part of a changeset then the default may not be long enough.
164 The default is 60.
164 165 convert.cvsps.mergeto
165 Specify a regular expression to which commit log messages are
166 matched. If a match occurs, then the conversion process will
167 insert a dummy revision merging the branch on which this log
168 message occurs to the branch indicated in the regex. Default
169 is "{{mergetobranch ([-\w]+)}}"
166 Specify a regular expression to which commit log messages
167 are matched. If a match occurs, then the conversion process
168 will insert a dummy revision merging the branch on which
169 this log message occurs to the branch indicated in the
170 regex. Default is "{{mergetobranch ([-\w]+)}}"
170 171 convert.cvsps.mergefrom
171 Specify a regular expression to which commit log messages are
172 matched. If a match occurs, then the conversion process will
173 add the most recent revision on the branch indicated in the
174 regex as the second parent of the changeset. Default is
172 Specify a regular expression to which commit log messages
173 are matched. If a match occurs, then the conversion process
174 will add the most recent revision on the branch indicated in
175 the regex as the second parent of the changeset. Default is
175 176 "{{mergefrombranch ([-\w]+)}}"
176 hook.cvslog
177 Specify a Python function to be called at the end of gathering
178 the CVS log. The function is passed a list with the log
179 entries, and can modify the entries in-place, or add or delete
180 them.
177 hook.cvslog Specify a Python function to be called at the end of
178 gathering the CVS log. The function is passed a list with
179 the log entries, and can modify the entries in-place, or add
180 or delete them.
181 181 hook.cvschangesets
182 182 Specify a Python function to be called after the changesets
183 are calculated from the the CVS log. The function is passed a
184 list with the changeset entries, and can modify the changesets
185 in-place, or add or delete them.
183 are calculated from the the CVS log. The function is passed
184 a list with the changeset entries, and can modify the
185 changesets in-place, or add or delete them.
186 186
187 187 An additional "debugcvsps" Mercurial command allows the builtin changeset
188 188 merging code to be run without doing a conversion. Its parameters and
189 189 output are similar to that of cvsps 2.1. Please see the command help for
190 190 more details.
191 191
192 192 Subversion Source
193 193 '''''''''''''''''
194 194
195 195 Subversion source detects classical trunk/branches/tags layouts. By
196 196 default, the supplied "svn://repo/path/" source URL is converted as a
197 197 single branch. If "svn://repo/path/trunk" exists it replaces the default
198 198 branch. If "svn://repo/path/branches" exists, its subdirectories are
199 199 listed as possible branches. If "svn://repo/path/tags" exists, it is
200 200 looked for tags referencing converted branches. Default "trunk",
201 201 "branches" and "tags" values can be overridden with following options. Set
202 202 them to paths relative to the source URL, or leave them blank to disable
203 203 auto detection.
204 204
205 205 The following options can be set with "--config":
206 206
207 207 convert.svn.branches
208 208 specify the directory containing branches. The default is
209 209 "branches".
210 210 convert.svn.tags
211 specify the directory containing tags. The default is "tags".
211 specify the directory containing tags. The default is
212 "tags".
212 213 convert.svn.trunk
213 specify the name of the trunk branch. The default is "trunk".
214 specify the name of the trunk branch. The default is
215 "trunk".
214 216
215 217 Source history can be retrieved starting at a specific revision, instead
216 218 of being integrally converted. Only single branch conversions are
217 219 supported.
218 220
219 221 convert.svn.startrev
220 222 specify start Subversion revision number. The default is 0.
221 223
222 224 Perforce Source
223 225 '''''''''''''''
224 226
225 227 The Perforce (P4) importer can be given a p4 depot path or a client
226 228 specification as source. It will convert all files in the source to a flat
227 229 Mercurial repository, ignoring labels, branches and integrations. Note
228 230 that when a depot path is given you then usually should specify a target
229 231 directory, because otherwise the target may be named "...-hg".
230 232
231 233 It is possible to limit the amount of source history to be converted by
232 234 specifying an initial Perforce revision:
233 235
234 236 convert.p4.startrev
235 237 specify initial Perforce revision (a Perforce changelist
236 238 number).
237 239
238 240 Mercurial Destination
239 241 '''''''''''''''''''''
240 242
241 243 The following options are supported:
242 244
243 245 convert.hg.clonebranches
244 246 dispatch source branches in separate clones. The default is
245 247 False.
246 248 convert.hg.tagsbranch
247 249 branch name for tag revisions, defaults to "default".
248 250 convert.hg.usebranchnames
249 251 preserve branch names. The default is True.
250 252
251 253 options:
252 254
253 255 -s --source-type TYPE source repository type
254 256 -d --dest-type TYPE destination repository type
255 257 -r --rev REV import up to target revision REV
256 258 -A --authormap FILE remap usernames using this file
257 259 --filemap FILE remap file names using contents of file
258 260 --splicemap FILE splice synthesized history into place
259 261 --branchmap FILE change branch names while converting
260 262 --branchsort try to sort changesets by branches
261 263 --datesort try to sort changesets by date
262 264 --sourcesort preserve source changesets order
263 265
264 266 use "hg -v help convert" to show more info
265 267 $ hg init a
266 268 $ cd a
267 269 $ echo a > a
268 270 $ hg ci -d'0 0' -Ama
269 271 adding a
270 272 $ hg cp a b
271 273 $ hg ci -d'1 0' -mb
272 274 $ hg rm a
273 275 $ hg ci -d'2 0' -mc
274 276 $ hg mv b a
275 277 $ hg ci -d'3 0' -md
276 278 $ echo a >> a
277 279 $ hg ci -d'4 0' -me
278 280 $ cd ..
279 281 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
280 282 assuming destination a-hg
281 283 initializing destination a-hg repository
282 284 scanning source...
283 285 sorting...
284 286 converting...
285 287 4 a
286 288 3 b
287 289 2 c
288 290 1 d
289 291 0 e
290 292 $ hg --cwd a-hg pull ../a
291 293 pulling from ../a
292 294 searching for changes
293 295 no changes found
294 296 $ touch bogusfile
295 297
296 298 should fail
297 299
298 300 $ hg convert a bogusfile
299 301 initializing destination bogusfile repository
300 302 abort: cannot create new bundle repository
301 303 [255]
302 304 $ mkdir bogusdir
303 305 $ chmod 000 bogusdir
304 306
305 307 should fail
306 308
307 309 $ hg convert a bogusdir
308 310 abort: Permission denied: bogusdir
309 311 [255]
310 312
311 313 should succeed
312 314
313 315 $ chmod 700 bogusdir
314 316 $ hg convert a bogusdir
315 317 initializing destination bogusdir repository
316 318 scanning source...
317 319 sorting...
318 320 converting...
319 321 4 a
320 322 3 b
321 323 2 c
322 324 1 d
323 325 0 e
324 326
325 327 test pre and post conversion actions
326 328
327 329 $ echo 'include b' > filemap
328 330 $ hg convert --debug --filemap filemap a partialb | \
329 331 > grep 'run hg'
330 332 run hg source pre-conversion action
331 333 run hg sink pre-conversion action
332 334 run hg sink post-conversion action
333 335 run hg source post-conversion action
334 336
335 337 converting empty dir should fail "nicely
336 338
337 339 $ mkdir emptydir
338 340
339 341 override $PATH to ensure p4 not visible; use $PYTHON in case we're
340 342 running from a devel copy, not a temp installation
341 343
342 344 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
343 345 assuming destination emptydir-hg
344 346 initializing destination emptydir-hg repository
345 347 emptydir does not look like a CVS checkout
346 348 emptydir does not look like a Git repository
347 349 emptydir does not look like a Subversion repository
348 350 emptydir is not a local Mercurial repository
349 351 emptydir does not look like a darcs repository
350 352 emptydir does not look like a monotone repository
351 353 emptydir does not look like a GNU Arch repository
352 354 emptydir does not look like a Bazaar repository
353 355 cannot find required "p4" tool
354 356 abort: emptydir: missing or unsupported repository
355 357 [255]
356 358
357 359 convert with imaginary source type
358 360
359 361 $ hg convert --source-type foo a a-foo
360 362 initializing destination a-foo repository
361 363 abort: foo: invalid source repository type
362 364 [255]
363 365
364 366 convert with imaginary sink type
365 367
366 368 $ hg convert --dest-type foo a a-foo
367 369 abort: foo: invalid destination repository type
368 370 [255]
369 371
370 372 testing: convert must not produce duplicate entries in fncache
371 373
372 374 $ hg convert a b
373 375 initializing destination b repository
374 376 scanning source...
375 377 sorting...
376 378 converting...
377 379 4 a
378 380 3 b
379 381 2 c
380 382 1 d
381 383 0 e
382 384
383 385 contents of fncache file:
384 386
385 387 $ cat b/.hg/store/fncache | sort
386 388 data/a.i
387 389 data/b.i
388 390
389 391 test bogus URL
390 392
391 393 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
392 394 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
393 395 [255]
@@ -1,764 +1,766
1 1 == paragraphs ==
2 2 60 column format:
3 3 ----------------------------------------------------------------------
4 4 This is some text in the first paragraph.
5 5
6 6 A small indented paragraph. It is followed by some lines
7 7 containing random whitespace.
8 8
9 9 The third and final paragraph.
10 10 ----------------------------------------------------------------------
11 11
12 12 30 column format:
13 13 ----------------------------------------------------------------------
14 14 This is some text in the first
15 15 paragraph.
16 16
17 17 A small indented paragraph.
18 18 It is followed by some lines
19 19 containing random
20 20 whitespace.
21 21
22 22 The third and final paragraph.
23 23 ----------------------------------------------------------------------
24 24
25 25 html format:
26 26 ----------------------------------------------------------------------
27 27 <p>
28 28 This is some text in the first paragraph.
29 29 </p>
30 30 <p>
31 31 A small indented paragraph.
32 32 It is followed by some lines
33 33 containing random whitespace.
34 34 </p>
35 35 <p>
36 36 The third and final paragraph.
37 37 </p>
38 38 ----------------------------------------------------------------------
39 39
40 40 == definitions ==
41 41 60 column format:
42 42 ----------------------------------------------------------------------
43 43 A Term
44 44 Definition. The indented lines make up the definition.
45 45
46 46 Another Term
47 47 Another definition. The final line in the definition
48 48 determines the indentation, so this will be indented
49 49 with four spaces.
50 50
51 51 A Nested/Indented Term
52 52 Definition.
53 53 ----------------------------------------------------------------------
54 54
55 55 30 column format:
56 56 ----------------------------------------------------------------------
57 57 A Term
58 58 Definition. The indented
59 59 lines make up the
60 60 definition.
61 61
62 62 Another Term
63 63 Another definition. The
64 64 final line in the
65 65 definition determines the
66 66 indentation, so this will
67 67 be indented with four
68 68 spaces.
69 69
70 70 A Nested/Indented Term
71 71 Definition.
72 72 ----------------------------------------------------------------------
73 73
74 74 html format:
75 75 ----------------------------------------------------------------------
76 76 <dl>
77 77 <dt>A Term
78 78 <dd>Definition. The indented lines make up the definition.
79 79 <dt>Another Term
80 80 <dd>Another definition. The final line in the definition determines the indentation, so this will be indented with four spaces.
81 81 <dt>A Nested/Indented Term
82 82 <dd>Definition.
83 83 </dl>
84 84 ----------------------------------------------------------------------
85 85
86 86 == literals ==
87 87 60 column format:
88 88 ----------------------------------------------------------------------
89 89 The fully minimized form is the most convenient form:
90 90
91 91 Hello
92 92 literal
93 93 world
94 94
95 95 In the partially minimized form a paragraph simply ends with
96 96 space-double-colon.
97 97
98 98 ////////////////////////////////////////
99 99 long un-wrapped line in a literal block
100 100 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
101 101
102 102 This literal block is started with '::',
103 103 the so-called expanded form. The paragraph
104 104 with '::' disappears in the final output.
105 105 ----------------------------------------------------------------------
106 106
107 107 30 column format:
108 108 ----------------------------------------------------------------------
109 109 The fully minimized form is
110 110 the most convenient form:
111 111
112 112 Hello
113 113 literal
114 114 world
115 115
116 116 In the partially minimized
117 117 form a paragraph simply ends
118 118 with space-double-colon.
119 119
120 120 ////////////////////////////////////////
121 121 long un-wrapped line in a literal block
122 122 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
123 123
124 124 This literal block is started with '::',
125 125 the so-called expanded form. The paragraph
126 126 with '::' disappears in the final output.
127 127 ----------------------------------------------------------------------
128 128
129 129 html format:
130 130 ----------------------------------------------------------------------
131 131 <p>
132 132 The fully minimized form is the most
133 133 convenient form:
134 134 </p>
135 135 <pre>
136 136 Hello
137 137 literal
138 138 world
139 139 </pre>
140 140 <p>
141 141 In the partially minimized form a paragraph
142 142 simply ends with space-double-colon.
143 143 </p>
144 144 <pre>
145 145 ////////////////////////////////////////
146 146 long un-wrapped line in a literal block
147 147 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
148 148 </pre>
149 149 <pre>
150 150 This literal block is started with '::',
151 151 the so-called expanded form. The paragraph
152 152 with '::' disappears in the final output.
153 153 </pre>
154 154 ----------------------------------------------------------------------
155 155
156 156 == lists ==
157 157 60 column format:
158 158 ----------------------------------------------------------------------
159 159 - This is the first list item.
160 160
161 161 Second paragraph in the first list item.
162 162
163 163 - List items need not be separated by a blank line.
164 164 - And will be rendered without one in any case.
165 165
166 166 We can have indented lists:
167 167
168 168 - This is an indented list item
169 169 - Another indented list item:
170 170
171 171 - A literal block in the middle
172 172 of an indented list.
173 173
174 174 (The above is not a list item since we are in the literal block.)
175 175
176 176 Literal block with no indentation (apart from
177 177 the two spaces added to all literal blocks).
178 178
179 179 1. This is an enumerated list (first item).
180 180 2. Continuing with the second item.
181 181 (1) foo
182 182 (2) bar
183 183 1) Another
184 184 2) List
185 185
186 186 Line blocks are also a form of list:
187 187
188 188 This is the first line. The line continues here.
189 189 This is the second line.
190 190 ----------------------------------------------------------------------
191 191
192 192 30 column format:
193 193 ----------------------------------------------------------------------
194 194 - This is the first list item.
195 195
196 196 Second paragraph in the
197 197 first list item.
198 198
199 199 - List items need not be
200 200 separated by a blank line.
201 201 - And will be rendered without
202 202 one in any case.
203 203
204 204 We can have indented lists:
205 205
206 206 - This is an indented list
207 207 item
208 208 - Another indented list
209 209 item:
210 210
211 211 - A literal block in the middle
212 212 of an indented list.
213 213
214 214 (The above is not a list item since we are in the literal block.)
215 215
216 216 Literal block with no indentation (apart from
217 217 the two spaces added to all literal blocks).
218 218
219 219 1. This is an enumerated list
220 220 (first item).
221 221 2. Continuing with the second
222 222 item.
223 223 (1) foo
224 224 (2) bar
225 225 1) Another
226 226 2) List
227 227
228 228 Line blocks are also a form of
229 229 list:
230 230
231 231 This is the first line. The
232 232 line continues here.
233 233 This is the second line.
234 234 ----------------------------------------------------------------------
235 235
236 236 html format:
237 237 ----------------------------------------------------------------------
238 238 <ul>
239 239 <li> This is the first list item.
240 240 <p>
241 241 Second paragraph in the first list item.
242 242 </p>
243 243 <li> List items need not be separated by a blank line.
244 244 <li> And will be rendered without one in any case.
245 245 </ul>
246 246 <p>
247 247 We can have indented lists:
248 248 </p>
249 249 <ul>
250 250 <li> This is an indented list item
251 251 <li> Another indented list item:
252 252 <pre>
253 253 - A literal block in the middle
254 254 of an indented list.
255 255 </pre>
256 256 <pre>
257 257 (The above is not a list item since we are in the literal block.)
258 258 </pre>
259 259 </ul>
260 260 <pre>
261 261 Literal block with no indentation (apart from
262 262 the two spaces added to all literal blocks).
263 263 </pre>
264 264 <ol>
265 265 <li> This is an enumerated list (first item).
266 266 <li> Continuing with the second item.
267 267 <li> foo
268 268 <li> bar
269 269 <li> Another
270 270 <li> List
271 271 </ol>
272 272 <p>
273 273 Line blocks are also a form of list:
274 274 </p>
275 275 <ol>
276 276 <li> This is the first line. The line continues here.
277 277 <li> This is the second line.
278 278 </ol>
279 279 ----------------------------------------------------------------------
280 280
281 281 == options ==
282 282 60 column format:
283 283 ----------------------------------------------------------------------
284 284 There is support for simple option lists, but only with long
285 285 options:
286 286
287 287 -X --exclude filter an option with a short and long option
288 288 with an argument
289 289 -I --include an option with both a short option and
290 290 a long option
291 291 --all Output all.
292 292 --both Output both (this description is quite
293 293 long).
294 294 --long Output all day long.
295 295 --par This option has two paragraphs in its
296 296 description. This is the first.
297 297
298 298 This is the second. Blank lines may
299 299 be omitted between options (as above)
300 300 or left in (as here).
301 301
302 302 The next paragraph looks like an option list, but lacks the
303 303 two-space marker after the option. It is treated as a normal
304 304 paragraph:
305 305
306 306 --foo bar baz
307 307 ----------------------------------------------------------------------
308 308
309 309 30 column format:
310 310 ----------------------------------------------------------------------
311 311 There is support for simple
312 312 option lists, but only with
313 313 long options:
314 314
315 315 -X --exclude filter an
316 316 option
317 317 with a
318 318 short
319 319 and
320 320 long
321 321 option
322 322 with an
323 323 argumen
324 324 t
325 325 -I --include an
326 326 option
327 327 with
328 328 both a
329 329 short
330 330 option
331 331 and a
332 332 long
333 333 option
334 334 --all Output
335 335 all.
336 336 --both Output
337 337 both
338 338 (this d
339 339 escript
340 340 ion is
341 341 quite
342 342 long).
343 343 --long Output
344 344 all day
345 345 long.
346 346 --par This
347 347 option
348 348 has two
349 349 paragra
350 350 phs in
351 351 its des
352 352 criptio
353 353 n. This
354 354 is the
355 355 first.
356 356
357 357 This is
358 358 the
359 359 second.
360 360 Blank
361 361 lines
362 362 may be
363 363 omitted
364 364 between
365 365 options
366 366 (as
367 367 above)
368 368 or left
369 369 in (as
370 370 here).
371 371
372 372 The next paragraph looks like
373 373 an option list, but lacks the
374 374 two-space marker after the
375 375 option. It is treated as a
376 376 normal paragraph:
377 377
378 378 --foo bar baz
379 379 ----------------------------------------------------------------------
380 380
381 381 html format:
382 382 ----------------------------------------------------------------------
383 383 <p>
384 384 There is support for simple option lists,
385 385 but only with long options:
386 386 </p>
387 387 <dl>
388 388 <dt>-X --exclude filter
389 389 <dd>an option with a short and long option with an argument
390 390 <dt>-I --include
391 391 <dd>an option with both a short option and a long option
392 392 <dt> --all
393 393 <dd>Output all.
394 394 <dt> --both
395 395 <dd>Output both (this description is quite long).
396 396 <dt> --long
397 397 <dd>Output all day long.
398 398 <dt> --par
399 399 <dd>This option has two paragraphs in its description. This is the first.
400 400 <p>
401 401 This is the second. Blank lines may be omitted between
402 402 options (as above) or left in (as here).
403 403 </p>
404 404 </dl>
405 405 <p>
406 406 The next paragraph looks like an option list, but lacks the two-space
407 407 marker after the option. It is treated as a normal paragraph:
408 408 </p>
409 409 <p>
410 410 --foo bar baz
411 411 </p>
412 412 ----------------------------------------------------------------------
413 413
414 414 == fields ==
415 415 60 column format:
416 416 ----------------------------------------------------------------------
417 417 a First item.
418 ab Second item. Indentation and wrapping is handled
419 automatically.
418 ab Second item. Indentation and wrapping is
419 handled automatically.
420 420
421 421 Next list:
422 422
423 423 small The larger key below triggers full indentation
424 424 here.
425 425 much too large
426 426 This key is big enough to get its own line.
427 427 ----------------------------------------------------------------------
428 428
429 429 30 column format:
430 430 ----------------------------------------------------------------------
431 431 a First item.
432 ab Second item. Indentation
433 and wrapping is handled
432 ab Second item.
433 Indentation and
434 wrapping is
435 handled
434 436 automatically.
435 437
436 438 Next list:
437 439
438 440 small The larger key
439 441 below triggers
440 442 full indentation
441 443 here.
442 444 much too large
443 445 This key is big
444 enough to get its
445 own line.
446 enough to get
447 its own line.
446 448 ----------------------------------------------------------------------
447 449
448 450 html format:
449 451 ----------------------------------------------------------------------
450 452 <dl>
451 453 <dt>a
452 454 <dd>First item.
453 455 <dt>ab
454 456 <dd>Second item. Indentation and wrapping is handled automatically.
455 457 </dl>
456 458 <p>
457 459 Next list:
458 460 </p>
459 461 <dl>
460 462 <dt>small
461 463 <dd>The larger key below triggers full indentation here.
462 464 <dt>much too large
463 465 <dd>This key is big enough to get its own line.
464 466 </dl>
465 467 ----------------------------------------------------------------------
466 468
467 469 == containers (normal) ==
468 470 60 column format:
469 471 ----------------------------------------------------------------------
470 472 Normal output.
471 473 ----------------------------------------------------------------------
472 474
473 475 30 column format:
474 476 ----------------------------------------------------------------------
475 477 Normal output.
476 478 ----------------------------------------------------------------------
477 479
478 480 html format:
479 481 ----------------------------------------------------------------------
480 482 <p>
481 483 Normal output.
482 484 </p>
483 485 ----------------------------------------------------------------------
484 486
485 487 == containers (verbose) ==
486 488 60 column format:
487 489 ----------------------------------------------------------------------
488 490 Normal output.
489 491
490 492 Verbose output.
491 493 ----------------------------------------------------------------------
492 494 ['debug', 'debug']
493 495 ----------------------------------------------------------------------
494 496
495 497 30 column format:
496 498 ----------------------------------------------------------------------
497 499 Normal output.
498 500
499 501 Verbose output.
500 502 ----------------------------------------------------------------------
501 503 ['debug', 'debug']
502 504 ----------------------------------------------------------------------
503 505
504 506 html format:
505 507 ----------------------------------------------------------------------
506 508 <p>
507 509 Normal output.
508 510 </p>
509 511 <p>
510 512 Verbose output.
511 513 </p>
512 514 ----------------------------------------------------------------------
513 515 ['debug', 'debug']
514 516 ----------------------------------------------------------------------
515 517
516 518 == containers (debug) ==
517 519 60 column format:
518 520 ----------------------------------------------------------------------
519 521 Normal output.
520 522
521 523 Initial debug output.
522 524 ----------------------------------------------------------------------
523 525 ['verbose']
524 526 ----------------------------------------------------------------------
525 527
526 528 30 column format:
527 529 ----------------------------------------------------------------------
528 530 Normal output.
529 531
530 532 Initial debug output.
531 533 ----------------------------------------------------------------------
532 534 ['verbose']
533 535 ----------------------------------------------------------------------
534 536
535 537 html format:
536 538 ----------------------------------------------------------------------
537 539 <p>
538 540 Normal output.
539 541 </p>
540 542 <p>
541 543 Initial debug output.
542 544 </p>
543 545 ----------------------------------------------------------------------
544 546 ['verbose']
545 547 ----------------------------------------------------------------------
546 548
547 549 == containers (verbose debug) ==
548 550 60 column format:
549 551 ----------------------------------------------------------------------
550 552 Normal output.
551 553
552 554 Initial debug output.
553 555
554 556 Verbose output.
555 557
556 558 Debug output.
557 559 ----------------------------------------------------------------------
558 560 []
559 561 ----------------------------------------------------------------------
560 562
561 563 30 column format:
562 564 ----------------------------------------------------------------------
563 565 Normal output.
564 566
565 567 Initial debug output.
566 568
567 569 Verbose output.
568 570
569 571 Debug output.
570 572 ----------------------------------------------------------------------
571 573 []
572 574 ----------------------------------------------------------------------
573 575
574 576 html format:
575 577 ----------------------------------------------------------------------
576 578 <p>
577 579 Normal output.
578 580 </p>
579 581 <p>
580 582 Initial debug output.
581 583 </p>
582 584 <p>
583 585 Verbose output.
584 586 </p>
585 587 <p>
586 588 Debug output.
587 589 </p>
588 590 ----------------------------------------------------------------------
589 591 []
590 592 ----------------------------------------------------------------------
591 593
592 594 == roles ==
593 595 60 column format:
594 596 ----------------------------------------------------------------------
595 597 Please see "hg add".
596 598 ----------------------------------------------------------------------
597 599
598 600 30 column format:
599 601 ----------------------------------------------------------------------
600 602 Please see "hg add".
601 603 ----------------------------------------------------------------------
602 604
603 605 html format:
604 606 ----------------------------------------------------------------------
605 607 <p>
606 608 Please see "hg add".
607 609 </p>
608 610 ----------------------------------------------------------------------
609 611
610 612 == sections ==
611 613 60 column format:
612 614 ----------------------------------------------------------------------
613 615 Title
614 616 =====
615 617
616 618 Section
617 619 -------
618 620
619 621 Subsection
620 622 ''''''''''
621 623
622 624 Markup: "foo" and "hg help"
623 625 ---------------------------
624 626 ----------------------------------------------------------------------
625 627
626 628 30 column format:
627 629 ----------------------------------------------------------------------
628 630 Title
629 631 =====
630 632
631 633 Section
632 634 -------
633 635
634 636 Subsection
635 637 ''''''''''
636 638
637 639 Markup: "foo" and "hg help"
638 640 ---------------------------
639 641 ----------------------------------------------------------------------
640 642
641 643 html format:
642 644 ----------------------------------------------------------------------
643 645 <h1>Title</h1>
644 646 <h2>Section</h2>
645 647 <h3>Subsection</h3>
646 648 <h2>Markup: "foo" and "hg help"</h2>
647 649 ----------------------------------------------------------------------
648 650
649 651 == admonitions ==
650 652 60 column format:
651 653 ----------------------------------------------------------------------
652 654 Note:
653 655 This is a note
654 656
655 657 - Bullet 1
656 658 - Bullet 2
657 659
658 660 Warning!
659 661 This is a warning Second input line of warning
660 662
661 663 !Danger!
662 664 This is danger
663 665 ----------------------------------------------------------------------
664 666
665 667 30 column format:
666 668 ----------------------------------------------------------------------
667 669 Note:
668 670 This is a note
669 671
670 672 - Bullet 1
671 673 - Bullet 2
672 674
673 675 Warning!
674 676 This is a warning Second
675 677 input line of warning
676 678
677 679 !Danger!
678 680 This is danger
679 681 ----------------------------------------------------------------------
680 682
681 683 html format:
682 684 ----------------------------------------------------------------------
683 685 <p>
684 686 <b>Note:</b> This is a note
685 687 </p>
686 688 <ul>
687 689 <li> Bullet 1
688 690 <li> Bullet 2
689 691 </ul>
690 692 <p>
691 693 <b>Warning!</b> This is a warning Second input line of warning
692 694 </p>
693 695 <p>
694 696 <b>!Danger!</b> This is danger
695 697 </p>
696 698 ----------------------------------------------------------------------
697 699
698 700 == comments ==
699 701 60 column format:
700 702 ----------------------------------------------------------------------
701 703 Some text.
702 704
703 705 Some indented text.
704 706
705 707 Empty comment above
706 708 ----------------------------------------------------------------------
707 709
708 710 30 column format:
709 711 ----------------------------------------------------------------------
710 712 Some text.
711 713
712 714 Some indented text.
713 715
714 716 Empty comment above
715 717 ----------------------------------------------------------------------
716 718
717 719 html format:
718 720 ----------------------------------------------------------------------
719 721 <p>
720 722 Some text.
721 723 </p>
722 724 <p>
723 725 Some indented text.
724 726 </p>
725 727 <p>
726 728 Empty comment above
727 729 </p>
728 730 ----------------------------------------------------------------------
729 731
730 732 === === ========================================
731 733 a b c
732 734 === === ========================================
733 735 1 2 3
734 736 foo bar baz this list is very very very long man
735 737 === === ========================================
736 738
737 739 == table ==
738 740 60 column format:
739 741 ----------------------------------------------------------------------
740 742 a b c
741 743 ------------------------------------------------
742 744 1 2 3
743 745 foo bar baz this list is very very very long man
744 746 ----------------------------------------------------------------------
745 747
746 748 30 column format:
747 749 ----------------------------------------------------------------------
748 750 a b c
749 751 ------------------------------
750 752 1 2 3
751 753 foo bar baz this list is
752 754 very very very long
753 755 man
754 756 ----------------------------------------------------------------------
755 757
756 758 html format:
757 759 ----------------------------------------------------------------------
758 760 <table>
759 761 <tr><th>a</th><th>b</th><th>c</th></tr>
760 762 <tr><td>1</td><td>2</td><td>3</td></tr>
761 763 <tr><td>foo</td><td>bar</td><td>baz this list is very very very long man</td></tr>
762 764 </table>
763 765 ----------------------------------------------------------------------
764 766
General Comments 0
You need to be logged in to leave comments. Login now