markdown-it
Advanced tools
Comparing version
@@ -0,1 +1,14 @@ | ||
8.0.0 / 2016-09-16 | ||
------------------ | ||
- Updated CM spec compatibility to 0.26: | ||
- Two consecutive newlines no longer terminate a list. | ||
- Ordered list terminating a paragraph can now only start with 1. | ||
- Adjust emphasis algorithm (`*foo**bar**baz*` is now parsed as `<strong>` | ||
inside `<em>`). | ||
- Fix tab width calculation inside lists and blockquotes. | ||
- Benchmarks src cleanup. | ||
- Remove testing in old nodes (but still use es5). | ||
7.0.1 / 2016-08-16 | ||
@@ -2,0 +15,0 @@ ------------------ |
@@ -97,5 +97,2 @@ /** internal | ||
line++; | ||
// two empty lines should stop the parser in list mode | ||
if (line < endLine && state.parentType === 'list' && state.isEmpty(line)) { break; } | ||
state.line = line; | ||
@@ -102,0 +99,0 @@ } |
@@ -9,5 +9,21 @@ // Block quotes | ||
module.exports = function blockquote(state, startLine, endLine, silent) { | ||
var nextLine, lastLineEmpty, oldTShift, oldSCount, oldBMarks, oldIndent, oldParentType, lines, initial, offset, ch, | ||
terminatorRules, token, | ||
i, l, terminate, | ||
var adjustTab, | ||
ch, | ||
i, | ||
initial, | ||
l, | ||
lastLineEmpty, | ||
lines, | ||
nextLine, | ||
offset, | ||
oldBMarks, | ||
oldBSCount, | ||
oldIndent, | ||
oldParentType, | ||
oldSCount, | ||
oldTShift, | ||
spaceAfterMarker, | ||
terminate, | ||
terminatorRules, | ||
token, | ||
pos = state.bMarks[startLine] + state.tShift[startLine], | ||
@@ -23,5 +39,2 @@ max = state.eMarks[startLine]; | ||
// skip one optional space (but not tab, check cmark impl) after '>' | ||
if (state.src.charCodeAt(pos) === 0x20) { pos++; } | ||
oldIndent = state.blkIndent; | ||
@@ -33,2 +46,31 @@ state.blkIndent = 0; | ||
// skip one optional space after '>' | ||
if (state.src.charCodeAt(pos) === 0x20 /* space */) { | ||
// ' > test ' | ||
// ^ -- position start of line here: | ||
pos++; | ||
initial++; | ||
offset++; | ||
adjustTab = false; | ||
spaceAfterMarker = true; | ||
} else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { | ||
spaceAfterMarker = true; | ||
if ((state.bsCount[startLine] + offset) % 4 === 3) { | ||
// ' >\t test ' | ||
// ^ -- position start of line here (tab has width===1) | ||
pos++; | ||
initial++; | ||
offset++; | ||
adjustTab = false; | ||
} else { | ||
// ' >\t test ' | ||
// ^ -- position start of line here + shift bsCount slightly | ||
// to make extra space appear | ||
adjustTab = true; | ||
} | ||
} else { | ||
spaceAfterMarker = false; | ||
} | ||
oldBMarks = [ state.bMarks[startLine] ]; | ||
@@ -42,3 +84,3 @@ state.bMarks[startLine] = pos; | ||
if (ch === 0x09) { | ||
offset += 4 - offset % 4; | ||
offset += 4 - (offset + state.bsCount[startLine] + (adjustTab ? 1 : 0)) % 4; | ||
} else { | ||
@@ -54,2 +96,5 @@ offset++; | ||
oldBSCount = [ state.bsCount[startLine] ]; | ||
state.bsCount[startLine] = state.sCount[startLine] + 1 + (spaceAfterMarker ? 1 : 0); | ||
lastLineEmpty = pos >= max; | ||
@@ -65,2 +110,5 @@ | ||
oldParentType = state.parentType; | ||
state.parentType = 'blockquote'; | ||
// Search the end of the block | ||
@@ -98,8 +146,34 @@ // | ||
// skip one optional space (but not tab, check cmark impl) after '>' | ||
if (state.src.charCodeAt(pos) === 0x20) { pos++; } | ||
// skip spaces after ">" and re-calculate offset | ||
initial = offset = state.sCount[nextLine] + pos - (state.bMarks[nextLine] + state.tShift[nextLine]); | ||
// skip one optional space after '>' | ||
if (state.src.charCodeAt(pos) === 0x20 /* space */) { | ||
// ' > test ' | ||
// ^ -- position start of line here: | ||
pos++; | ||
initial++; | ||
offset++; | ||
adjustTab = false; | ||
spaceAfterMarker = true; | ||
} else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { | ||
spaceAfterMarker = true; | ||
if ((state.bsCount[nextLine] + offset) % 4 === 3) { | ||
// ' >\t test ' | ||
// ^ -- position start of line here (tab has width===1) | ||
pos++; | ||
initial++; | ||
offset++; | ||
adjustTab = false; | ||
} else { | ||
// ' >\t test ' | ||
// ^ -- position start of line here + shift bsCount slightly | ||
// to make extra space appear | ||
adjustTab = true; | ||
} | ||
} else { | ||
spaceAfterMarker = false; | ||
} | ||
oldBMarks.push(state.bMarks[nextLine]); | ||
@@ -113,3 +187,3 @@ state.bMarks[nextLine] = pos; | ||
if (ch === 0x09) { | ||
offset += 4 - offset % 4; | ||
offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4; | ||
} else { | ||
@@ -127,2 +201,5 @@ offset++; | ||
oldBSCount.push(state.bsCount[nextLine]); | ||
state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0); | ||
oldSCount.push(state.sCount[nextLine]); | ||
@@ -150,2 +227,3 @@ state.sCount[nextLine] = offset - initial; | ||
oldBMarks.push(state.bMarks[nextLine]); | ||
oldBSCount.push(state.bsCount[nextLine]); | ||
oldTShift.push(state.tShift[nextLine]); | ||
@@ -159,5 +237,2 @@ oldSCount.push(state.sCount[nextLine]); | ||
oldParentType = state.parentType; | ||
state.parentType = 'blockquote'; | ||
token = state.push('blockquote_open', 'blockquote', 1); | ||
@@ -181,2 +256,3 @@ token.markup = '>'; | ||
state.sCount[i + startLine] = oldSCount[i]; | ||
state.bsCount[i + startLine] = oldBSCount[i]; | ||
} | ||
@@ -183,0 +259,0 @@ state.blkIndent = oldIndent; |
@@ -7,3 +7,3 @@ // Code block (4 spaces padded) | ||
module.exports = function code(state, startLine, endLine/*, silent*/) { | ||
var nextLine, last, token, emptyLines = 0; | ||
var nextLine, last, token; | ||
@@ -16,10 +16,2 @@ if (state.sCount[startLine] - state.blkIndent < 4) { return false; } | ||
if (state.isEmpty(nextLine)) { | ||
emptyLines++; | ||
// workaround for lists: 2 blank lines should terminate indented | ||
// code block, but not fenced code block | ||
if (emptyLines >= 2 && state.parentType === 'list') { | ||
break; | ||
} | ||
nextLine++; | ||
@@ -29,4 +21,2 @@ continue; | ||
emptyLines = 0; | ||
if (state.sCount[nextLine] - state.blkIndent >= 4) { | ||
@@ -33,0 +23,0 @@ nextLine++; |
@@ -25,3 +25,3 @@ // heading (#, ##, ...) | ||
if (level > 6 || (pos < max && ch !== 0x20/* space */)) { return false; } | ||
if (level > 6 || (pos < max && !isSpace(ch))) { return false; } | ||
@@ -28,0 +28,0 @@ if (silent) { return true; } |
@@ -8,5 +8,8 @@ // lheading (---, ===) | ||
var content, terminate, i, l, token, pos, max, level, marker, | ||
nextLine = startLine + 1, | ||
nextLine = startLine + 1, oldParentType, | ||
terminatorRules = state.md.block.ruler.getRules('paragraph'); | ||
oldParentType = state.parentType; | ||
state.parentType = 'paragraph'; // use paragraph to match terminatorRules | ||
// jump line-by-line until empty one or EOF | ||
@@ -75,3 +78,5 @@ for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { | ||
state.parentType = oldParentType; | ||
return true; | ||
}; |
@@ -101,35 +101,59 @@ // Lists | ||
module.exports = function list(state, startLine, endLine, silent) { | ||
var nextLine, | ||
var ch, | ||
contentStart, | ||
i, | ||
indent, | ||
indentAfterMarker, | ||
initial, | ||
isOrdered, | ||
itemLines, | ||
l, | ||
listLines, | ||
listTokIdx, | ||
markerCharCode, | ||
markerValue, | ||
max, | ||
nextLine, | ||
offset, | ||
indent, | ||
oldTShift, | ||
oldIndent, | ||
oldLIndent, | ||
oldParentType, | ||
oldTShift, | ||
oldTight, | ||
oldParentType, | ||
start, | ||
pos, | ||
posAfterMarker, | ||
ch, | ||
pos, | ||
max, | ||
indentAfterMarker, | ||
markerValue, | ||
markerCharCode, | ||
isOrdered, | ||
contentStart, | ||
listTokIdx, | ||
prevEmptyEnd, | ||
listLines, | ||
itemLines, | ||
tight = true, | ||
start, | ||
terminate, | ||
terminatorRules, | ||
token, | ||
i, l, terminate; | ||
isTerminatingParagraph = false, | ||
tight = true; | ||
// limit conditions when list can interrupt | ||
// a paragraph (validation mode only) | ||
if (silent && state.parentType === 'paragraph') { | ||
// Next list item should still terminate previous list item; | ||
// | ||
// This code can fail if plugins use blkIndent as well as lists, | ||
// but I hope the spec gets fixed long before that happens. | ||
// | ||
if (state.tShift[startLine] >= state.blkIndent) { | ||
isTerminatingParagraph = true; | ||
} | ||
} | ||
// Detect list type and position after marker | ||
if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) { | ||
isOrdered = true; | ||
start = state.bMarks[startLine] + state.tShift[startLine]; | ||
markerValue = Number(state.src.substr(start, posAfterMarker - start - 1)); | ||
// If we're starting a new ordered list right after | ||
// a paragraph, it should start with 1. | ||
if (isTerminatingParagraph && markerValue !== 1) return false; | ||
} else if ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0) { | ||
isOrdered = false; | ||
} else { | ||
@@ -139,2 +163,8 @@ return false; | ||
// If we're starting a new unordered list right after | ||
// a paragraph, first line should not be empty. | ||
if (isTerminatingParagraph) { | ||
if (state.skipSpaces(posAfterMarker) >= state.eMarks[startLine]) return false; | ||
} | ||
// We should terminate list on style change. Remember first one to compare. | ||
@@ -150,5 +180,2 @@ markerCharCode = state.src.charCodeAt(posAfterMarker - 1); | ||
if (isOrdered) { | ||
start = state.bMarks[startLine] + state.tShift[startLine]; | ||
markerValue = Number(state.src.substr(start, posAfterMarker - start - 1)); | ||
token = state.push('ordered_list_open', 'ol', 1); | ||
@@ -174,2 +201,5 @@ if (markerValue !== 1) { | ||
oldParentType = state.parentType; | ||
state.parentType = 'list'; | ||
while (nextLine < endLine) { | ||
@@ -186,3 +216,3 @@ pos = posAfterMarker; | ||
if (ch === 0x09) { | ||
offset += 4 - offset % 4; | ||
offset += 4 - (offset + state.bsCount[nextLine]) % 4; | ||
} else { | ||
@@ -224,6 +254,4 @@ offset++; | ||
oldLIndent = state.sCount[startLine]; | ||
oldParentType = state.parentType; | ||
state.blkIndent = indent; | ||
state.tight = true; | ||
state.parentType = 'list'; | ||
state.tShift[startLine] = contentStart - state.bMarks[startLine]; | ||
@@ -257,3 +285,2 @@ state.sCount[startLine] = offset; | ||
state.tight = oldTight; | ||
state.parentType = oldParentType; | ||
@@ -269,6 +296,2 @@ token = state.push('list_item_close', 'li', -1); | ||
if (state.isEmpty(nextLine)) { | ||
break; | ||
} | ||
// | ||
@@ -312,2 +335,4 @@ // Try to check if list is terminated or continued. | ||
state.parentType = oldParentType; | ||
// mark paragraphs tight if needed | ||
@@ -314,0 +339,0 @@ if (tight) { |
@@ -7,3 +7,3 @@ // Paragraph | ||
module.exports = function paragraph(state, startLine/*, endLine*/) { | ||
var content, terminate, i, l, token, | ||
var content, terminate, i, l, token, oldParentType, | ||
nextLine = startLine + 1, | ||
@@ -13,2 +13,5 @@ terminatorRules = state.md.block.ruler.getRules('paragraph'), | ||
oldParentType = state.parentType; | ||
state.parentType = 'paragraph'; | ||
// jump line-by-line until empty one or EOF | ||
@@ -48,3 +51,5 @@ for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { | ||
state.parentType = oldParentType; | ||
return true; | ||
}; |
@@ -20,2 +20,3 @@ 'use strict'; | ||
labelEnd, | ||
oldParentType, | ||
res, | ||
@@ -50,2 +51,5 @@ start, | ||
oldParentType = state.parentType; | ||
state.parentType = 'reference'; | ||
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { | ||
@@ -191,4 +195,6 @@ // this would be a code block normally, but after paragraph | ||
state.parentType = oldParentType; | ||
state.line = startLine + lines + 1; | ||
return true; | ||
}; |
@@ -30,2 +30,14 @@ // Parser state class | ||
// An amount of virtual spaces (tabs expanded) between beginning | ||
// of each line (bMarks) and real beginning of that line. | ||
// | ||
// It exists only as a hack because blockquotes override bMarks | ||
// losing information in the process. | ||
// | ||
// It's used only when expanding tabs, you can think about it as | ||
// an initial tab length, e.g. bsCount=21 applied to string `\t123` | ||
// means first tab should be expanded to 4-21%4 === 3 spaces. | ||
// | ||
this.bsCount = []; | ||
// block parser variables | ||
@@ -37,5 +49,8 @@ this.blkIndent = 0; // required block content indent | ||
this.tight = false; // loose/tight mode for lists | ||
this.parentType = 'root'; // if `list`, block parser stops on two newlines | ||
this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any) | ||
// can be 'blockquote', 'list', 'root', 'paragraph' or 'reference' | ||
// used in lists to determine if they interrupt a paragraph | ||
this.parentType = 'root'; | ||
this.level = 0; | ||
@@ -75,2 +90,3 @@ | ||
this.sCount.push(offset); | ||
this.bsCount.push(0); | ||
@@ -89,2 +105,3 @@ indent_found = false; | ||
this.sCount.push(0); | ||
this.bsCount.push(0); | ||
@@ -187,3 +204,3 @@ this.lineMax = this.bMarks.length - 1; // don't count last fake line | ||
if (ch === 0x09) { | ||
lineIndent += 4 - lineIndent % 4; | ||
lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4; | ||
} else { | ||
@@ -202,3 +219,9 @@ lineIndent++; | ||
queue[i] = this.src.slice(first, last); | ||
if (lineIndent > indent) { | ||
// partially expanding tabs in code blocks, e.g '\t\tfoobar' | ||
// with indent=2 becomes ' \tfoobar' | ||
queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last); | ||
} else { | ||
queue[i] = this.src.slice(first, last); | ||
} | ||
} | ||
@@ -205,0 +228,0 @@ |
@@ -26,7 +26,15 @@ // For each opening emphasis-like marker find a matching closing one | ||
lastDelim.jump = i - j; | ||
lastDelim.open = false; | ||
currDelim.end = i; | ||
currDelim.jump = 0; | ||
break; | ||
// typeofs are for backward compatibility with plugins | ||
var odd_match = (currDelim.close || lastDelim.open) && | ||
typeof currDelim.length !== 'undefined' && | ||
typeof lastDelim.length !== 'undefined' && | ||
(currDelim.length + lastDelim.length) % 3 === 0; | ||
if (!odd_match) { | ||
lastDelim.jump = i - j; | ||
lastDelim.open = false; | ||
currDelim.end = i; | ||
currDelim.jump = 0; | ||
break; | ||
} | ||
} | ||
@@ -33,0 +41,0 @@ |
@@ -28,2 +28,6 @@ // Process *this* and _that_ | ||
// Total length of these series of delimiters. | ||
// | ||
length: scanned.length, | ||
// An amount of characters before this one that's equivalent to | ||
@@ -30,0 +34,0 @@ // current one. In plain English: if this delimiter does not open |
{ | ||
"name": "markdown-it", | ||
"version": "7.0.1", | ||
"version": "8.0.0", | ||
"description": "Markdown-it - modern pluggable markdown parser.", | ||
@@ -32,18 +32,16 @@ "keywords": [ | ||
"mdurl": "^1.0.1", | ||
"uc.micro": "^1.0.1" | ||
"uc.micro": "^1.0.3" | ||
}, | ||
"devDependencies": { | ||
"ansi": "~0.3.0", | ||
"autolinker": "^0.18.1", | ||
"autoprefixer-stylus": "~0.9.2", | ||
"autoprefixer-stylus": "~0.10.0", | ||
"benchmark": "~2.1.0", | ||
"browserify": "*", | ||
"chai": "^3.4.1", | ||
"commonmark": "~0.25.0", | ||
"coveralls": "~2.11.9", | ||
"eslint": "~2.13.0", | ||
"eslint": "^3.5.0", | ||
"highlight.js": "^9.2.0", | ||
"istanbul": "*", | ||
"istanbul": "^0.4.5", | ||
"jade": "~1.11.0", | ||
"markdown-it-abbr": "^1.0.2", | ||
"markdown-it-abbr": "^1.0.4", | ||
"markdown-it-container": "^2.0.0", | ||
@@ -59,8 +57,7 @@ "markdown-it-deflist": "^2.0.0", | ||
"markdown-it-testgen": "~0.1.3", | ||
"marked": "0.3.5", | ||
"mocha": "*", | ||
"ndoc": "^4.0.0", | ||
"ndoc": "^5.0.0", | ||
"stylus": "~0.54.2", | ||
"uglify-js": "*" | ||
"uglify-js": "^2.7.3" | ||
} | ||
} |
@@ -244,3 +244,5 @@ # markdown-it | ||
```bash | ||
$ benchmark/benchmark.js readme | ||
make benchmark-deps | ||
benchmark/benchmark.js readme | ||
Selected samples: (1 of 28) | ||
@@ -253,3 +255,3 @@ > README | ||
> current-commonmark x 1,568 ops/sec ±0.84% (98 runs sampled) | ||
> marked-0.3.2 x 1,587 ops/sec ±4.31% (93 runs sampled) | ||
> marked x 1,587 ops/sec ±4.31% (93 runs sampled) | ||
``` | ||
@@ -256,0 +258,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
537406
1.88%25
-10.71%11692
2.21%295
0.68%Updated