##// END OF EJS Templates
minirst: improve layout of field lists...
Martin Geisler -
r10065:a1ae0ed7 default
parent child Browse files
Show More
@@ -1,277 +1,316 b''
1 1 # minirst.py - minimal reStructuredText parser
2 2 #
3 3 # Copyright 2009 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, incorporated herein by reference.
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 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):( +)(.*)')
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 _fieldwidth = 12
163
164 def updatefieldlists(blocks):
165 """Find key and maximum key width for field lists."""
166 i = 0
167 while i < len(blocks):
168 if blocks[i]['type'] != 'field':
169 i += 1
170 continue
171
172 keywidth = 0
173 j = i
174 while j < len(blocks) and blocks[j]['type'] == 'field':
175 m = _fieldre.match(blocks[j]['lines'][0])
176 key, rest = m.groups()
177 blocks[j]['lines'][0] = rest
178 blocks[j]['key'] = key
179 keywidth = max(keywidth, len(key))
180 j += 1
181
182 for block in blocks[i:j]:
183 block['keywidth'] = keywidth
184 i = j + 1
185
186 return blocks
187
188
162 189 def findsections(blocks):
163 190 """Finds sections.
164 191
165 192 The blocks must have a 'type' field, i.e., they should have been
166 193 run through findliteralblocks first.
167 194 """
168 195 for block in blocks:
169 196 # Searching for a block that looks like this:
170 197 #
171 198 # +------------------------------+
172 199 # | Section title |
173 200 # | ------------- |
174 201 # +------------------------------+
175 202 if (block['type'] == 'paragraph' and
176 203 len(block['lines']) == 2 and
177 204 block['lines'][1] == '-' * len(block['lines'][0])):
178 205 block['type'] = 'section'
179 206 return blocks
180 207
181 208
182 209 def inlineliterals(blocks):
183 210 for b in blocks:
184 211 if b['type'] == 'paragraph':
185 212 b['lines'] = [l.replace('``', '"') for l in b['lines']]
186 213 return blocks
187 214
188 215
189 216 def addmargins(blocks):
190 217 """Adds empty blocks for vertical spacing.
191 218
192 219 This groups bullets, options, and definitions together with no vertical
193 220 space between them, and adds an empty block between all other blocks.
194 221 """
195 222 i = 1
196 223 while i < len(blocks):
197 224 if (blocks[i]['type'] == blocks[i-1]['type'] and
198 225 blocks[i]['type'] in ('bullet', 'option', 'field', 'definition')):
199 226 i += 1
200 227 else:
201 228 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
202 229 i += 2
203 230 return blocks
204 231
205 232
206 233 def formatblock(block, width):
207 234 """Format a block according to width."""
208 235 if width <= 0:
209 236 width = 78
210 237 indent = ' ' * block['indent']
211 238 if block['type'] == 'margin':
212 239 return ''
213 240 if block['type'] == 'literal':
214 241 indent += ' '
215 242 return indent + ('\n' + indent).join(block['lines'])
216 243 if block['type'] == 'section':
217 244 return indent + ('\n' + indent).join(block['lines'])
218 245 if block['type'] == 'definition':
219 246 term = indent + block['lines'][0]
220 247 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
221 248 defindent = indent + hang * ' '
222 249 text = ' '.join(map(str.strip, block['lines'][1:]))
223 250 return "%s\n%s" % (term, textwrap.fill(text, width=width,
224 251 initial_indent=defindent,
225 252 subsequent_indent=defindent))
226 253 initindent = subindent = indent
227 254 if block['type'] == 'bullet':
228 255 m = _bulletre.match(block['lines'][0])
229 256 subindent = indent + m.end() * ' '
230 257 elif block['type'] == 'field':
231 m = _fieldre.match(block['lines'][0])
232 key, spaces, rest = m.groups()
233 # Turn ":foo: bar" into "foo bar".
234 block['lines'][0] = '%s %s%s' % (key, spaces, rest)
235 subindent = indent + (2 + len(key) + len(spaces)) * ' '
258 keywidth = block['keywidth']
259 key = block['key']
260
261 subindent = indent + _fieldwidth * ' '
262 if len(key) + 2 > _fieldwidth:
263 # key too large, use full line width
264 key = key.ljust(width)
265 elif keywidth + 2 < _fieldwidth:
266 # all keys are small, add only two spaces
267 key = key.ljust(keywidth + 2)
268 subindent = indent + (keywidth + 2) * ' '
269 else:
270 # mixed sizes, use fieldwidth for this one
271 key = key.ljust(_fieldwidth)
272 block['lines'][0] = key + block['lines'][0]
236 273 elif block['type'] == 'option':
237 274 m = _optionre.match(block['lines'][0])
238 275 option, arg, rest = m.groups()
239 276 subindent = indent + (len(option) + len(arg)) * ' '
240 277
241 278 text = ' '.join(map(str.strip, block['lines']))
242 279 return textwrap.fill(text, width=width,
243 280 initial_indent=initindent,
244 281 subsequent_indent=subindent)
245 282
246 283
247 284 def format(text, width, indent=0):
248 285 """Parse and format the text according to width."""
249 286 blocks = findblocks(text)
250 287 for b in blocks:
251 288 b['indent'] += indent
252 289 blocks = findliteralblocks(blocks)
253 290 blocks = inlineliterals(blocks)
254 291 blocks = splitparagraphs(blocks)
292 blocks = updatefieldlists(blocks)
255 293 blocks = findsections(blocks)
256 294 blocks = addmargins(blocks)
257 295 return '\n'.join(formatblock(b, width) for b in blocks)
258 296
259 297
260 298 if __name__ == "__main__":
261 299 from pprint import pprint
262 300
263 301 def debug(func, blocks):
264 302 blocks = func(blocks)
265 303 print "*** after %s:" % func.__name__
266 304 pprint(blocks)
267 305 print
268 306 return blocks
269 307
270 308 text = open(sys.argv[1]).read()
271 309 blocks = debug(findblocks, text)
272 310 blocks = debug(findliteralblocks, blocks)
273 311 blocks = debug(inlineliterals, blocks)
274 312 blocks = debug(splitparagraphs, blocks)
313 blocks = debug(updatefieldlists, blocks)
275 314 blocks = debug(findsections, blocks)
276 315 blocks = debug(addmargins, blocks)
277 316 print '\n'.join(formatblock(b, 30) for b in blocks)
@@ -1,33 +1,33 b''
1 1 adding a
2 2 # missing arg
3 3 hg cat: invalid arguments
4 4 hg cat [OPTION]... FILE...
5 5
6 6 output the current or given revision of files
7 7
8 8 Print the specified files as they were at the given revision. If no
9 9 revision is given, the parent of the working directory is used, or tip if
10 10 no revision is checked out.
11 11
12 12 Output may be to a file, in which case the name of the file is given using
13 13 a format string. The formatting rules are the same as for the export
14 14 command, with the following additions:
15 15
16 "%s" basename of file being printed
17 "%d" dirname of file being printed, or '.' if in repository root
18 "%p" root-relative path name of file being printed
16 "%s" basename of file being printed
17 "%d" dirname of file being printed, or '.' if in repository root
18 "%p" root-relative path name of file being printed
19 19
20 20 options:
21 21
22 22 -o --output print output to file with formatted name
23 23 -r --rev print the given revision
24 24 --decode apply any matching decode filter
25 25 -I --include include names matching the given patterns
26 26 -X --exclude exclude names matching the given patterns
27 27
28 28 use "hg -v help cat" to show global options
29 29 % [defaults]
30 30 a
31 31 a: No such file in rev 000000000000
32 32 % no repo
33 33 abort: There is no Mercurial repository here (.hg not found)!
@@ -1,147 +1,148 b''
1 1 #!/usr/bin/env python
2 2
3 3 from mercurial import minirst
4 4
5 5 def debugformat(title, text, width):
6 6 print "%s formatted to fit within %d characters:" % (title, width)
7 7 print "-" * 70
8 8 print minirst.format(text, width)
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
19 19
20 20
21 21 The third and final paragraph.
22 22 """
23 23
24 24 debugformat('paragraphs', paragraphs, 60)
25 25 debugformat('paragraphs', paragraphs, 30)
26 26
27 27
28 28 definitions = """
29 29 A Term
30 30 Definition. The indented
31 31 lines make up the definition.
32 32 Another Term
33 33 Another definition. The final line in the
34 34 definition determines the indentation, so
35 35 this will be indented with four spaces.
36 36
37 37 A Nested/Indented Term
38 38 Definition.
39 39 """
40 40
41 41 debugformat('definitions', definitions, 60)
42 42 debugformat('definitions', definitions, 30)
43 43
44 44
45 45 literals = r"""
46 46 The fully minimized form is the most
47 47 convenient form::
48 48
49 49 Hello
50 50 literal
51 51 world
52 52
53 53 In the partially minimized form a paragraph
54 54 simply ends with space-double-colon. ::
55 55
56 56 ////////////////////////////////////////
57 57 long un-wrapped line in a literal block
58 58 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
59 59
60 60 ::
61 61
62 62 This literal block is started with '::',
63 63 the so-called expanded form. The paragraph
64 64 with '::' disappears in the final output.
65 65 """
66 66
67 67 debugformat('literals', literals, 60)
68 68 debugformat('literals', literals, 30)
69 69
70 70
71 71 lists = """
72 72 - This is the first list item.
73 73
74 74 Second paragraph in the first list item.
75 75
76 76 - List items need not be separated
77 77 by a blank line.
78 78 - And will be rendered without
79 79 one in any case.
80 80
81 81 We can have indented lists:
82 82
83 83 - This is an indented list item
84 84
85 85 - Another indented list item::
86 86
87 87 - A literal block in the middle
88 88 of an indented list.
89 89
90 90 (The above is not a list item since we are in the literal block.)
91 91
92 92 ::
93 93
94 94 Literal block with no indentation (apart from
95 95 the two spaces added to all literal blocks).
96 96
97 97 1. This is an enumerated list (first item).
98 98 2. Continuing with the second item.
99 99
100 100 (1) foo
101 101 (2) bar
102 102
103 103 1) Another
104 104 2) List
105 105 """
106 106
107 107 debugformat('lists', lists, 60)
108 108 debugformat('lists', lists, 30)
109 109
110 110
111 111 options = """
112 112 There is support for simple option lists,
113 113 but only with long options:
114 114
115 115 --all Output all.
116 116 --both Output both (this description is
117 117 quite long).
118 118 --long Output all day long.
119 119
120 120 --par This option has two paragraphs in its description.
121 121 This is the first.
122 122
123 123 This is the second. Blank lines may be omitted between
124 124 options (as above) or left in (as here).
125 125
126 126 The next paragraph looks like an option list, but lacks the two-space
127 127 marker after the option. It is treated as a normal paragraph:
128 128
129 129 --foo bar baz
130 130 """
131 131
132 132 debugformat('options', options, 60)
133 133 debugformat('options', options, 30)
134 134
135 135
136 136 fields = """
137 Field lists give a simple two-column layout:
137 :a: First item.
138 :ab: Second item. Indentation and wrapping
139 is handled automatically.
138 140
139 :key: The whitespace following the key is
140 significant for the wrapping of this text.
141 :another key: More text.
142 The indentation on the following
143 lines is not significant.
141 Next list:
142
143 :small: The larger key below triggers full indentation here.
144 :much too large: This key is big enough to get its own line.
144 145 """
145 146
146 147 debugformat('fields', fields, 60)
147 148 debugformat('fields', fields, 30)
@@ -1,243 +1,248 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 Field lists give a simple two-column layout:
218 a First item.
219 ab Second item. Indentation and wrapping is handled
220 automatically.
219 221
220 key The whitespace following the key is
221 significant for the wrapping of this text.
222 another key More text. The indentation on the following
223 lines is not significant.
222 Next list:
223
224 small The larger key below triggers full indentation
225 here.
226 much too large
227 This key is big enough to get its own line.
224 228 ----------------------------------------------------------------------
225 229
226 230 fields formatted to fit within 30 characters:
227 231 ----------------------------------------------------------------------
228 Field lists give a simple two-
229 column layout:
232 a First item.
233 ab Second item. Indentation
234 and wrapping is handled
235 automatically.
236
237 Next list:
230 238
231 key The whitespace
232 following the
233 key is
234 significant for
235 the wrapping of
236 this text.
237 another key More text. The
238 indentation on
239 the following
240 lines is not
241 significant.
239 small The larger key
240 below triggers
241 full indentation
242 here.
243 much too large
244 This key is big
245 enough to get its
246 own line.
242 247 ----------------------------------------------------------------------
243 248
General Comments 0
You need to be logged in to leave comments. Login now