Browse Source

Add multichar replacements in smartquotes

fix #115
pull/124/head
Alex Kocharin 9 years ago
parent
commit
a7b2b3b4e8
  1. 5
      README.md
  2. 7
      lib/index.js
  3. 5
      lib/presets/commonmark.js
  4. 5
      lib/presets/default.js
  5. 5
      lib/presets/zero.js
  6. 2
      lib/rules_core/replacements.js
  7. 28
      lib/rules_core/smartquotes.js
  8. 33
      test/misc.js

5
README.md

@ -111,7 +111,10 @@ var md = require('markdown-it')({
typographer: false, typographer: false,
// Double + single quotes replacement pairs, when typographer enabled, // Double + single quotes replacement pairs, when typographer enabled,
// and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. // and smartquotes on. Could be either a String or an Array.
//
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
quotes: '“”‘’', quotes: '“”‘’',
// Highlighter function. Should return escaped HTML, // Highlighter function. Should return escaped HTML,

7
lib/index.js

@ -152,9 +152,10 @@ function normalizeLinkText(url) {
* - __typographer__ - `false`. Set `true` to enable [some language-neutral * - __typographer__ - `false`. Set `true` to enable [some language-neutral
* replacement](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.js) + * replacement](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.js) +
* quotes beautification (smartquotes). * quotes beautification (smartquotes).
* - __quotes__ - `“”‘’`, string. Double + single quotes replacement pairs, when * - __quotes__ - `“”‘’`, String or Array. Double + single quotes replacement
* typographer enabled and smartquotes on. Set doubles to '«»' for Russian, * pairs, when typographer enabled and smartquotes on. For example, you can
* '„“' for German. * use `'«»„“'` for Russian, `'„“‚‘'` for German, and
* `['«\xA0', '\xA0»', '‹\xA0', '\xA0›']` for French (including nbsp).
* - __highlight__ - `null`. Highlighter function for fenced code blocks. * - __highlight__ - `null`. Highlighter function for fenced code blocks.
* Highlighter `function (str, lang)` should return escaped HTML. It can also * Highlighter `function (str, lang)` should return escaped HTML. It can also
* return empty string if the source was not changed and should be escaped externaly. * return empty string if the source was not changed and should be escaped externaly.

5
lib/presets/commonmark.js

@ -15,7 +15,10 @@ module.exports = {
typographer: false, typographer: false,
// Double + single quotes replacement pairs, when typographer enabled, // Double + single quotes replacement pairs, when typographer enabled,
// and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. // and smartquotes on. Could be either a String or an Array.
//
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */, quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */,
// Highlighter function. Should return escaped HTML, // Highlighter function. Should return escaped HTML,

5
lib/presets/default.js

@ -15,7 +15,10 @@ module.exports = {
typographer: false, typographer: false,
// Double + single quotes replacement pairs, when typographer enabled, // Double + single quotes replacement pairs, when typographer enabled,
// and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. // and smartquotes on. Could be either a String or an Array.
//
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */, quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */,
// Highlighter function. Should return escaped HTML, // Highlighter function. Should return escaped HTML,

5
lib/presets/zero.js

@ -16,7 +16,10 @@ module.exports = {
typographer: false, typographer: false,
// Double + single quotes replacement pairs, when typographer enabled, // Double + single quotes replacement pairs, when typographer enabled,
// and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. // and smartquotes on. Could be either a String or an Array.
//
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */, quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */,
// Highlighter function. Should return escaped HTML, // Highlighter function. Should return escaped HTML,

2
lib/rules_core/replacements.js

@ -1,7 +1,5 @@
// Simple typographyc replacements // Simple typographyc replacements
// //
// '' → ‘’
// "" → “”. Set '«»' for Russian, '„“' for German, empty to disable
// (c) (C) → © // (c) (C) → ©
// (tm) (TM) → ™ // (tm) (TM) → ™
// (r) (R) → ® // (r) (R) → ®

28
lib/rules_core/smartquotes.js

@ -19,7 +19,7 @@ function replaceAt(str, index, ch) {
function process_inlines(tokens, state) { function process_inlines(tokens, state) {
var i, token, text, t, pos, max, thisLevel, item, lastChar, nextChar, var i, token, text, t, pos, max, thisLevel, item, lastChar, nextChar,
isLastPunctChar, isNextPunctChar, isLastWhiteSpace, isNextWhiteSpace, isLastPunctChar, isNextPunctChar, isLastWhiteSpace, isNextWhiteSpace,
canOpen, canClose, j, isSingle, stack; canOpen, canClose, j, isSingle, stack, openQuote, closeQuote;
stack = []; stack = [];
@ -104,16 +104,28 @@ function process_inlines(tokens, state) {
if (stack[j].level < thisLevel) { break; } if (stack[j].level < thisLevel) { break; }
if (item.single === isSingle && stack[j].level === thisLevel) { if (item.single === isSingle && stack[j].level === thisLevel) {
item = stack[j]; item = stack[j];
if (isSingle) { if (isSingle) {
tokens[item.token].content = replaceAt( openQuote = state.md.options.quotes[2];
tokens[item.token].content, item.pos, state.md.options.quotes[2]); closeQuote = state.md.options.quotes[3];
token.content = replaceAt(
token.content, t.index, state.md.options.quotes[3]);
} else { } else {
tokens[item.token].content = replaceAt( openQuote = state.md.options.quotes[0];
tokens[item.token].content, item.pos, state.md.options.quotes[0]); closeQuote = state.md.options.quotes[1];
token.content = replaceAt(token.content, t.index, state.md.options.quotes[1]);
} }
// replace token.content *before* tokens[item.token].content,
// because, if they are pointing at the same token, replaceAt
// could mess up indices when quote length != 1
token.content = replaceAt(token.content, t.index, closeQuote);
tokens[item.token].content = replaceAt(
tokens[item.token].content, item.pos, openQuote);
pos += closeQuote.length - 1;
if (item.token === i) { pos += openQuote.length - 1; }
text = token.content;
max = text.length;
stack.length = j; stack.length = j;
continue OUTER; continue OUTER;
} }

33
test/misc.js

@ -275,3 +275,36 @@ describe('maxNesting', function () {
}); });
}); });
describe('smartquotes', function () {
var md = markdownit({
typographer: true,
// all strings have different length to make sure
// we didn't accidentally count the wrong one
quotes: [ '[[[', ']]', '(((((', '))))' ]
});
it('Should support multi-character quotes', function () {
assert.strictEqual(
md.render('"foo" \'bar\''),
'<p>[[[foo]] (((((bar))))</p>\n'
);
});
it('Should support nested multi-character quotes', function () {
assert.strictEqual(
md.render('"foo \'bar\' baz"'),
'<p>[[[foo (((((bar)))) baz]]</p>\n'
);
});
it('Should support multi-character quotes in different tags', function () {
assert.strictEqual(
md.render('"a *b \'c *d* e\' f* g"'),
'<p>[[[a <em>b (((((c <em>d</em> e)))) f</em> g]]</p>\n'
);
});
});

Loading…
Cancel
Save