Skip to content

Commit 3785185

Browse files
authored
Merge pull request #339 from commonmark/issue-337-lazy-continuation-line-source-span
Fix source spans of blocks with lazy continuation lines
2 parents 6bebfe2 + 7be4204 commit 3785185

2 files changed

Lines changed: 66 additions & 4 deletions

File tree

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ private void parseLine(String ln) {
288288
// What remains at the offset is a text line. Add the text to the
289289
// appropriate block.
290290

291-
// First check for a lazy paragraph continuation:
291+
// First check for a lazy continuation line
292292
if (!startedNewBlock && !isBlank() &&
293293
getActiveBlockParser().canHaveLazyContinuationLines()) {
294294
openBlockParsers.get(openBlockParsers.size() - 1).sourceIndex = lastIndex;
@@ -441,10 +441,12 @@ private void addLine() {
441441

442442
private void addSourceSpans() {
443443
if (includeSourceSpans != IncludeSourceSpans.NONE) {
444-
// Don't add source spans for Document itself (it would get the whole source text)
444+
// Don't add source spans for Document itself (it would get the whole source text), so start at 1, not 0
445445
for (int i = 1; i < openBlockParsers.size(); i++) {
446-
OpenBlockParser openBlockParser = openBlockParsers.get(i);
447-
int blockIndex = openBlockParser.sourceIndex;
446+
var openBlockParser = openBlockParsers.get(i);
447+
// In case of a lazy continuation line, the index is less than where the block parser would expect the
448+
// contents to start, so let's use whichever is smaller.
449+
int blockIndex = Math.min(openBlockParser.sourceIndex, index);
448450
int length = line.getContent().length() - blockIndex;
449451
if (length != 0) {
450452
openBlockParser.blockParser.addSourceSpan(SourceSpan.of(lineIndex, blockIndex, length));

commonmark/src/test/java/org/commonmark/test/SourceSpansTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,66 @@ public void linkReferenceDefinitionHeading() {
204204
assertEquals(List.of(SourceSpan.of(1, 0, 7), SourceSpan.of(2, 0, 3)), heading.getSourceSpans());
205205
}
206206

207+
@Test
208+
public void lazyContinuationLines() {
209+
{
210+
// From https://spec.commonmark.org/0.31.2/#example-250
211+
// Wrong source span for the inner block quote for the second line.
212+
var doc = PARSER.parse("> > > foo\nbar\n");
213+
214+
var bq1 = (BlockQuote) doc.getLastChild();
215+
assertEquals(List.of(SourceSpan.of(0, 0, 9), SourceSpan.of(1, 0, 3)), bq1.getSourceSpans());
216+
var bq2 = (BlockQuote) bq1.getLastChild();
217+
assertEquals(List.of(SourceSpan.of(0, 2, 7), SourceSpan.of(1, 0, 3)), bq2.getSourceSpans());
218+
var bq3 = (BlockQuote) bq2.getLastChild();
219+
assertEquals(List.of(SourceSpan.of(0, 4, 5), SourceSpan.of(1, 0, 3)), bq3.getSourceSpans());
220+
var paragraph = (Paragraph) bq3.getLastChild();
221+
assertEquals(List.of(SourceSpan.of(0, 6, 3), SourceSpan.of(1, 0, 3)), paragraph.getSourceSpans());
222+
}
223+
224+
{
225+
// Adding one character to the last line remove blockQuote3 source for the second line
226+
var doc = PARSER.parse("> > > foo\nbars\n");
227+
228+
var bq1 = (BlockQuote) doc.getLastChild();
229+
assertEquals(List.of(SourceSpan.of(0, 0, 9), SourceSpan.of(1, 0, 4)), bq1.getSourceSpans());
230+
var bq2 = (BlockQuote) bq1.getLastChild();
231+
assertEquals(List.of(SourceSpan.of(0, 2, 7), SourceSpan.of(1, 0, 4)), bq2.getSourceSpans());
232+
var bq3 = (BlockQuote) bq2.getLastChild();
233+
assertEquals(List.of(SourceSpan.of(0, 4, 5), SourceSpan.of(1, 0, 4)), bq3.getSourceSpans());
234+
var paragraph = (Paragraph) bq3.getLastChild();
235+
assertEquals(List.of(SourceSpan.of(0, 6, 3), SourceSpan.of(1, 0, 4)), paragraph.getSourceSpans());
236+
}
237+
238+
{
239+
// From https://spec.commonmark.org/0.31.2/#example-292
240+
var doc = PARSER.parse("> 1. > Blockquote\ncontinued here.");
241+
242+
var bq1 = (BlockQuote) doc.getLastChild();
243+
assertEquals(List.of(SourceSpan.of(0, 0, 17), SourceSpan.of(1, 0, 15)), bq1.getSourceSpans());
244+
var orderedList = (OrderedList) bq1.getLastChild();
245+
assertEquals(List.of(SourceSpan.of(0, 2, 15), SourceSpan.of(1, 0, 15)), orderedList.getSourceSpans());
246+
var listItem = (ListItem) orderedList.getLastChild();
247+
assertEquals(List.of(SourceSpan.of(0, 2, 15), SourceSpan.of(1, 0, 15)), listItem.getSourceSpans());
248+
var bq2 = (BlockQuote) listItem.getLastChild();
249+
assertEquals(List.of(SourceSpan.of(0, 5, 12), SourceSpan.of(1, 0, 15)), bq2.getSourceSpans());
250+
var paragraph = (Paragraph) bq2.getLastChild();
251+
assertEquals(List.of(SourceSpan.of(0, 7, 10), SourceSpan.of(1, 0, 15)), paragraph.getSourceSpans());
252+
}
253+
254+
{
255+
// Lazy continuation line for nested blockquote
256+
var doc = PARSER.parse("> > foo\n> bar\n");
257+
258+
var bq1 = (BlockQuote) doc.getLastChild();
259+
assertEquals(List.of(SourceSpan.of(0, 0, 7), SourceSpan.of(1, 0, 5)), bq1.getSourceSpans());
260+
var bq2 = (BlockQuote) bq1.getLastChild();
261+
assertEquals(List.of(SourceSpan.of(0, 2, 5), SourceSpan.of(1, 2, 3)), bq2.getSourceSpans());
262+
var paragraph = (Paragraph) bq2.getLastChild();
263+
assertEquals(List.of(SourceSpan.of(0, 4, 3), SourceSpan.of(1, 2, 3)), paragraph.getSourceSpans());
264+
}
265+
}
266+
207267
@Test
208268
public void visualCheck() {
209269
assertEquals("(> {[* <foo>]})\n(> {[ <bar>]})\n(> {⸢* ⸤baz⸥⸣})\n",

0 commit comments

Comments
 (0)