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