mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-15 23:37:25 +00:00
Merge pull request #2979 from overleaf/ta-outline-parser-v2
Improve Outline Parser GitOrigin-RevId: 1774ac1055efc8c2b899396e4f56b6993438daad
This commit is contained in:
parent
0e9771ac09
commit
3660a98fd3
1 changed files with 80 additions and 9 deletions
|
@ -1,32 +1,97 @@
|
|||
const COMMAND_LEVELS = {
|
||||
section: 0,
|
||||
subsection: 1,
|
||||
subsubsection: 2
|
||||
book: 10,
|
||||
part: 20,
|
||||
chapter: 30,
|
||||
section: 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 = []
|
||||
lines.forEach((line, lineId) => {
|
||||
const match = line.match(/\\(?<command>[sub]*section)\{(?<title>[^}]+)\}/)
|
||||
const match = line.match(MATCHER)
|
||||
if (!match) return
|
||||
const {
|
||||
groups: { command, title }
|
||||
} = match
|
||||
const [, command, , shortTitle, title] = match
|
||||
|
||||
flatOutline.push({
|
||||
line: lineId + 1,
|
||||
title,
|
||||
title: matchDisplayTitle(shortTitle || title),
|
||||
level: COMMAND_LEVELS[command]
|
||||
})
|
||||
})
|
||||
return flatOutline
|
||||
}
|
||||
|
||||
const DISPLAY_TITLE_REGEX = new RegExp('([^\\\\]*)\\\\([^{]+){([^}]+)}(.*)')
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
|
||||
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) {
|
||||
let parentOutlines = {}
|
||||
let nestedOutlines = []
|
||||
flatOutline.forEach(outline => {
|
||||
const parentOutline = parentOutlines[outline.level - 1]
|
||||
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)
|
||||
|
@ -37,7 +102,13 @@ function nestOutline(flatOutline) {
|
|||
// 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
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue