Преглед изворни кода

Merge pull request #12696 from overleaf/mj-rt-skip-more-preamble

[cm6+rt] Update skip-preamble-cursor to skip \maketitle and abstract

GitOrigin-RevId: 3c54d8159bfdb431763872790a2b82ac4cffc09f
Mathias Jakobsen пре 3 година
родитељ
комит
3bd174631a

+ 17 - 17
services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts

@@ -731,6 +731,23 @@ export const atomicDecorations = (options: Options) => {
               return false
               return false
             }
             }
           }
           }
+        } else if (nodeRef.type.is('Maketitle')) {
+          if (shouldDecorate(state, nodeRef)) {
+            const line = state.doc.lineAt(nodeRef.from)
+            const from = extendBackwardsOverEmptyLines(state.doc, line)
+            const to = extendForwardsOverEmptyLines(state.doc, line)
+
+            if (shouldDecorate(state, { from, to })) {
+              decorations.push(
+                Decoration.replace({
+                  widget: new MakeTitleWidget(preamble),
+                  block: true,
+                }).range(from, to)
+              )
+            }
+
+            return false
+          }
         } else if (nodeRef.type.is('Item')) {
         } else if (nodeRef.type.is('Item')) {
           // only decorate \item inside a list
           // only decorate \item inside a list
           if (currentListEnvironment) {
           if (currentListEnvironment) {
@@ -823,23 +840,6 @@ export const atomicDecorations = (options: Options) => {
                       widget: new TeXWidget(),
                       widget: new TeXWidget(),
                     }).range(nodeRef.from, nodeRef.to)
                     }).range(nodeRef.from, nodeRef.to)
                   )
                   )
-                  return false
-                }
-              } else if (commandName === '\\maketitle') {
-                if (shouldDecorate(state, nodeRef)) {
-                  const line = state.doc.lineAt(nodeRef.from)
-                  const from = extendBackwardsOverEmptyLines(state.doc, line)
-                  const to = extendForwardsOverEmptyLines(state.doc, line)
-
-                  if (shouldDecorate(state, { from, to })) {
-                    decorations.push(
-                      Decoration.replace({
-                        widget: new MakeTitleWidget(preamble),
-                        block: true,
-                      }).range(from, to)
-                    )
-                  }
-
                   return false
                   return false
                 }
                 }
               } else if (hasCharacterSubstitution(commandName)) {
               } else if (hasCharacterSubstitution(commandName)) {

+ 10 - 3
services/web/frontend/js/features/source-editor/extensions/visual/skip-preamble-cursor.ts

@@ -1,7 +1,8 @@
 import { EditorView, ViewPlugin } from '@codemirror/view'
 import { EditorView, ViewPlugin } from '@codemirror/view'
 import { EditorSelection } from '@codemirror/state'
 import { EditorSelection } from '@codemirror/state'
-import { findDocumentEnvironment } from '../../utils/tree-operations/environments'
+import { findStartOfDocumentContent } from '../../utils/tree-operations/environments'
 import { syntaxTree } from '@codemirror/language'
 import { syntaxTree } from '@codemirror/language'
+import { extendForwardsOverEmptyLines } from './selection'
 export const skipPreambleWithCursor = ViewPlugin.define((view: EditorView) => {
 export const skipPreambleWithCursor = ViewPlugin.define((view: EditorView) => {
   let checkedOnce = false
   let checkedOnce = false
   return {
   return {
@@ -21,10 +22,16 @@ export const skipPreambleWithCursor = ViewPlugin.define((view: EditorView) => {
           )
           )
         ) {
         ) {
           setTimeout(() => {
           setTimeout(() => {
-            const position = findDocumentEnvironment(view.state)
+            const position =
+              extendForwardsOverEmptyLines(
+                update.state.doc,
+                update.state.doc.lineAt(
+                  findStartOfDocumentContent(update.state) ?? 0
+                )
+              ) + 1
             view.dispatch({
             view.dispatch({
               selection: EditorSelection.cursor(
               selection: EditorSelection.cursor(
-                Math.min(position ? position + 1 : 0, update.state.doc.length)
+                Math.min(position, update.state.doc.length)
               ),
               ),
             })
             })
           }, 0)
           }, 0)

+ 5 - 1
services/web/frontend/js/features/source-editor/lezer-latex/latex.grammar

@@ -84,7 +84,8 @@
     CenteringCtrlSeq,
     CenteringCtrlSeq,
     BibliographyCtrlSeq,
     BibliographyCtrlSeq,
     BibliographyStyleCtrlSeq,
     BibliographyStyleCtrlSeq,
-    AuthorCtrlSeq
+    AuthorCtrlSeq,
+    MaketitleCtrlSeq
 }
 }
 
 
 @external specialize {EnvName} specializeEnvName from "./tokens.mjs" {
 @external specialize {EnvName} specializeEnvName from "./tokens.mjs" {
@@ -310,6 +311,9 @@ KnownCommand {
     } |
     } |
     Item {
     Item {
         ItemCtrlSeq optionalWhitespace?
         ItemCtrlSeq optionalWhitespace?
+    } |
+    Maketitle {
+        MaketitleCtrlSeq optionalWhitespace?
     }
     }
 }
 }
 
 

+ 2 - 0
services/web/frontend/js/features/source-editor/lezer-latex/tokens.mjs

@@ -65,6 +65,7 @@ import {
   BibliographyStyleCtrlSeq,
   BibliographyStyleCtrlSeq,
   CenteringCtrlSeq,
   CenteringCtrlSeq,
   ListEnvName,
   ListEnvName,
+  MaketitleCtrlSeq,
 } from './latex.terms.mjs'
 } from './latex.terms.mjs'
 
 
 function nameChar(ch) {
 function nameChar(ch) {
@@ -635,6 +636,7 @@ const otherKnowncommands = {
   '\\centering': CenteringCtrlSeq,
   '\\centering': CenteringCtrlSeq,
   '\\bibliography': BibliographyCtrlSeq,
   '\\bibliography': BibliographyCtrlSeq,
   '\\bibliographystyle': BibliographyStyleCtrlSeq,
   '\\bibliographystyle': BibliographyStyleCtrlSeq,
+  '\\maketitle': MaketitleCtrlSeq,
 }
 }
 // specializer for control sequences
 // specializer for control sequences
 // return new tokens for specific control sequences
 // return new tokens for specific control sequences

+ 56 - 8
services/web/frontend/js/features/source-editor/utils/tree-operations/environments.ts

@@ -1,6 +1,6 @@
 import { ensureSyntaxTree } from '@codemirror/language'
 import { ensureSyntaxTree } from '@codemirror/language'
 import { EditorState } from '@codemirror/state'
 import { EditorState } from '@codemirror/state'
-import { SyntaxNode, SyntaxNodeRef } from '@lezer/common'
+import { SyntaxNode, SyntaxNodeRef, Tree } from '@lezer/common'
 import { previousSiblingIs } from './common'
 import { previousSiblingIs } from './common'
 import { NodeIntersectsChangeFn, ProjectionItem } from './projection'
 import { NodeIntersectsChangeFn, ProjectionItem } from './projection'
 
 
@@ -138,21 +138,69 @@ export const cursorIsAtEndEnvironment = (
   }
   }
 }
 }
 
 
-export const findDocumentEnvironment = (state: EditorState): number | null => {
-  const tree = ensureSyntaxTree(state, state.doc.length, HUNDRED_MS)
-  let position: number | null = null
+const findStartOfDocumentEnvironment = (tree: Tree): number | null => {
+  const docEnvironment = findNodeInDocument(tree, 'DocumentEnvironment')
+  return docEnvironment?.getChild('Content')?.from || null
+}
+
+const findStartOfAbstractEnvironment = (
+  tree: Tree,
+  state: EditorState
+): number | null => {
+  const abstractEnvironment = findNodeInDocument(
+    tree,
+    (nodeRef: SyntaxNodeRef) => {
+      return Boolean(
+        nodeRef.type.is('$Environment') &&
+          getEnvironmentName(nodeRef.node, state) === 'abstract'
+      )
+    }
+  )
+  return abstractEnvironment?.getChild('Content')?.from || null
+}
+
+const findMaketitleCommand = (tree: Tree): number | null => {
+  const maketitle = findNodeInDocument(tree, 'Maketitle')
+  return maketitle?.to ?? null
+}
+
+const findNodeInDocument = (
+  tree: Tree,
+  predicate: number | string | ((node: SyntaxNodeRef) => boolean)
+): SyntaxNode | null => {
+  let node: SyntaxNode | null = null
+  const predicateFn =
+    typeof predicate !== 'function'
+      ? (nodeRef: SyntaxNodeRef) => {
+          return nodeRef.type.is(predicate)
+        }
+      : predicate
   tree?.iterate({
   tree?.iterate({
     enter(nodeRef) {
     enter(nodeRef) {
-      if (position !== null) {
+      if (node !== null) {
         return false
         return false
       }
       }
-      if (nodeRef.type.is('DocumentEnvironment')) {
-        position = nodeRef.node.getChild('Content')?.from || null
+      if (predicateFn(nodeRef)) {
+        node = nodeRef.node
         return false
         return false
       }
       }
     },
     },
   })
   })
-  return position
+  return node
+}
+
+export const findStartOfDocumentContent = (
+  state: EditorState
+): number | null => {
+  const tree = ensureSyntaxTree(state, state.doc.length, HUNDRED_MS)
+  if (!tree) {
+    return null
+  }
+  return (
+    findStartOfAbstractEnvironment(tree, state) ??
+    findMaketitleCommand(tree) ??
+    findStartOfDocumentEnvironment(tree)
+  )
 }
 }
 
 
 /**
 /**