Browse Source

list review & reorganize

pull/14/head
Vitaly Puzrin 10 years ago
parent
commit
65f096804a
  1. 2
      .eslintrc
  2. 3
      lib/lexer_block/blockquote.js
  3. 299
      lib/lexer_block/list.js
  4. 2
      lib/lexer_block/paragraph.js
  5. 18
      lib/renderer.js

2
.eslintrc

@ -17,7 +17,7 @@ rules:
eqeqeq: 2
guard-for-in: 2
handle-callback-err: 2
max-depth: [ 1, 3 ]
max-depth: [ 1, 4 ]
max-nested-callbacks: [ 1, 4 ]
# string can exceed 80 chars, but should not overflow github website :)
max-len: [ 2, 120, 1000 ]

3
lib/lexer_block/blockquote.js

@ -73,6 +73,9 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
if (rules_named.list(state, nextLine, endLine, true)) { break; }
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; }
}
state.tokens.push({ type: 'blockquote_open' });

299
lib/lexer_block/list.js

@ -4,57 +4,22 @@
var isEmpty = require('../helpers').isEmpty;
var getLines = require('../helpers').getLines;
var skipSpaces = require('../helpers').skipSpaces;
var skipEmptyLines = require('../helpers').skipEmptyLines;
function findEndOfItem(state, startLine, endLine, indent) {
var lastNonEmptyLine = startLine,
rules_named = state.lexerBlock.rules_named,
nextLine = startLine + 1;
// Search `[-+*][\n ]`, returns next pos arter marker on success
// or -1 on fail.
function skipBulletListMarker(state, startLine) {
var marker, pos, max;
// jump line-by-line until empty one or EOF
for (; nextLine < endLine; nextLine++) {
if (isEmpty(state, nextLine)) {
// two successive newlines end the list
if (lastNonEmptyLine < nextLine - 1) { break; }
continue;
}
// 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;
}
// paragraph after linebreak - not a continuation
if (lastNonEmptyLine < nextLine - 1) { break; }
// Otherwise it's a possible continuation, so we need to check
// other tags, same as with blockquote and paragraph.
if (rules_named.fences(state, nextLine, endLine, true)) { break; }
if (rules_named.hr(state, nextLine, endLine, true)) { break; }
if (rules_named.heading(state, nextLine, endLine, true)) { break; }
if (rules_named.lheading(state, nextLine, endLine, true)) { break; }
if (rules_named.blockquote(state, nextLine, endLine, true)) { break; }
if (rules_named.list(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;
}
return lastNonEmptyLine;
}
if (state.tShift[startLine] > 3) { return -1; }
// skips `[-+*][\n ]`, returns -1 if not found
function skipBulletListMarker(state, startLine/*, endLine*/) {
var marker,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
pos = state.bMarks[startLine] + state.tShift[startLine];
max = state.eMarks[startLine];
if (pos > max) { return -1; }
if (pos >= max) { return -1; }
marker = state.src.charCodeAt(pos++);
// Check bullet
@ -68,138 +33,216 @@ function skipBulletListMarker(state, startLine/*, endLine*/) {
// " 1.test " - is not a list item
return -1;
}
return pos;
}
// skips `\d+\.[\n ]`, returns -1 if not found
function skipOrderedListMarker(state, startLine/*, endLine*/) {
var marker,
haveMarker = false,
// Search `\d+\.[\n ]`, returns next pos arter marker on success
// or -1 on fail.
function skipOrderedListMarker(state, startLine) {
var ch,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
if (pos + 1 > max) { return -1; }
if (pos + 1 >= max) { return -1; }
marker = state.src.charCodeAt(pos++);
if (marker < 0x30/* 0 */ || marker > 0x39/* 9 */) { return -1; }
ch = state.src.charCodeAt(pos++);
// First char should be non zero digit
if (ch < 0x31/* 1 */ || ch > 0x39/* 9 */) { return -1; }
while (pos < max) {
marker = state.src.charCodeAt(pos++);
for (;;) {
// EOL -> fail
if (pos >= max) { return -1; }
ch = state.src.charCodeAt(pos++);
if (ch >= 0x30/* 0 */ && ch <= 0x39/* 9 */) {
continue;
}
// found valid marker
if (marker === 0x29 || marker === 0x2e) {
haveMarker = true;
if (ch === 0x29/* ) */ || ch === 0x2e/* . */) {
break;
}
// still skipping digits...
if (marker < 0x30/* 0 */ || marker > 0x39/* 9 */) { return -1; }
}
if (!haveMarker) {
// " 1\n"
return -1;
}
if (pos < max && state.src.charCodeAt(pos) !== 0x20) {
if (pos < max && state.src.charCodeAt(pos) !== 0x20/* space */) {
// " 1.test " - is not a list item
return -1;
}
return pos;
}
function bullet_item(state, startLine, endLine, isOrdered) {
var indentAfterMarker, indent, start, lastLine, subState, pos,
max = state.eMarks[startLine];
if (isOrdered) {
pos = skipOrderedListMarker(state, startLine, endLine);
module.exports = function list(state, startLine, endLine, silent) {
var line,
nextLine,
indent,
start,
posAfterMarker,
max,
indentAfterMarker,
markerValue,
isOrdered,
lastNonEmptyLine,
hasNextItem,
subState,
posNext,
contentStart,
rules_named = state.lexerBlock.rules_named;
// Detect list type and position after marker
if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) {
isOrdered = true;
} else if ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0) {
isOrdered = false;
} else {
pos = skipBulletListMarker(state, startLine, endLine);
return false;
}
if (pos === -1) { return false; }
// For validation mode we can terminate immediately
if (silent) { return true; }
// Start list
if (isOrdered) {
start = state.bMarks[startLine] + state.tShift[startLine];
markerValue = Number(state.src.substr(start, posAfterMarker - start - 1));
start = pos;
pos = skipSpaces(state, pos);
state.tokens.push({
type: 'ordered_list_open',
order: markerValue
});
if (pos >= max) {
// trimming space in "- \n 3" case, indent is 1 here
indentAfterMarker = 1;
} else {
indentAfterMarker = pos - start;
state.tokens.push({ type: 'bullet_list_open' });
}
// If we have more than 4 spaces, the indent is 1
// (the rest is just indented code block)
if (indentAfterMarker > 4) { indentAfterMarker = 1; }
//
// Iterate list items
//
// If indent is less than 1, assume that it's one, example:
// "-\n test"
if (indentAfterMarker < 1) { indentAfterMarker = 1; }
line = startLine;
nextLine = line + 1;
// " - test"
// ^^^^^ - calculating total length of this thing
indent = state.tShift[startLine] + indentAfterMarker + (isOrdered ? 2 : 1);
while (line < endLine) {
lastLine = findEndOfItem(state, startLine, endLine, indent);
contentStart = skipSpaces(state, posAfterMarker);
max = state.eMarks[line];
state.tokens.push({ type: 'list_item_open' });
/*state.lexerInline.tokenize(
state,
state.bMarks[startLine],
state.eMarks[lastLine]
);*/
subState = state.clone(getLines(state, startLine, lastLine + 1, true)
.replace(RegExp('^ {' + indent + '}', 'mg'), '').substr(indent));
state.lexerBlock.tokenize(subState, 0, subState.lineMax);
if (contentStart >= max) {
// trimming space in "- \n 3" case, indent is 1 here
indentAfterMarker = 1;
} else {
indentAfterMarker = contentStart - posAfterMarker;
}
state.tokens.push({ type: 'list_item_close' });
// If we have more than 4 spaces, the indent is 1
// (the rest is just indented code block)
if (indentAfterMarker > 4) { indentAfterMarker = 1; }
// If indent is less than 1, assume that it's one, example:
// "-\n test"
if (indentAfterMarker < 1) { indentAfterMarker = 1; }
// " - test"
// ^^^^^ - calculating total length of this thing
indent = (posAfterMarker - state.tShift[line]) + 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
// two successive newlines end the list
if (lastNonEmptyLine < nextLine - 1) { break; }
continue;
}
// 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;
}
// paragraph after linebreak - not a continuation
if (lastNonEmptyLine < nextLine - 1) { break; }
state.line = lastLine + 1;
return true;
}
//
// if we are here, then next line is not empty and not last.
//
// Check that list is not terminated with another block type
if (rules_named.fences(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;
}
// Another type of list item - need to terminate this list.
if (rules_named.list(state, nextLine, endLine, true)) { break; }
//////////////////////////////////////////////////////////////////////////
if (rules_named.heading(state, nextLine, endLine, true)) { break; }
if (rules_named.lheading(state, nextLine, endLine, true)) { break; }
if (rules_named.blockquote(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; }
module.exports = function list(state, startLine, endLine, silent) {
var line, start, markerInt,
orderedMarker = skipOrderedListMarker(state, startLine, endLine),
bulletMarker = skipBulletListMarker(state, startLine, endLine),
isOrdered;
lastNonEmptyLine = nextLine;
}
if (orderedMarker === -1 && bulletMarker === -1) { return false; }
if (silent) { return true; }
// Run sublexer & write tokens
state.tokens.push({ type: 'list_item_open' });
isOrdered = orderedMarker !== -1;
if (isOrdered) {
start = state.bMarks[startLine] + state.tShift[startLine];
markerInt = Number(state.src.substr(start, orderedMarker - start));
if (markerInt > 1) {
state.tokens.push({ type: 'ordered_list_open', start: markerInt });
} else {
state.tokens.push({ type: 'ordered_list_open' });
}
} else {
state.tokens.push({ type: 'bullet_list_open' });
}
// TODO: need to detect loose type.
// Problem: blocks. separated by empty lines can be member of sublists.
line = startLine;
while (line < endLine) {
if (bullet_item(state, line, endLine, isOrdered)) {
line = state.line;
subState = state.clone(state.src.slice(
contentStart,
state.eMarks[lastNonEmptyLine])
.replace(RegExp('^ {1,' + indent + '}', 'mg'), ''));
state.lexerBlock.tokenize(subState, 0, subState.lineMax);
state.tokens.push({ type: 'list_item_close' });
// if we have a trailing newline, skip it;
// if we have more than one, it should end the list,
// so can't use skipEmptyNewlines here
if (line < endLine && isEmpty(state, line)) { line++; }
} else {
break;
}
if (!hasNextItem) { break; }
posAfterMarker = posNext;
line = nextLine;
nextLine++;
}
// Finilize list
if (isOrdered) {
state.tokens.push({ type: 'ordered_list_close' });
} else {
state.tokens.push({ type: 'bullet_list_close' });
}
state.line = skipEmptyLines(state, state.line);
state.line = skipEmptyLines(state, nextLine);
return true;
};

2
lib/lexer_block/paragraph.js

@ -14,13 +14,13 @@ module.exports = function paragraph(state, startLine, endLine) {
// jump line-by-line until empty one or EOF
while (nextLine < endLine && !isEmpty(state, nextLine)) {
// Some tags can terminate paragraph without empty line.
// Try those tags in validation more (without tokens generation)
if (rules_named.fences(state, nextLine, endLine, true)) { break; }
if (rules_named.hr(state, nextLine, endLine, true)) { break; }
if (rules_named.list(state, nextLine, endLine, true)) { break; }
if (rules_named.heading(state, nextLine, endLine, true)) { break; }
if (rules_named.lheading(state, nextLine, endLine, true)) { break; }
if (rules_named.blockquote(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; }
nextLine++;

18
lib/renderer.js

@ -29,14 +29,6 @@ rules.blockquote_close = function (state /*, token*/) {
};
rules.bullet_list_open = function (state /*, token*/) {
state.result += '<ul>\n';
};
rules.bullet_list_close = function (state /*, token*/) {
state.result += '</ul>\n';
};
rules.code = function (state, token) {
state.result += '<pre><code>' + escapeHtml(token.content) + '</code></pre>\n';
};
@ -67,17 +59,21 @@ rules.hr = function (state/*, token*/) {
};
rules.bullet_list_open = function (state /*, token*/) {
state.result += '<ul>\n';
};
rules.bullet_list_close = function (state /*, token*/) {
state.result += '</ul>\n';
};
rules.list_item_open = function (state /*, token*/) {
state.result += '<li>';
};
rules.list_item_close = function (state /*, token*/) {
state.result += '</li>\n';
};
rules.ordered_list_open = function (state, token) {
state.result += '<ol'
+ (token.start ? ' start="' + token.start + '"' : '')
+ (token.order > 1 ? ' start="' + token.order + '"' : '')
+ '>\n';
};
rules.ordered_list_close = function (state /*, token*/) {

Loading…
Cancel
Save