|
|
@ -3,8 +3,8 @@ |
|
|
|
'use strict'; |
|
|
|
|
|
|
|
|
|
|
|
var isEmpty = require('../helpers').isEmpty; |
|
|
|
var skipSpaces = require('../helpers').skipSpaces; |
|
|
|
var isEmpty = require('../helpers').isEmpty; |
|
|
|
var skipSpaces = require('../helpers').skipSpaces; |
|
|
|
|
|
|
|
|
|
|
|
// Search `[-+*][\n ]`, returns next pos arter marker on success
|
|
|
@ -77,8 +77,7 @@ function skipOrderedListMarker(state, startLine) { |
|
|
|
|
|
|
|
|
|
|
|
module.exports = function list(state, startLine, endLine, silent) { |
|
|
|
var line, |
|
|
|
nextLine, |
|
|
|
var nextLine, |
|
|
|
indent, |
|
|
|
start, |
|
|
|
posAfterMarker, |
|
|
@ -86,13 +85,14 @@ module.exports = function list(state, startLine, endLine, silent) { |
|
|
|
indentAfterMarker, |
|
|
|
markerValue, |
|
|
|
isOrdered, |
|
|
|
lastNonEmptyLine, |
|
|
|
hasNextItem, |
|
|
|
lastLine, |
|
|
|
subState, |
|
|
|
posNext, |
|
|
|
subString, |
|
|
|
contentStart, |
|
|
|
listTokIdx, |
|
|
|
rules_named = state.lexerBlock.rules_named; |
|
|
|
lineMax, |
|
|
|
endOfList; |
|
|
|
//rules_named = state.lexerBlock.rules_named;
|
|
|
|
|
|
|
|
// Detect list type and position after marker
|
|
|
|
if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) { |
|
|
@ -130,13 +130,21 @@ module.exports = function list(state, startLine, endLine, silent) { |
|
|
|
// Iterate list items
|
|
|
|
//
|
|
|
|
|
|
|
|
line = startLine; |
|
|
|
nextLine = line + 1; |
|
|
|
nextLine = startLine; |
|
|
|
lineMax = state.lineMax; |
|
|
|
endOfList = false; |
|
|
|
|
|
|
|
while (line < endLine) { |
|
|
|
while (nextLine < endLine && !endOfList) { |
|
|
|
if (isOrdered) { |
|
|
|
posAfterMarker = skipOrderedListMarker(state, nextLine); |
|
|
|
if (posAfterMarker < 0) { break; } |
|
|
|
} else { |
|
|
|
posAfterMarker = skipBulletListMarker(state, nextLine); |
|
|
|
if (posAfterMarker < 0) { break; } |
|
|
|
} |
|
|
|
|
|
|
|
contentStart = skipSpaces(state, posAfterMarker); |
|
|
|
max = state.eMarks[line]; |
|
|
|
max = state.eMarks[nextLine]; |
|
|
|
|
|
|
|
if (contentStart >= max) { |
|
|
|
// trimming space in "- \n 3" case, indent is 1 here
|
|
|
@ -155,96 +163,70 @@ module.exports = function list(state, startLine, endLine, silent) { |
|
|
|
|
|
|
|
// " - test"
|
|
|
|
// ^^^^^ - calculating total length of this thing
|
|
|
|
indent = (posAfterMarker - state.bMarks[line]) + indentAfterMarker; |
|
|
|
indent = (posAfterMarker - state.bMarks[nextLine]) + indentAfterMarker; |
|
|
|
|
|
|
|
//
|
|
|
|
// Scan lines inside list items
|
|
|
|
//
|
|
|
|
lastNonEmptyLine = line; |
|
|
|
hasNextItem = false; |
|
|
|
|
|
|
|
for (; nextLine < endLine; nextLine++) { |
|
|
|
if (isEmpty(state, nextLine)) { |
|
|
|
// TODO: check right fenced code block
|
|
|
|
// Problem - can be in nested list, should detect indent right
|
|
|
|
lastLine = startLine; |
|
|
|
|
|
|
|
// two successive newlines end the list
|
|
|
|
if (lastNonEmptyLine < nextLine - 1) { break; } |
|
|
|
continue; |
|
|
|
} |
|
|
|
// Run sublexer & write tokens
|
|
|
|
state.tokens.push({ type: 'list_item_open' }); |
|
|
|
|
|
|
|
nextLine++; |
|
|
|
for (;;) { |
|
|
|
// if this line is indented more than with N spaces,
|
|
|
|
// it's the new paragraph of the same list item
|
|
|
|
if (state.tShift[nextLine] >= indent) { |
|
|
|
lastNonEmptyLine = nextLine; |
|
|
|
continue; |
|
|
|
if (nextLine < lineMax) { |
|
|
|
if (isEmpty(state, nextLine)) { |
|
|
|
nextLine++; |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (state.tShift[nextLine] >= indent) { |
|
|
|
if (nextLine < endLine) { lastLine = nextLine; } |
|
|
|
nextLine++; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// paragraph after linebreak - not a continuation
|
|
|
|
if (lastNonEmptyLine < nextLine - 1) { break; } |
|
|
|
|
|
|
|
//
|
|
|
|
// if we are here, then next line is not empty and not last.
|
|
|
|
//
|
|
|
|
if (lastLine < 0) { break; } |
|
|
|
|
|
|
|
// Check that list is not terminated with another block type
|
|
|
|
if (rules_named.fences(state, nextLine, endLine, true)) { break; } |
|
|
|
if (rules_named.blockquote(state, nextLine, endLine, true)) { break; } |
|
|
|
if (rules_named.hr(state, nextLine, endLine, true)) { break; } |
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// In other block types this check (block ot the same type) is skipped.
|
|
|
|
|
|
|
|
// check if next item of the same type exists,
|
|
|
|
// and remember the new position after marker
|
|
|
|
if (isOrdered) { |
|
|
|
posNext = skipOrderedListMarker(state, nextLine); |
|
|
|
} else { |
|
|
|
posNext = skipBulletListMarker(state, nextLine); |
|
|
|
} |
|
|
|
if (posNext >= 0) { |
|
|
|
hasNextItem = true; |
|
|
|
break; |
|
|
|
subString = state.src.slice(contentStart, state.eMarks[lastLine]) |
|
|
|
.replace(RegExp('^ {' + indent + '}', 'mg'), ''); |
|
|
|
if (lastLine < lineMax) { |
|
|
|
// TODO: we should slice up to next empty line, not up to the end of the document
|
|
|
|
// (or even better - up to the next valid token)
|
|
|
|
//
|
|
|
|
// This has no impact on the algorithm except for performance
|
|
|
|
subString += state.src.slice(state.eMarks[lastLine]); |
|
|
|
} |
|
|
|
// Another type of list item - need to terminate this list.
|
|
|
|
if (rules_named.list(state, nextLine, endLine, true)) { break; } |
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
subState = state.clone(subString); |
|
|
|
state.lexerBlock.tokenize(subState, 0, lastLine - startLine + 1, true); |
|
|
|
nextLine = startLine = subState.line + startLine; |
|
|
|
lastLine = -1; |
|
|
|
contentStart = state.eMarks[startLine]; |
|
|
|
|
|
|
|
// TODO: need to detect loose type.
|
|
|
|
// Problem: blocks. separated by empty lines can be member of sublists.
|
|
|
|
|
|
|
|
if (rules_named.heading(state, nextLine, endLine, true)) { break; } |
|
|
|
if (rules_named.lheading(state, nextLine, endLine, true)) { break; } |
|
|
|
if (rules_named.table(state, nextLine, endLine, true)) { break; } |
|
|
|
//if (rules_named.tag(state, nextLine, endLine, true)) { break; }
|
|
|
|
//if (rules_named.def(state, nextLine, endLine, true)) { break; }
|
|
|
|
|
|
|
|
lastNonEmptyLine = nextLine; |
|
|
|
} |
|
|
|
|
|
|
|
// Run sublexer & write tokens
|
|
|
|
state.tokens.push({ type: 'list_item_open' }); |
|
|
|
|
|
|
|
// TODO: need to detect loose type.
|
|
|
|
// Problem: blocks. separated by empty lines can be member of sublists.
|
|
|
|
// If any of list item is loose, mark list as loose
|
|
|
|
if (!subState.tight) { |
|
|
|
state.tokens[listTokIdx].tight = false; |
|
|
|
} |
|
|
|
|
|
|
|
subState = state.clone(state.src.slice( |
|
|
|
contentStart, |
|
|
|
state.eMarks[lastNonEmptyLine]) |
|
|
|
.replace(RegExp('^ {1,' + indent + '}', 'mg'), '')); |
|
|
|
state.lexerBlock.tokenize(subState, 0, subState.lineMax); |
|
|
|
if (nextLine >= endLine) { break; } |
|
|
|
|
|
|
|
// If any of list item is loose, mark list as loose
|
|
|
|
if (!subState.tight) { |
|
|
|
state.tokens[listTokIdx].tight = false; |
|
|
|
if (isEmpty(state, nextLine)) { |
|
|
|
nextLine++; |
|
|
|
if (nextLine >= endLine || isEmpty(state, nextLine)) { |
|
|
|
// two newlines end the list
|
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
state.tokens.push({ type: 'list_item_close' }); |
|
|
|
|
|
|
|
if (!hasNextItem) { break; } |
|
|
|
|
|
|
|
posAfterMarker = posNext; |
|
|
|
line = nextLine; |
|
|
|
nextLine++; |
|
|
|
} |
|
|
|
|
|
|
|
// Finilize list
|
|
|
|