diff --git a/services/web/frontend/js/features/source-editor/extensions/visual/utils/typeset-content.ts b/services/web/frontend/js/features/source-editor/extensions/visual/utils/typeset-content.ts index 246fc5f49a..913c045b83 100644 --- a/services/web/frontend/js/features/source-editor/extensions/visual/utils/typeset-content.ts +++ b/services/web/frontend/js/features/source-editor/extensions/visual/utils/typeset-content.ts @@ -34,8 +34,6 @@ export function typesetNodeIntoElement( const popAncestor = () => ancestorStack.pop()! 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 node.cursor().iterate( @@ -45,20 +43,21 @@ export function typesetNodeIntoElement( ancestor().append( document.createTextNode(state.sliceDoc(from, childNode.from)) ) - from = ignoredRanges.some( - range => range.from <= childNode.from && range.to >= childNode.from - ) - ? childNode.to - : childNode.from + + from = childNode.from } if (isUnknownCommandWithName(childNode, '\\textit', state)) { pushAncestor(document.createElement('i')) - const argument = childNode.getChild('TextArgument') - from = argument?.getChild('LongArg')?.from ?? childNode.to - const endBrace = argument?.getChild('CloseBrace') - if (endBrace) { - ignoredRanges.push(endBrace) - } + const textArgument = childNode.getChild('TextArgument') + from = textArgument?.getChild('LongArg')?.from ?? childNode.to + } else if (isUnknownCommandWithName(childNode, '\\textbf', state)) { + pushAncestor(document.createElement('b')) + 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)) { ancestor().appendChild(document.createElement('br')) from = childNode.to @@ -69,6 +68,27 @@ export function typesetNodeIntoElement( if (isUnknownCommandWithName(childNode, '\\textit', 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, '\\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 + } } } ) diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx index f8c298610e..3159f7fbbf 100644 --- a/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx @@ -360,6 +360,39 @@ describe(' in Visual mode', function () { cy.get('.ol-cm-maketitle') cy.get('.ol-cm-title').should('contain.html', 'Document title
with') 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 italic bold emph' + ) + + 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
formated only italic' + ) + + 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 \\& 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 () {