blob: 8a7b4346abf8f800d686ccfd21556a8b8bbcec34 [file] [log] [blame]
'use strict'
var whitespace = require('is-whitespace-character')
module.exports = table
var tab = '\t'
var lineFeed = '\n'
var space = ' '
var dash = '-'
var colon = ':'
var backslash = '\\'
var graveAccent = '`'
var verticalBar = '|'
var minColumns = 1
var minRows = 2
var left = 'left'
var center = 'center'
var right = 'right'
function table(eat, value, silent) {
var self = this
var index
var alignments
var alignment
var subvalue
var row
var length
var lines
var queue
var character
var hasDash
var align
var cell
var preamble
var count
var opening
var now
var position
var lineCount
var line
var rows
var table
var lineIndex
var pipeIndex
var first
// Exit when not in gfm-mode.
if (!self.options.gfm) {
return
}
// Get the rows.
// Detecting tables soon is hard, so there are some checks for performance
// here, such as the minimum number of rows, and allowed characters in the
// alignment row.
index = 0
lineCount = 0
length = value.length + 1
lines = []
while (index < length) {
lineIndex = value.indexOf(lineFeed, index)
pipeIndex = value.indexOf(verticalBar, index + 1)
if (lineIndex === -1) {
lineIndex = value.length
}
if (pipeIndex === -1 || pipeIndex > lineIndex) {
if (lineCount < minRows) {
return
}
break
}
lines.push(value.slice(index, lineIndex))
lineCount++
index = lineIndex + 1
}
// Parse the alignment row.
subvalue = lines.join(lineFeed)
alignments = lines.splice(1, 1)[0] || []
index = 0
length = alignments.length
lineCount--
alignment = false
align = []
while (index < length) {
character = alignments.charAt(index)
if (character === verticalBar) {
hasDash = null
if (alignment === false) {
if (first === false) {
return
}
} else {
align.push(alignment)
alignment = false
}
first = false
} else if (character === dash) {
hasDash = true
alignment = alignment || null
} else if (character === colon) {
if (alignment === left) {
alignment = center
} else if (hasDash && alignment === null) {
alignment = right
} else {
alignment = left
}
} else if (!whitespace(character)) {
return
}
index++
}
if (alignment !== false) {
align.push(alignment)
}
// Exit when without enough columns.
if (align.length < minColumns) {
return
}
/* istanbul ignore if - never used (yet) */
if (silent) {
return true
}
// Parse the rows.
position = -1
rows = []
table = eat(subvalue).reset({type: 'table', align: align, children: rows})
while (++position < lineCount) {
line = lines[position]
row = {type: 'tableRow', children: []}
// Eat a newline character when this is not the first row.
if (position) {
eat(lineFeed)
}
// Eat the row.
eat(line).reset(row, table)
length = line.length + 1
index = 0
queue = ''
cell = ''
preamble = true
count = null
opening = null
while (index < length) {
character = line.charAt(index)
if (character === tab || character === space) {
if (cell) {
queue += character
} else {
eat(character)
}
index++
continue
}
if (character === '' || character === verticalBar) {
if (preamble) {
eat(character)
} else {
if (character && opening) {
queue += character
index++
continue
}
if ((cell || character) && !preamble) {
subvalue = cell
if (queue.length > 1) {
if (character) {
subvalue += queue.slice(0, queue.length - 1)
queue = queue.charAt(queue.length - 1)
} else {
subvalue += queue
queue = ''
}
}
now = eat.now()
eat(subvalue)(
{type: 'tableCell', children: self.tokenizeInline(cell, now)},
row
)
}
eat(queue + character)
queue = ''
cell = ''
}
} else {
if (queue) {
cell += queue
queue = ''
}
cell += character
if (character === backslash && index !== length - 2) {
cell += line.charAt(index + 1)
index++
}
if (character === graveAccent) {
count = 1
while (line.charAt(index + 1) === character) {
cell += character
index++
count++
}
if (!opening) {
opening = count
} else if (count >= opening) {
opening = 0
}
}
}
preamble = false
index++
}
// Eat the alignment row.
if (!position) {
eat(lineFeed + alignments)
}
}
return table
}