class MarkdownIt::RulesBlock::Table
Public Class Methods
escapedSplit(str)
click to toggle source
# File lib/motion-markdown-it/rules_block/table.rb, line 17 def self.escapedSplit(str) result = [] pos = 0 max = str.length escapes = 0 lastPos = 0 backTicked = false lastBackTick = 0 ch = charCodeAt(str, pos) while (pos < max) if ch == 0x60 # ` if (backTicked) # make \` close code sequence, but not open it; # the reason is: `\` is correct code block backTicked = false lastBackTick = pos elsif escapes % 2 == 0 backTicked = !backTicked lastBackTick = pos end elsif (ch == 0x7c && (escapes % 2 == 0) && !backTicked) # '|' result.push(str[lastPos...pos]) lastPos = pos + 1 end if ch == 0x5c # '\' escapes += 1 else escapes = 0 end pos += 1 # If there was an un-closed backtick, go back to just after # the last backtick, but as if it was a normal character if (pos == max && backTicked) backTicked = false pos = lastBackTick + 1 end ch = charCodeAt(str, pos) end result.push(str[lastPos..-1]) return result end
getLine(state, line)
click to toggle source
# File lib/motion-markdown-it/rules_block/table.rb, line 9 def self.getLine(state, line) pos = state.bMarks[line] + state.blkIndent max = state.eMarks[line] return state.src[pos, max - pos] end
table(state, startLine, endLine, silent)
click to toggle source
# File lib/motion-markdown-it/rules_block/table.rb, line 67 def self.table(state, startLine, endLine, silent) # should have at least two lines return false if (startLine + 2 > endLine) nextLine = startLine + 1 return false if (state.sCount[nextLine] < state.blkIndent) # if it's indented more than 3 spaces, it should be a code block return false if state.sCount[nextLine] - state.blkIndent >= 4 # first character of the second line should be '|', '-', ':', # and no other characters are allowed but spaces; # basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp pos = state.bMarks[nextLine] + state.tShift[nextLine] return false if (pos >= state.eMarks[nextLine]) ch = charCodeAt(state.src, pos) pos += 1 return false if (ch != 0x7C && ch != 0x2D && ch != 0x3A) # | or - or : while pos < state.eMarks[nextLine] ch = charCodeAt(state.src, pos) return false if (ch != 0x7C && ch != 0x2D && ch != 0x3A && !isSpace(ch)) # | or - or : pos += 1 end lineText = getLine(state, startLine + 1) columns = lineText.split('|') aligns = [] (0...columns.length).each do |i| t = columns[i].strip if t.empty? # allow empty columns before and after table, but not in between columns # e.g. allow ` |---| `, disallow ` ---||--- ` if (i == 0 || i == columns.length - 1) next else return false end end return false if (/^:?-+:?$/ =~ t).nil? if (charCodeAt(t, t.length - 1) == 0x3A) # ':' aligns.push(charCodeAt(t, 0) == 0x3A ? 'center' : 'right') elsif (charCodeAt(t, 0) == 0x3A) aligns.push('left') else aligns.push('') end end lineText = getLine(state, startLine).strip return false if !lineText.include?('|') return false if state.sCount[startLine] - state.blkIndent >= 4 columns = self.escapedSplit(lineText.gsub(/^\||\|$/, '')) # header row will define an amount of columns in the entire table, # and align row shouldn't be smaller than that (the rest of the rows can) columnCount = columns.length return false if columnCount > aligns.length return true if silent token = state.push('table_open', 'table', 1) token.map = tableLines = [ startLine, 0 ] token = state.push('thead_open', 'thead', 1) token.map = [ startLine, startLine + 1 ] token = state.push('tr_open', 'tr', 1) token.map = [ startLine, startLine + 1 ] (0...columns.length).each do |i| token = state.push('th_open', 'th', 1) token.map = [ startLine, startLine + 1 ] unless aligns[i].empty? token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ] end token = state.push('inline', '', 0) token.content = columns[i].strip token.map = [ startLine, startLine + 1 ] token.children = [] token = state.push('th_close', 'th', -1) end token = state.push('tr_close', 'tr', -1) token = state.push('thead_close', 'thead', -1) token = state.push('tbody_open', 'tbody', 1) token.map = tbodyLines = [ startLine + 2, 0 ] nextLine = startLine + 2 while nextLine < endLine break if (state.sCount[nextLine] < state.blkIndent) lineText = getLine(state, nextLine).strip break if !lineText.include?('|') break if state.sCount[nextLine] - state.blkIndent >= 4 columns = self.escapedSplit(lineText.gsub(/^\||\|$/, '')) token = state.push('tr_open', 'tr', 1) (0...columnCount).each do |i| token = state.push('td_open', 'td', 1) unless aligns[i].empty? token.attrs = [ [ 'style', 'text-align:' + aligns[i] ] ] end token = state.push('inline', '', 0) token.content = columns[i] ? columns[i].strip : '' token.children = [] token = state.push('td_close', 'td', -1) end token = state.push('tr_close', 'tr', -1) nextLine += 1 end token = state.push('tbody_close', 'tbody', -1) token = state.push('table_close', 'table', -1) tableLines[1] = tbodyLines[1] = nextLine state.line = nextLine return true end