[cm6] support \emph and \textbf in maketitle (#13703)

* [cm6] support \textbf in maketitle

* support for emph

* added support for \\textbf

* fix overlapping ignored ranges

* fix overlapping fn, added test

* multiple maketitle cases in the same test

* prettier

* simplify from calculation

* added test cases from #13736

GitOrigin-RevId: 00dcf15d6d62903ae30b387a16929e68dde8ca1b
This commit is contained in:
Domagoj Kriskovic 2023-07-11 12:46:19 +02:00 committed by Copybot
parent fdeb36c2c2
commit 8ab6239a24
2 changed files with 66 additions and 13 deletions

View file

@ -34,8 +34,6 @@ export function typesetNodeIntoElement(
const popAncestor = () => ancestorStack.pop()! const popAncestor = () => ancestorStack.pop()!
const pushAncestor = (x: HTMLElement) => ancestorStack.push(x) const pushAncestor = (x: HTMLElement) => ancestorStack.push(x)
// NOTE: Quite hack-ish way to omit closing braces from the output
const ignoredRanges: { from: number; to: number }[] = []
let from = node.from let from = node.from
node.cursor().iterate( node.cursor().iterate(
@ -45,20 +43,21 @@ export function typesetNodeIntoElement(
ancestor().append( ancestor().append(
document.createTextNode(state.sliceDoc(from, childNode.from)) document.createTextNode(state.sliceDoc(from, childNode.from))
) )
from = ignoredRanges.some(
range => range.from <= childNode.from && range.to >= childNode.from from = childNode.from
)
? childNode.to
: childNode.from
} }
if (isUnknownCommandWithName(childNode, '\\textit', state)) { if (isUnknownCommandWithName(childNode, '\\textit', state)) {
pushAncestor(document.createElement('i')) pushAncestor(document.createElement('i'))
const argument = childNode.getChild('TextArgument') const textArgument = childNode.getChild('TextArgument')
from = argument?.getChild('LongArg')?.from ?? childNode.to from = textArgument?.getChild('LongArg')?.from ?? childNode.to
const endBrace = argument?.getChild('CloseBrace') } else if (isUnknownCommandWithName(childNode, '\\textbf', state)) {
if (endBrace) { pushAncestor(document.createElement('b'))
ignoredRanges.push(endBrace) const textArgument = childNode.getChild('TextArgument')
} from = textArgument?.getChild('LongArg')?.from ?? childNode.to
} else if (isUnknownCommandWithName(childNode, '\\emph', state)) {
pushAncestor(document.createElement('em'))
const textArgument = childNode.getChild('TextArgument')
from = textArgument?.getChild('LongArg')?.from ?? childNode.to
} else if (isNewline(childNode, state)) { } else if (isNewline(childNode, state)) {
ancestor().appendChild(document.createElement('br')) ancestor().appendChild(document.createElement('br'))
from = childNode.to from = childNode.to
@ -69,6 +68,27 @@ export function typesetNodeIntoElement(
if (isUnknownCommandWithName(childNode, '\\textit', state)) { if (isUnknownCommandWithName(childNode, '\\textit', state)) {
const typeSetElement = popAncestor() const typeSetElement = popAncestor()
ancestor().appendChild(typeSetElement) ancestor().appendChild(typeSetElement)
const textArgument = childNode.getChild('TextArgument')
const endBrace = textArgument?.getChild('CloseBrace')
if (endBrace) {
from = endBrace.to
}
} else if (isUnknownCommandWithName(childNode, '\\textbf', state)) {
const typeSetElement = popAncestor()
ancestor().appendChild(typeSetElement)
const textArgument = childNode.getChild('TextArgument')
const endBrace = textArgument?.getChild('CloseBrace')
if (endBrace) {
from = endBrace.to
}
} else if (isUnknownCommandWithName(childNode, '\\emph', state)) {
const typeSetElement = popAncestor()
ancestor().appendChild(typeSetElement)
const textArgument = childNode.getChild('TextArgument')
const endBrace = textArgument?.getChild('CloseBrace')
if (endBrace) {
from = endBrace.to
}
} }
} }
) )

View file

@ -360,6 +360,39 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
cy.get('.ol-cm-maketitle') cy.get('.ol-cm-maketitle')
cy.get('.ol-cm-title').should('contain.html', 'Document title<br>with') cy.get('.ol-cm-title').should('contain.html', 'Document title<br>with')
cy.get('.ol-cm-author').should('have.text', 'Author') cy.get('.ol-cm-author').should('have.text', 'Author')
cy.get('.ol-cm-preamble-widget').click()
const deleteLine =
'{command}{leftArrow}{shift}{command}{rightArrow}{backspace}'
cy.get('@second-line').type(deleteLine)
cy.get('@second-line').type(
'\\title{{}formatted with \\textit{{}italic} \\textbf{{}bold} \\emph{{}emph}}'
)
cy.get('.ol-cm-title').should(
'contain.html',
'formatted with <i>italic</i> <b>bold</b> <em>emph</em>'
)
cy.get('@second-line').type(deleteLine)
cy.get('@second-line').type(
'\\title{{}title\\\\ \\textbf{{}\\textit{{}\\emph{{}formated}}} \\textit{{}only italic}}'
)
cy.get('.ol-cm-title').should(
'contain.html',
'title<br> <b><i><em>formated</em></i></b> <i>only italic</i>'
)
cy.get('@second-line').type(deleteLine)
cy.get('@second-line').type('\\title{{}Title with \\& ampersands}')
cy.get('.ol-cm-title').should(
'contain.html',
'Title with \\&amp; ampersands'
)
cy.get('@second-line').type(deleteLine)
cy.get('@second-line').type('\\title{{}My \\LaTeX{{}} document}')
cy.get('.ol-cm-title').should('contain.html', 'My \\LaTeX{} document')
}) })
it('decorates footnotes', function () { it('decorates footnotes', function () {