diff --git a/lib/lexer_block.js b/lib/lexer_block.js index 8f6dd0e..988dd07 100644 --- a/lib/lexer_block.js +++ b/lib/lexer_block.js @@ -71,7 +71,7 @@ var rules = []; // code -rules.push(function code(state, startLine, endLine) { +rules.push(function code(state, startLine, endLine, silent) { var nextLine, last; if (getIndent(state, startLine, 4) === -1) { return false; } @@ -94,6 +94,8 @@ rules.push(function code(state, startLine, endLine) { break; } + if (silent) { return true; } + state.tokens.push({ type: 'code', startLine: startLine, @@ -105,8 +107,79 @@ rules.push(function code(state, startLine, endLine) { }); +// heading +rules.push(function heading(state, startLine, endLine, silent) { + var ch, level, + pos = state.bMarks[startLine], + max = state.eMarks[startLine]; + + ch = state.src.charCodeAt(pos); + + // skip leading spaces + while (isWhiteSpace(ch) && pos < max) { + ch = state.src.charCodeAt(++pos); + } + + if (ch !== 0x23/* # */ || pos >= max) { return false; } + + // count heading level + level = 1; + ch = state.src.charCodeAt(++pos); + while (ch === 0x23/* # */ && pos < max && level <= 6) { + level++; + ch = state.src.charCodeAt(++pos); + } + + if (!isWhiteSpace(ch) || pos >= max || level > 6) { return false; } + + // skip spaces before heading text + ch = state.src.charCodeAt(++pos); + while (isWhiteSpace(ch) && pos < max) { + ch = state.src.charCodeAt(++pos); + } + + if (pos >= max) { return false; } + + // Now pos contains offset of first heared char + // Let's cut tails like ' ### ' from the end of string + + max--; + ch = state.src.charCodeAt(max); + + while (isWhiteSpace(ch) && max > pos) { + ch = state.src.charCodeAt(--max); + } + if (ch === 0x23/* # */) { + while (ch === 0x23/* # */ && max > pos) { + ch = state.src.charCodeAt(--max); + } + if (isWhiteSpace(ch)) { + while (isWhiteSpace(ch) && max > pos) { + ch = state.src.charCodeAt(--max); + } + } else if (ch === 0x5C/* \ */) { + max++; + } + } + max++; + + if (silent) { return true; } + + if (silent) { + return true; + } + + state.tokens.push({ type: 'heading_open', level: level }); + state.lexerInline.tokenize(state, pos, max); + state.tokens.push({ type: 'heading_close', level: level }); + + skipEmptyLines(state, ++startLine); + return true; +}); + + // Horizontal rule -rules.push(function hr(state, startLine, endLine) { +rules.push(function hr(state, startLine, endLine, silent) { var ch, marker, pos = state.bMarks[startLine], space_max = pos + 3, @@ -162,6 +235,8 @@ rules.push(function hr(state, startLine, endLine) { } } + if (silent) { return true; } + state.tokens.push({ type: 'hr' }); skipEmptyLines(state, ++startLine); @@ -171,10 +246,18 @@ rules.push(function hr(state, startLine, endLine) { // Paragraph rules.push(function paragraph(state, startLine, endLine) { - var nextLine = startLine + 1; + var nextLine = startLine + 1, + rules_named = state.lexerBlock.rules_named; // jump line-by-line until empty one or EOF while (nextLine < endLine && !isEmpty(state, nextLine)) { + // Force paragraph termination of next tag found + 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.tag(state, nextLine, endLine, true)) { break; } + //if (rules_named.def(state, nextLine, endLine, true)) { break; } nextLine++; } @@ -194,10 +277,16 @@ rules.push(function paragraph(state, startLine, endLine) { //////////////////////////////////////////////////////////////////////////////// // Lexer class +function functionName(fn) { + var ret = fn.toString(); + ret = ret.substr('function '.length); + ret = ret.substr(0, ret.indexOf('(')); + return ret; +} function findByName(self, name) { for (var i = 0; i < self.rules.length; i++) { - if (self.rules[i].name === name) { + if (functionName(self.rules[i]) === name) { return i; } } @@ -209,6 +298,7 @@ function findByName(self, name) { // function LexerBlock() { this.rules = []; + this.rules_named = {}; for (var i = 0; i < rules.length; i++) { this.after(null, rules[i]); @@ -229,6 +319,8 @@ LexerBlock.prototype.at = function (name, fn) { } else { this.rules = this.rules.slice(0, index).concat(this.rules.slice(index + 1)); } + + this.rules_named[functionName(fn)] = fn; }; @@ -238,6 +330,7 @@ LexerBlock.prototype.at = function (name, fn) { LexerBlock.prototype.before = function (name, fn) { if (!name) { this.rules.unshift(fn); + this.rules_named[functionName(fn)] = fn; return; } @@ -247,6 +340,7 @@ LexerBlock.prototype.before = function (name, fn) { } this.rules.splice(index, 0, fn); + this.rules_named[functionName(fn)] = fn; }; @@ -256,6 +350,7 @@ LexerBlock.prototype.before = function (name, fn) { LexerBlock.prototype.after = function (name, fn) { if (!name) { this.rules.push(fn); + this.rules_named[functionName(fn)] = fn; return; } @@ -265,6 +360,7 @@ LexerBlock.prototype.after = function (name, fn) { } this.rules.splice(index + 1, 0, fn); + this.rules_named[functionName(fn)] = fn; }; @@ -286,7 +382,7 @@ LexerBlock.prototype.tokenize = function (state, startLine, endLine) { // - return true for (i = 0; i < len; i++) { - ok = rules[i](state, line, endLine); + ok = rules[i](state, line, endLine, false); if (ok) { break; } } diff --git a/lib/lexer_inline.js b/lib/lexer_inline.js index f2f2af1..72e4ce5 100644 --- a/lib/lexer_inline.js +++ b/lib/lexer_inline.js @@ -26,9 +26,16 @@ rules.push(function text(state, begin, end) { // Lexer class +function functionName(fn) { + var ret = fn.toString(); + ret = ret.substr('function '.length); + ret = ret.substr(0, ret.indexOf('(')); + return ret; +} + function findByName(self, name) { for (var i = 0; i < self.rules.length; i++) { - if (self.rules[i].name === name) { + if (functionName(self.rules[i]) === name) { return i; } } diff --git a/lib/renderer.js b/lib/renderer.js index 3a335b1..16d1678 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -22,6 +22,15 @@ rules.code = function (state, token) { state.result += '
' + escapeHTML(lines) + '
\n'; }; + +rules.heading_open = function (state, token) { + state.result += ''; +}; +rules.heading_close = function (state, token) { + state.result += '\n'; +}; + + rules.hr = function (state, token) { state.result += '
\n'; };