| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- const COMMAND_LEVELS = {
- book: 10,
- part: 20,
- addpart: 20,
- chapter: 30,
- addchap: 30,
- section: 40,
- addsec: 40,
- subsection: 50,
- subsubsection: 60,
- paragraph: 70,
- subparagraph: 80,
- }
- /*
- *
- * RegExp matcher parts:
- *
- * REGEX_START: begining of line, any number of spaces, double \ (required)
- * REGEX_COMMAND: any of the listed commands (required)
- * REGEX_SPACING: spaces and * between groups (optional)
- * REGEX_SHORT_TITLE: a text between square brackets (optional)
- * REGEX_TITLE: a text between curly brackets (required)
- *
- */
- const REGEX_START = '^\\s*\\\\'
- const REGEX_COMMAND = `(${Object.keys(COMMAND_LEVELS).join('|')})`
- const REGEX_SPACING = '\\s?\\*?\\s?'
- const REGEX_SHORT_TITLE = '(\\[([^\\]]+)\\])?'
- const REGEX_TITLE = '{(.*)}'
- const MATCHER = new RegExp(
- `${REGEX_START}${REGEX_COMMAND}${REGEX_SPACING}${REGEX_SHORT_TITLE}${REGEX_SPACING}${REGEX_TITLE}`
- )
- function matchOutline(content) {
- const lines = content.split('\n')
- const flatOutline = matchOutlineFromLines(lines)
- return flatOutline
- }
- function matchOutlineFromLines(lines) {
- const flatOutline = []
- lines.forEach((line, lineId) => {
- const match = line.match(MATCHER)
- if (!match) return
- const [, command, , shortTitle, title] = match
- flatOutline.push({
- line: lineId + 1,
- title: matchDisplayTitle(shortTitle || title),
- level: COMMAND_LEVELS[command],
- })
- })
- return flatOutline
- }
- const DISPLAY_TITLE_REGEX = /([^\\]*)\\([^{]+){([^}]+)}(.*)/
- const END_OF_TITLE_REGEX = /^([^{}]*?({[^{}]*?}[^{}]*?)*)}/
- /*
- * Attempt to improve the display of the outline title for titles with commands.
- * Either skip the command (for labels) or display the command's content instead
- * of the entire command.
- *
- * e.g. "Label \\label{foo} between" => "Label between"
- * e.g. "TT \\texttt{Bar}" => "TT Bar"
- *
- */
- function matchDisplayTitle(title) {
- const closingBracketPosition = title.indexOf('}')
- if (closingBracketPosition < 0) {
- // simple title (no commands)
- return title
- }
- // if there is anything outside the title def on the line, remove it
- // before proceeding
- const titleOnlyMatch = title.match(END_OF_TITLE_REGEX)
- if (titleOnlyMatch) {
- title = titleOnlyMatch[1]
- }
- const titleMatch = title.match(DISPLAY_TITLE_REGEX)
- if (!titleMatch) {
- // no contained commands; strip everything after the first closing bracket
- return title.substring(0, closingBracketPosition)
- }
- const [, textBefore, command, commandContent, textAfter] = titleMatch
- if (command === 'label') {
- // label: don't display them at all
- title = `${textBefore}${textAfter}`
- } else {
- // display the content of the command. Works well for formatting commands
- title = `${textBefore}${commandContent}${textAfter}`
- }
- return title
- }
- function nestOutline(flatOutline) {
- const parentOutlines = {}
- const nestedOutlines = []
- flatOutline.forEach(outline => {
- const parentOutlineLevels = Object.keys(parentOutlines)
- // find the nearest parent outline
- const nearestParentLevel = parentOutlineLevels
- .reverse()
- .find(level => level < outline.level)
- const parentOutline = parentOutlines[nearestParentLevel]
- if (!parentOutline) {
- // top level
- nestedOutlines.push(outline)
- } else if (!parentOutline.children) {
- // first outline in this node
- parentOutline.children = [outline]
- } else {
- // push outline to node
- parentOutline.children.push(outline)
- }
- // store the outline as new parent at its level and forget lower-level
- // outlines (if any) as they shouldn't get any children anymore
- parentOutlines[outline.level] = outline
- parentOutlineLevels
- .filter(level => level > outline.level)
- .forEach(level => delete parentOutlines[level])
- })
- return nestedOutlines
- }
- export { matchOutline, matchOutlineFromLines, nestOutline }
|