class MarkdownIt::RulesCore::Smartquotes
Constants
- APOSTROPHE
- QUOTE_RE
- QUOTE_TEST_RE
Public Class Methods
process_inlines(tokens, state)
click to toggle source
# File lib/motion-markdown-it/rules_core/smartquotes.rb, line 18 def self.process_inlines(tokens, state) stack = [] (0...tokens.length).each do |i| token = tokens[i] thisLevel = tokens[i].level j = stack.length - 1 while j >= 0 break if (stack[j][:level] <= thisLevel) j -= 1 end # stack.length = j + 1 stack = (j < stack.length ? stack.slice(0, j + 1) : stack.fill(nil, stack.length...(j+1))) next if (token.type != 'text') text = token.content pos = 0 max = text.length # OUTER loop while pos < max continue_outer_loop = false t = QUOTE_RE.match(text, pos) break if t.nil? canOpen = true canClose = true pos = t.begin(0) + 1 isSingle = (t[0] == "'") # Find previous character, # default to space if it's the beginning of the line # lastChar = 0x20 if t.begin(0) - 1 >= 0 lastChar = charCodeAt(text, t.begin(0) - 1) else (i - 1).downto(0) do |j| break if tokens[j].type == 'softbreak' || tokens[j].type == 'hardbreak' # lastChar defaults to 0x20 next if tokens[j].type != 'text' lastChar = charCodeAt(tokens[j].content, tokens[j].content.length - 1) break end end # Find next character, # default to space if it's the end of the line # nextChar = 0x20 if pos < max nextChar = charCodeAt(text, pos) else (i + 1).upto(tokens.length - 1) do |j| break if tokens[j].type == 'softbreak' || tokens[j].type == 'hardbreak' # nextChar defaults to 0x20 next if tokens[j].type != 'text' nextChar = charCodeAt(tokens[j].content, 0) break end end isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(fromCodePoint(lastChar)) isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(fromCodePoint(nextChar)) isLastWhiteSpace = isWhiteSpace(lastChar) isNextWhiteSpace = isWhiteSpace(nextChar) if (isNextWhiteSpace) canOpen = false elsif (isNextPunctChar) if (!(isLastWhiteSpace || isLastPunctChar)) canOpen = false end end if (isLastWhiteSpace) canClose = false elsif (isLastPunctChar) if (!(isNextWhiteSpace || isNextPunctChar)) canClose = false end end if (nextChar == 0x22 && t[0] == '"') # " if (lastChar >= 0x30 && lastChar <= 0x39) # >= 0 && <= 9 # special case: 1"" - count first quote as an inch canClose = canOpen = false end end if (canOpen && canClose) # treat this as the middle of the word canOpen = false canClose = isNextPunctChar end if (!canOpen && !canClose) # middle of word if (isSingle) token.content = replaceAt(token.content, t.begin(0), APOSTROPHE) end next end if (canClose) # this could be a closing quote, rewind the stack to get a match j = stack.length - 1 while j >= 0 item = stack[j] break if (stack[j][:level] < thisLevel) if (item[:single] == isSingle && stack[j][:level] == thisLevel) item = stack[j] if isSingle openQuote = state.md.options[:quotes][2] closeQuote = state.md.options[:quotes][3] else openQuote = state.md.options[:quotes][0] closeQuote = state.md.options[:quotes][1] end # 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.begin(0), closeQuote) tokens[item[:token]].content = replaceAt(tokens[item[:token]].content, item[:pos], openQuote) pos += closeQuote.length - 1 pos += (openQuote.length - 1) if item[:token] == i text = token.content max = text.length stack = (j < stack.length ? stack.slice(0, j) : stack.fill(nil, stack.length...(j))) # stack.length = j continue_outer_loop = true # continue OUTER; break end j -= 1 end end next if continue_outer_loop if (canOpen) stack.push({ token: i, pos: t.begin(0), single: isSingle, level: thisLevel }) elsif (canClose && isSingle) token.content = replaceAt(token.content, t.begin(0), APOSTROPHE) end end end end
replaceAt(str, index, ch)
click to toggle source
# File lib/motion-markdown-it/rules_core/smartquotes.rb, line 13 def self.replaceAt(str, index, ch) return str[0, index] + ch + str[(index + 1)..-1] end
smartquotes(state)
click to toggle source
# File lib/motion-markdown-it/rules_core/smartquotes.rb, line 182 def self.smartquotes(state) return if (!state.md.options[:typographer]) blkIdx = state.tokens.length - 1 while blkIdx >= 0 if (state.tokens[blkIdx].type != 'inline' || !(QUOTE_TEST_RE =~ state.tokens[blkIdx].content)) blkIdx -= 1 next end process_inlines(state.tokens[blkIdx].children, state) blkIdx -= 1 end end