|
|
|
@ -7,14 +7,36 @@ const QUOTE_TEST_RE = /['"]/ |
|
|
|
const QUOTE_RE = /['"]/g |
|
|
|
const APOSTROPHE = '\u2019' /* ’ */ |
|
|
|
|
|
|
|
function replaceAt (str, index, ch) { |
|
|
|
return str.slice(0, index) + ch + str.slice(index + 1) |
|
|
|
function addReplacement (replacements, tokenIdx, pos, ch) { |
|
|
|
if (!replacements[tokenIdx]) { |
|
|
|
replacements[tokenIdx] = [] |
|
|
|
} |
|
|
|
|
|
|
|
replacements[tokenIdx].push({ pos, ch }) |
|
|
|
} |
|
|
|
|
|
|
|
function applyReplacements (str, replacements) { |
|
|
|
let result = '' |
|
|
|
let lastPos = 0 |
|
|
|
|
|
|
|
replacements.sort((a, b) => a.pos - b.pos) |
|
|
|
|
|
|
|
for (let i = 0; i < replacements.length; i++) { |
|
|
|
const replacement = replacements[i] |
|
|
|
|
|
|
|
result += str.slice(lastPos, replacement.pos) + replacement.ch |
|
|
|
lastPos = replacement.pos + 1 |
|
|
|
} |
|
|
|
|
|
|
|
return result + str.slice(lastPos) |
|
|
|
} |
|
|
|
|
|
|
|
function process_inlines (tokens, state) { |
|
|
|
let j |
|
|
|
|
|
|
|
const stack = [] |
|
|
|
// token index -> list of replacements in the original token content
|
|
|
|
const replacements = {} |
|
|
|
|
|
|
|
for (let i = 0; i < tokens.length; i++) { |
|
|
|
const token = tokens[i] |
|
|
|
@ -28,9 +50,9 @@ function process_inlines (tokens, state) { |
|
|
|
|
|
|
|
if (token.type !== 'text') { continue } |
|
|
|
|
|
|
|
let text = token.content |
|
|
|
const text = token.content |
|
|
|
let pos = 0 |
|
|
|
let max = text.length |
|
|
|
const max = text.length |
|
|
|
|
|
|
|
/* eslint no-labels:0,block-scoped-var:0 */ |
|
|
|
OUTER: |
|
|
|
@ -122,7 +144,7 @@ function process_inlines (tokens, state) { |
|
|
|
if (!canOpen && !canClose) { |
|
|
|
// middle of word
|
|
|
|
if (isSingle) { |
|
|
|
token.content = replaceAt(token.content, t.index, APOSTROPHE) |
|
|
|
addReplacement(replacements, i, t.index, APOSTROPHE) |
|
|
|
} |
|
|
|
continue |
|
|
|
} |
|
|
|
@ -145,18 +167,8 @@ function process_inlines (tokens, state) { |
|
|
|
closeQuote = 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 |
|
|
|
addReplacement(replacements, i, t.index, closeQuote) |
|
|
|
addReplacement(replacements, item.token, item.pos, openQuote) |
|
|
|
|
|
|
|
stack.length = j |
|
|
|
continue OUTER |
|
|
|
@ -172,10 +184,14 @@ function process_inlines (tokens, state) { |
|
|
|
level: thisLevel |
|
|
|
}) |
|
|
|
} else if (canClose && isSingle) { |
|
|
|
token.content = replaceAt(token.content, t.index, APOSTROPHE) |
|
|
|
addReplacement(replacements, i, t.index, APOSTROPHE) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Object.keys(replacements).forEach(function (tokenIdx) { |
|
|
|
tokens[tokenIdx].content = applyReplacements(tokens[tokenIdx].content, replacements[tokenIdx]) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
export default function smartquotes (state) { |
|
|
|
|