Procházet zdrojové kódy

Merge pull request #33391 from overleaf/em-bibtex-projection-32449

Use a projected state field for BibTeX entries in the editor

GitOrigin-RevId: 5034be8bdc0cb4b9d854135ac117046c1b3750e7
Eric Mc Sween před 3 měsíci
rodič
revize
aca60c02c0

+ 4 - 0
services/web/frontend/js/features/source-editor/utils/tree-operations/commands.ts

@@ -79,6 +79,7 @@ export const enterNode = (
 
     items.push({
       line: state.doc.lineAt(node.from).number,
+      toLine: state.doc.lineAt(node.to).number,
       title: commandName,
       from: node.from,
       to: node.to,
@@ -106,6 +107,7 @@ export const enterNode = (
 
     items.push({
       line: state.doc.lineAt(node.from).number,
+      toLine: state.doc.lineAt(node.to).number,
       title: commandName,
       from: node.from,
       to: node.to,
@@ -127,6 +129,7 @@ export const enterNode = (
     }
     items.push({
       line: state.doc.lineAt(node.from).number,
+      toLine: state.doc.lineAt(node.to).number,
       title: commandName,
       from: node.from,
       to: node.to,
@@ -180,6 +183,7 @@ export const enterNode = (
 
     items.push({
       line: state.doc.lineAt(commandNode.from).number,
+      toLine: state.doc.lineAt(commandNode.to).number,
       title: text,
       from: commandNode.from,
       to: commandNode.to,

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

@@ -45,6 +45,7 @@ export const enterNode = (
       from: envNameNode.from,
       to: envNameNode.to,
       line: state.doc.lineAt(envNameNode.from).number,
+      toLine: state.doc.lineAt(envNameNode.to).number,
       type: 'usage',
       raw: state.sliceDoc(node.from, node.to),
     }
@@ -74,6 +75,7 @@ export const enterNode = (
       from: envNameNode.from,
       to: envNameNode.to,
       line: state.doc.lineAt(envNameNode.from).number,
+      toLine: state.doc.lineAt(envNameNode.to).number,
       type: 'definition',
       raw: state.sliceDoc(node.from, node.to),
     }

+ 2 - 0
services/web/frontend/js/features/source-editor/utils/tree-operations/outline.ts

@@ -155,6 +155,7 @@ export const enterNode = (
 
     const thisNode = {
       line: state.doc.lineAt(command.from).number,
+      toLine: state.doc.lineAt(command.to).number,
       title: getEntryText(state, name),
       from: command.from,
       to: command.to,
@@ -181,6 +182,7 @@ export const enterNode = (
           : ''
         const thisNode = {
           line: state.doc.lineAt(beginEnv.from).number,
+          toLine: state.doc.lineAt(beginEnv.to).number,
           title,
           from: beginEnv.from,
           to: beginEnv.to,

+ 11 - 3
services/web/frontend/js/features/source-editor/utils/tree-operations/projection.ts

@@ -12,6 +12,7 @@ export abstract class ProjectionItem {
   readonly from: number = 0
   readonly to: number = 0
   readonly line: number = 0
+  readonly toLine: number = 0
 }
 
 /* eslint-disable no-unused-vars */
@@ -48,9 +49,15 @@ export function updatePosition<T extends ProjectionItem>(
   const { from, to } = item
   const newFrom = transaction.changes.mapPos(from)
   const newTo = transaction.changes.mapPos(to)
-  const lineNumber = transaction.state.doc.lineAt(newFrom).number
+  const newLine = transaction.state.doc.lineAt(newFrom).number
+  const newToLine = transaction.state.doc.lineAt(newTo).number
 
-  if (newFrom === from && newTo === to && lineNumber === item.line) {
+  if (
+    newFrom === from &&
+    newTo === to &&
+    newLine === item.line &&
+    newToLine === item.toLine
+  ) {
     // Optimisation - if the item hasn't moved, don't create a new object
     // If items are not immutable this can introduce problems
     return item
@@ -60,7 +67,8 @@ export function updatePosition<T extends ProjectionItem>(
     ...item,
     from: newFrom,
     to: newTo,
-    line: lineNumber,
+    line: newLine,
+    toLine: newToLine,
   }
 }
 

+ 31 - 0
services/web/test/frontend/features/source-editor/languages/latex/latex-outline.test.ts

@@ -94,6 +94,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
             level: SECTION_LEVEL,
             title: 'sec title',
             line: 2,
+            toLine: 2,
           },
           {
             from: 35,
@@ -101,6 +102,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
             level: SUB_SECTION_LEVEL,
             title: 'subsec title',
             line: 4,
+            toLine: 4,
           },
         ])
       })
@@ -127,6 +129,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
             level: SECTION_LEVEL,
             title: 'sec title 1',
             line: 2,
+            toLine: 2,
           },
           {
             from: 37,
@@ -134,6 +137,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
             level: SECTION_LEVEL,
             title: 'sec title 2',
             line: 4,
+            toLine: 4,
           },
         ])
       })
@@ -152,6 +156,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 16,
           title: 'title ',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
       ])
@@ -170,6 +175,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
             to: 17,
             title: 'title 1',
             line: 1,
+            toLine: 1,
             level: SECTION_LEVEL,
           },
         ])
@@ -189,6 +195,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
             to: 15,
             title: 'title',
             line: 1,
+            toLine: 1,
             level: SECTION_LEVEL,
           },
         ])
@@ -208,6 +215,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 15,
           title: 'title',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
         {
@@ -215,6 +223,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 37,
           title: 'subtitle',
           line: 2,
+          toLine: 2,
           level: SUB_SECTION_LEVEL,
         },
       ])
@@ -229,6 +238,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 15,
           title: 'title',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
         {
@@ -236,6 +246,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 38,
           title: 'subtitle',
           line: 3,
+          toLine: 3,
           level: SUB_SECTION_LEVEL,
         },
       ])
@@ -254,6 +265,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 15,
           title: 'title',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
       ])
@@ -282,6 +294,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 17,
           title: 'section',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
         {
@@ -289,6 +302,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 72,
           title: 'subsubsection',
           line: 3,
+          toLine: 3,
           level: SUB_SUB_SECTION_LEVEL,
         },
       ])
@@ -304,6 +318,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 17,
           title: 'section',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
         {
@@ -311,6 +326,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 41,
           title: 'subsection',
           line: 2,
+          toLine: 2,
           level: SUB_SECTION_LEVEL,
         },
         {
@@ -318,6 +334,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 71,
           title: 'subsubsection',
           line: 3,
+          toLine: 3,
           level: SUB_SUB_SECTION_LEVEL,
         },
       ])
@@ -343,6 +360,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 17,
           title: 'section',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
       ])
@@ -373,6 +391,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 11,
           title: 'book',
           line: 1,
+          toLine: 1,
           level: BOOK_LEVEL,
         },
         {
@@ -380,6 +399,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 23,
           title: 'part',
           line: 2,
+          toLine: 2,
           level: PART_LEVEL,
         },
         {
@@ -387,6 +407,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 41,
           title: 'chapter',
           line: 3,
+          toLine: 3,
           level: CHAPTER_LEVEL,
         },
         {
@@ -394,6 +415,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 59,
           title: 'section',
           line: 4,
+          toLine: 4,
           level: SECTION_LEVEL,
         },
         {
@@ -401,6 +423,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 83,
           title: 'subsection',
           line: 5,
+          toLine: 5,
           level: SUB_SECTION_LEVEL,
         },
         {
@@ -408,6 +431,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 113,
           title: 'subsubsection',
           line: 6,
+          toLine: 6,
           level: SUB_SUB_SECTION_LEVEL,
         },
         {
@@ -415,6 +439,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 135,
           title: 'paragraph',
           line: 7,
+          toLine: 7,
           level: PARAGRAPH_LEVEL,
         },
         {
@@ -422,6 +447,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 163,
           title: 'subparagraph',
           line: 8,
+          toLine: 8,
           level: SUB_PARAGRAPH_LEVEL,
         },
       ])
@@ -443,6 +469,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 30,
           title: 'section',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
       ])
@@ -466,6 +493,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 113,
           title: 'The function f(x) = x^2: Properties of x.',
           line: 1,
+          toLine: 1,
           level: SECTION_LEVEL,
         },
       ])
@@ -487,6 +515,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 22,
           title: 'test',
           line: 2,
+          toLine: 2,
           level: SECTION_LEVEL,
         },
         {
@@ -494,6 +523,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
           to: 41,
           title: 'test2',
           line: 3,
+          toLine: 3,
           level: SUB_SECTION_LEVEL,
         },
       ])
@@ -516,6 +546,7 @@ describe('CodeMirror LaTeX-FileOutline', function () {
             to: 28,
             title: 'frame title',
             line: 1,
+            toLine: 1,
             level: FRAME_LEVEL,
           },
         ])

+ 55 - 0
services/web/test/frontend/features/source-editor/utils/projection.test.ts

@@ -0,0 +1,55 @@
+import { expect } from 'chai'
+import { EditorState } from '@codemirror/state'
+import {
+  ProjectionItem,
+  updatePosition,
+} from '../../../../../frontend/js/features/source-editor/utils/tree-operations/projection'
+
+class TestItem extends ProjectionItem {}
+
+const itemAt = (
+  from: number,
+  to: number,
+  line: number,
+  toLine: number
+): TestItem => Object.assign(new TestItem(), { from, to, line, toLine })
+
+describe('updatePosition', function () {
+  it('returns the same instance when positions and lines are unchanged', function () {
+    const state = EditorState.create({ doc: 'first line\nsecond line' })
+    const tr = state.update({ changes: { from: 0, to: 0, insert: '' } })
+    const item = itemAt(11, 22, 2, 2)
+    expect(updatePosition(item, tr)).to.equal(item)
+  })
+
+  it('refreshes both line and toLine after an upstream insert adds lines', function () {
+    const state = EditorState.create({ doc: 'first line\nsecond line' })
+    const tr = state.update({
+      changes: { from: 0, to: 0, insert: 'top\nmiddle\n' },
+    })
+    // Item originally covered "second line" (lines 2..2) at offsets 11..22.
+    const item = itemAt(11, 22, 2, 2)
+    const updated = updatePosition(item, tr)
+    expect(updated).to.not.equal(item)
+    expect(updated.from).to.equal(22)
+    expect(updated.to).to.equal(33)
+    expect(updated.line).to.equal(4)
+    expect(updated.toLine).to.equal(4)
+  })
+
+  it('refreshes line and toLine independently for multi-line items', function () {
+    const state = EditorState.create({
+      doc: 'a\nbb\nccc\ndddd\neeeee',
+    })
+    // Item covers "bb\nccc" at offsets 2..8, on lines 2..3.
+    const item = itemAt(2, 8, 2, 3)
+    const tr = state.update({
+      changes: { from: 0, to: 0, insert: 'X\n' },
+    })
+    const updated = updatePosition(item, tr)
+    expect(updated.from).to.equal(4)
+    expect(updated.to).to.equal(10)
+    expect(updated.line).to.equal(3)
+    expect(updated.toLine).to.equal(4)
+  })
+})