Skip to content

Commit b1cb623

Browse files
committed
Merge pull request #47 from atlassian/spec-0.25
Update to CommonMark spec 0.25
2 parents 6e3a713 + a2ad9b3 commit b1cb623

8 files changed

Lines changed: 215 additions & 101 deletions

File tree

commonmark-test-util/src/main/resources/spec.txt

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22
title: CommonMark Spec
33
author: John MacFarlane
4-
version: 0.24
5-
date: '2015-01-12'
4+
version: 0.25
5+
date: '2016-03-24'
66
license: '[CC-BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)'
77
...
88

@@ -301,15 +301,40 @@ by spaces with a tab stop of 4 characters.
301301
</ul>
302302
````````````````````````````````
303303

304+
```````````````````````````````` example
305+
- foo
306+
307+
→→bar
308+
.
309+
<ul>
310+
<li>
311+
<p>foo</p>
312+
<pre><code> bar
313+
</code></pre>
314+
</li>
315+
</ul>
316+
````````````````````````````````
304317

305318
```````````````````````````````` example
306-
>→foo→bar
319+
>→→foo
307320
.
308321
<blockquote>
309-
<p>foo→bar</p>
322+
<pre><code> foo
323+
</code></pre>
310324
</blockquote>
311325
````````````````````````````````
312326

327+
```````````````````````````````` example
328+
-→→foo
329+
.
330+
<ul>
331+
<li>
332+
<pre><code> foo
333+
</code></pre>
334+
</li>
335+
</ul>
336+
````````````````````````````````
337+
313338

314339
```````````````````````````````` example
315340
foo
@@ -320,6 +345,24 @@ bar
320345
</code></pre>
321346
````````````````````````````````
322347

348+
```````````````````````````````` example
349+
- foo
350+
- bar
351+
→ - baz
352+
.
353+
<ul>
354+
<li>foo
355+
<ul>
356+
<li>bar
357+
<ul>
358+
<li>baz</li>
359+
</ul>
360+
</li>
361+
</ul>
362+
</li>
363+
</ul>
364+
````````````````````````````````
365+
323366

324367

325368
## Insecure characters
@@ -1204,7 +1247,7 @@ bar</p>
12041247

12051248

12061249
or use a thematic break that cannot count as a [setext heading
1207-
line], such as
1250+
underline], such as
12081251

12091252
```````````````````````````````` example
12101253
Foo
@@ -8940,7 +8983,7 @@ This is text that can be incorporated into the last open
89408983
block (a paragraph, code block, heading, or raw HTML).
89418984

89428985
Setext headings are formed when we see a line of a paragraph
8943-
that is a setext heading line.
8986+
that is a [setext heading underline].
89448987

89458988
Reference link definitions are detected when a paragraph is closed;
89468989
the accumulated text lines are parsed to see if they begin with

commonmark/src/main/java/org/commonmark/internal/BlockQuoteParser.java

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.commonmark.internal;
22

3+
import org.commonmark.internal.util.Parsing;
34
import org.commonmark.node.Block;
45
import org.commonmark.node.BlockQuote;
56
import org.commonmark.parser.block.*;
@@ -26,29 +27,33 @@ public BlockQuote getBlock() {
2627
@Override
2728
public BlockContinue tryContinue(ParserState state) {
2829
int nextNonSpace = state.getNextNonSpaceIndex();
29-
CharSequence line = state.getLine();
30-
if (state.getIndent() <= 3 && nextNonSpace < line.length() && line.charAt(nextNonSpace) == '>') {
31-
int newIndex = nextNonSpace + 1;
32-
if (newIndex < line.length() && line.charAt(newIndex) == ' ') {
33-
newIndex++;
30+
if (isMarker(state, nextNonSpace)) {
31+
int newColumn = state.getColumn() + state.getIndent() + 1;
32+
// optional following space or tab
33+
if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
34+
newColumn++;
3435
}
35-
return BlockContinue.atIndex(newIndex);
36+
return BlockContinue.atColumn(newColumn);
3637
} else {
3738
return BlockContinue.none();
3839
}
3940
}
4041

42+
private static boolean isMarker(ParserState state, int index) {
43+
CharSequence line = state.getLine();
44+
return state.getIndent() < Parsing.CODE_BLOCK_INDENT && index < line.length() && line.charAt(index) == '>';
45+
}
46+
4147
public static class Factory extends AbstractBlockParserFactory {
4248
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
43-
CharSequence line = state.getLine();
4449
int nextNonSpace = state.getNextNonSpaceIndex();
45-
if (state.getIndent() < 4 && line.charAt(nextNonSpace) == '>') {
46-
int newOffset = nextNonSpace + 1;
47-
// optional following space
48-
if (newOffset < line.length() && line.charAt(newOffset) == ' ') {
49-
newOffset++;
50+
if (isMarker(state, nextNonSpace)) {
51+
int newColumn = state.getColumn() + state.getIndent() + 1;
52+
// optional following space or tab
53+
if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
54+
newColumn++;
5055
}
51-
return BlockStart.of(new BlockQuoteParser()).atIndex(newOffset);
56+
return BlockStart.of(new BlockQuoteParser()).atColumn(newColumn);
5257
} else {
5358
return BlockStart.none();
5459
}

commonmark/src/main/java/org/commonmark/internal/DocumentParser.java

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ public class DocumentParser implements ParserState {
3535

3636
private int nextNonSpace = 0;
3737
private int nextNonSpaceColumn = 0;
38-
private boolean blank;
39-
4038
private int indent = 0;
39+
private boolean blank;
40+
private boolean columnIsInTab;
4141

4242
private final List<BlockParserFactory> blockParserFactories;
4343
private final InlineParserImpl inlineParser;
@@ -192,7 +192,7 @@ private void incorporateLine(CharSequence ln) {
192192
findNextNonSpace();
193193

194194
// this is a little performance optimization:
195-
if (isBlank() || (indent < IndentedCodeBlockParser.INDENT && Parsing.isLetter(line, nextNonSpace))) {
195+
if (isBlank() || (indent < Parsing.CODE_BLOCK_INDENT && Parsing.isLetter(line, nextNonSpace))) {
196196
setNewIndex(nextNonSpace);
197197
break;
198198
}
@@ -301,20 +301,46 @@ private void setNewColumn(int newColumn) {
301301
// Last character was a tab and we overshot our target
302302
index--;
303303
column = newColumn;
304+
columnIsInTab = true;
305+
} else {
306+
columnIsInTab = false;
304307
}
305308
}
306309

307310
private void advance() {
308311
char c = line.charAt(index);
309312
if (c == '\t') {
310313
index++;
311-
column += (4 - (column % 4));
314+
column += Parsing.columnsToNextTabStop(column);
312315
} else {
313316
index++;
314317
column++;
315318
}
316319
}
317320

321+
/**
322+
* Add line content to the active block parser. We assume it can accept lines -- that check should be done before
323+
* calling this.
324+
*/
325+
private void addLine() {
326+
CharSequence content;
327+
if (columnIsInTab) {
328+
// Our column is in a partially consumed tab. Expand the remaining columns (to the next tab stop) to spaces.
329+
int afterTab = index + 1;
330+
CharSequence rest = line.subSequence(afterTab, line.length());
331+
int spaces = Parsing.columnsToNextTabStop(column);
332+
StringBuilder sb = new StringBuilder(spaces + rest.length());
333+
for (int i = 0; i < spaces; i++) {
334+
sb.append(' ');
335+
}
336+
sb.append(rest);
337+
content = sb.toString();
338+
} else {
339+
content = line.subSequence(index, line.length());
340+
}
341+
getActiveBlockParser().addLine(content);
342+
}
343+
318344
private BlockStartImpl findBlockStart(BlockParser blockParser) {
319345
MatchedBlockParser matchedBlockParser = new MatchedBlockParserImpl(blockParser);
320346
for (BlockParserFactory blockParserFactory : blockParserFactories) {
@@ -410,14 +436,6 @@ private void breakOutOfLists(List<BlockParser> blockParsers) {
410436
}
411437
}
412438

413-
/**
414-
* Add a line to the block at the tip. We assume the tip can accept lines -- that check should be done before
415-
* calling this.
416-
*/
417-
private void addLine() {
418-
getActiveBlockParser().addLine(line.subSequence(index, line.length()));
419-
}
420-
421439
/**
422440
* Add block of type tag as a child of the tip. If the tip can't accept children, close and finalize it and try
423441
* its parent, and so on til we find a block that can accept children.

commonmark/src/main/java/org/commonmark/internal/IndentedCodeBlockParser.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package org.commonmark.internal;
22

3+
import org.commonmark.internal.util.Parsing;
34
import org.commonmark.node.*;
45
import org.commonmark.parser.block.*;
56

67
import java.util.regex.Pattern;
78

89
public class IndentedCodeBlockParser extends AbstractBlockParser {
910

10-
public static int INDENT = 4;
11-
1211
private static final Pattern TRAILING_BLANK_LINES = Pattern.compile("(?:\n[ \t]*)+$");
1312

1413
private final IndentedCodeBlock block = new IndentedCodeBlock();
@@ -21,8 +20,8 @@ public Block getBlock() {
2120

2221
@Override
2322
public BlockContinue tryContinue(ParserState state) {
24-
if (state.getIndent() >= INDENT) {
25-
return BlockContinue.atColumn(state.getColumn() + INDENT);
23+
if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT) {
24+
return BlockContinue.atColumn(state.getColumn() + Parsing.CODE_BLOCK_INDENT);
2625
} else if (state.isBlank()) {
2726
return BlockContinue.atIndex(state.getNextNonSpaceIndex());
2827
} else {
@@ -51,8 +50,8 @@ public static class Factory extends AbstractBlockParserFactory {
5150
@Override
5251
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
5352
// An indented code block cannot interrupt a paragraph.
54-
if (state.getIndent() >= INDENT && !state.isBlank() && !(state.getActiveBlockParser().getBlock() instanceof Paragraph)) {
55-
return BlockStart.of(new IndentedCodeBlockParser()).atColumn(state.getColumn() + INDENT);
53+
if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT && !state.isBlank() && !(state.getActiveBlockParser().getBlock() instanceof Paragraph)) {
54+
return BlockStart.of(new IndentedCodeBlockParser()).atColumn(state.getColumn() + Parsing.CODE_BLOCK_INDENT);
5655
} else {
5756
return BlockStart.none();
5857
}

0 commit comments

Comments
 (0)