Просмотр исходного кода

[visual] suport for \texttt command in maketitle (#13824)

GitOrigin-RevId: 712eb8fb4fc0f2cbc5cd3c2e39ff6b8af39c0a49
Domagoj Kriskovic 3 лет назад
Родитель
Сommit
c21b8e31fd

+ 27 - 17
services/web/frontend/js/features/source-editor/extensions/visual/utils/typeset-content.ts

@@ -58,6 +58,26 @@ export function typesetNodeIntoElement(
         pushAncestor(document.createElement('em'))
         const textArgument = childNode.getChild('TextArgument')
         from = textArgument?.getChild('LongArg')?.from ?? childNode.to
+      } else if (isUnknownCommandWithName(childNode, '\\texttt', state)) {
+        const spanElement = document.createElement('span')
+        spanElement.classList.add('ol-cm-command-texttt')
+        pushAncestor(spanElement)
+        const textArgument = childNode.getChild('TextArgument')
+        from = textArgument?.getChild('LongArg')?.from ?? childNode.to
+      } else if (isUnknownCommandWithName(childNode, '\\and', state)) {
+        const spanElement = document.createElement('span')
+        spanElement.classList.add('ol-cm-command-and')
+        pushAncestor(spanElement)
+        const textArgument = childNode.getChild('TextArgument')
+        from = textArgument?.getChild('LongArg')?.from ?? childNode.to
+      } else if (
+        isUnknownCommandWithName(childNode, '\\corref', state) ||
+        isUnknownCommandWithName(childNode, '\\fnref', state) ||
+        isUnknownCommandWithName(childNode, '\\thanks', state)
+      ) {
+        // ignoring these commands
+        from = childNode.to
+        return false
       } else if (isNewline(childNode, state)) {
         ancestor().appendChild(document.createElement('br'))
         from = childNode.to
@@ -65,23 +85,13 @@ export function typesetNodeIntoElement(
     },
     function leave(childNodeRef) {
       const childNode = childNodeRef.node
-      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)) {
+      if (
+        isUnknownCommandWithName(childNode, '\\and', state) ||
+        isUnknownCommandWithName(childNode, '\\textit', state) ||
+        isUnknownCommandWithName(childNode, '\\textbf', state) ||
+        isUnknownCommandWithName(childNode, '\\emph', state) ||
+        isUnknownCommandWithName(childNode, '\\texttt', state)
+      ) {
         const typeSetElement = popAncestor()
         ancestor().appendChild(typeSetElement)
         const textArgument = childNode.getChild('TextArgument')

+ 24 - 18
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/maketitle.ts

@@ -119,26 +119,32 @@ function buildAuthorsElement(
   const authorsElement = document.createElement('div')
   authorsElement.classList.add('ol-cm-authors')
 
-  for (const { node, content } of authors) {
-    const authorContent = content.slice(1, -1) // trimming the braces
-    const authors = authorContent.replaceAll(/\s+/g, ' ').split('\\and')
-
-    for (const author of authors) {
-      const authorElement = document.createElement('div')
-      authorElement.classList.add('ol-cm-author')
-
-      for (const authorInfoItem of author.split('\\\\')) {
-        const authorLineElement = document.createElement('div')
-        authorLineElement.classList.add('ol-cm-author-line')
-        authorLineElement.textContent = authorInfoItem.trim()
-        authorElement.appendChild(authorLineElement)
+  for (const { node } of authors) {
+    const typesettedAuthors = document.createElement('div')
+    typesetNodeIntoElement(node, typesettedAuthors, view.state)
+
+    let currentAuthor = document.createElement('div')
+    currentAuthor.classList.add('ol-cm-author')
+    authorsElement.append(currentAuthor)
+
+    while (typesettedAuthors.firstChild) {
+      const child = typesettedAuthors.firstChild
+      if (
+        child instanceof HTMLElement &&
+        child.classList.contains('ol-cm-command-and')
+      ) {
+        currentAuthor = document.createElement('div')
+        currentAuthor.classList.add('ol-cm-author')
+        authorsElement.append(currentAuthor)
+        child.remove()
+      } else {
+        currentAuthor.append(child)
       }
-
-      authorElement.addEventListener('mouseup', () => {
-        selectNode(view, node)
-      })
-      authorsElement.append(authorElement)
     }
+
+    currentAuthor.addEventListener('mouseup', () => {
+      selectNode(view, node)
+    })
   }
 
   return authorsElement

+ 38 - 0
services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx

@@ -419,6 +419,7 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
     const deleteLine =
       '{command}{leftArrow}{shift}{command}{rightArrow}{backspace}'
 
+    // italic, bold and emph
     cy.get('@second-line').type(deleteLine)
     cy.get('@second-line').type(
       '\\title{{}formatted with \\textit{{}italic} \\textbf{{}bold} \\emph{{}emph}}'
@@ -437,6 +438,24 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
       'title<br> <b><i><em>formated</em></i></b> <i>only italic</i>'
     )
 
+    // texttt command
+    cy.get('@second-line').type(deleteLine)
+    cy.get('@second-line').type('\\title{{}title with \\texttt{{}command}}')
+    cy.get('.ol-cm-title').should(
+      'contain.html',
+      'title with <span class="ol-cm-command-texttt">command</span>'
+    )
+
+    cy.get('@second-line').type(deleteLine)
+    cy.get('@second-line').type(
+      '\\title{{}title with \\texttt{{}\\textbf{{}command}}}'
+    )
+    cy.get('.ol-cm-title').should(
+      'contain.html',
+      'title with <span class="ol-cm-command-texttt"><b>command</b></span>'
+    )
+
+    // unsupported commands
     cy.get('@second-line').type(deleteLine)
     cy.get('@second-line').type('\\title{{}Title with \\& ampersands}')
     cy.get('.ol-cm-title').should(
@@ -528,6 +547,25 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
     cy.get('.ol-cm-author').eq(1).should('contain', 'AuthorNeX')
   })
 
+  it('should ignore some commands in author', function () {
+    cy.get('@first-line').type(
+      [
+        '\\author{{}Author with \\corref{{}cor1} and \\fnref{{}label2} in the name}',
+        '\\title{{}Document title}',
+        '\\begin{{}document}',
+        '\\maketitle',
+        '\\end{{}document}',
+        '',
+      ].join('{Enter}')
+    )
+
+    cy.get('.ol-cm-authors').should('have.length', 1)
+    cy.get('.ol-cm-author').should(
+      'contain.html',
+      'Author with  and  in the name'
+    )
+  })
+
   describe('handling of special characters', function () {
     it('decorates a tilde with a non-breaking space', function () {
       cy.get('@first-line').type('Test~test')