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

Use StringQuery and selectWord from @codemirror/search (#12828)

GitOrigin-RevId: 23b3e6a5db8583646f378cef52f41889a73a3672
Alf Eaton пре 3 година
родитељ
комит
62533ace6d

+ 7 - 7
package-lock.json

@@ -3384,8 +3384,8 @@
     },
     "node_modules/@codemirror/search": {
       "version": "6.4.0",
-      "resolved": "git+ssh://git@github.com/overleaf/codemirror-search.git#ea83364b22ad66455fc94babea7d576fa9f76a93",
-      "integrity": "sha512-02UOFSNY7/FamUaRPNPwcjq58V2nsRbtXRIT85/AKgfEWv6tVHj5slobCeaRKCXb6hPSOEDMztR5ShmbuxLfEw==",
+      "resolved": "git+ssh://git@github.com/overleaf/codemirror-search.git#6a09ea7eaad138d810f989753036eabce23cc969",
+      "integrity": "sha512-LblfUBGsW2+0U+orNGYcJHGKRnbUSU7V/tQDRNKlrw+hE0ZgAGW1B+dZAkcd+AITn6VwjFxKgERB1CzDkWmQWQ==",
       "license": "MIT",
       "dependencies": {
         "@codemirror/state": "^6.0.0",
@@ -41260,7 +41260,7 @@
         "@codemirror/lang-markdown": "^6.1.1",
         "@codemirror/language": "^6.6.0",
         "@codemirror/lint": "^6.2.1",
-        "@codemirror/search": "github:overleaf/codemirror-search#ea83364b22ad66455fc94babea7d576fa9f76a93",
+        "@codemirror/search": "github:overleaf/codemirror-search#6a09ea7eaad138d810f989753036eabce23cc969",
         "@codemirror/state": "^6.2.0",
         "@codemirror/view": "^6.14.0",
         "@contentful/rich-text-html-renderer": "^16.0.2",
@@ -45030,9 +45030,9 @@
       }
     },
     "@codemirror/search": {
-      "version": "git+ssh://git@github.com/overleaf/codemirror-search.git#ea83364b22ad66455fc94babea7d576fa9f76a93",
-      "integrity": "sha512-02UOFSNY7/FamUaRPNPwcjq58V2nsRbtXRIT85/AKgfEWv6tVHj5slobCeaRKCXb6hPSOEDMztR5ShmbuxLfEw==",
-      "from": "@codemirror/search@github:overleaf/codemirror-search#ea83364b22ad66455fc94babea7d576fa9f76a93",
+      "version": "git+ssh://git@github.com/overleaf/codemirror-search.git#6a09ea7eaad138d810f989753036eabce23cc969",
+      "integrity": "sha512-LblfUBGsW2+0U+orNGYcJHGKRnbUSU7V/tQDRNKlrw+hE0ZgAGW1B+dZAkcd+AITn6VwjFxKgERB1CzDkWmQWQ==",
+      "from": "@codemirror/search@github:overleaf/codemirror-search#6a09ea7eaad138d810f989753036eabce23cc969",
       "requires": {
         "@codemirror/state": "^6.0.0",
         "@codemirror/view": "^6.0.0",
@@ -50242,7 +50242,7 @@
         "@codemirror/lang-markdown": "^6.1.1",
         "@codemirror/language": "^6.6.0",
         "@codemirror/lint": "^6.2.1",
-        "@codemirror/search": "github:overleaf/codemirror-search#ea83364b22ad66455fc94babea7d576fa9f76a93",
+        "@codemirror/search": "github:overleaf/codemirror-search#6a09ea7eaad138d810f989753036eabce23cc969",
         "@codemirror/state": "^6.2.0",
         "@codemirror/view": "^6.14.0",
         "@contentful/rich-text-html-renderer": "^16.0.2",

+ 19 - 93
services/web/frontend/js/features/source-editor/commands/select.ts

@@ -1,91 +1,21 @@
 import { EditorView } from '@codemirror/view'
-import { EditorSelection, Text } from '@codemirror/state'
-import { selectNextOccurrence, SearchCursor } from '@codemirror/search'
+import { EditorSelection, EditorState, StateCommand } from '@codemirror/state'
+import { SearchQuery, StringQuery, selectWord } from '@codemirror/search'
 
-type Spec = {
-  caseSensitive?: boolean
-  unquoted: string
-}
-
-const stringCursor = (spec: Spec, doc: Text, from: number, to: number) => {
-  return new SearchCursor(
-    doc,
-    spec.unquoted,
-    from,
-    to,
-    spec.caseSensitive ? undefined : x => x.toLowerCase()
-  )
-}
-
-class QueryType {
-  protected spec
-
-  constructor(spec: Spec) {
-    this.spec = spec
-  }
-}
-
-class StringQuery extends QueryType {
-  // Searching in reverse is, rather than implementing inverted search
-  // cursor, done by scanning chunk after chunk forward.
-  prevMatchInRange(doc: Text, from: number, to: number) {
-    for (let pos = to; ; ) {
-      const start = Math.max(
-        from,
-        pos - 10000 /* ChunkSize */ - this.spec.unquoted.length
-      )
-      const cursor = stringCursor(this.spec, doc, start, pos)
-      let range = null
-
-      while (!cursor.nextOverlapping().done) {
-        range = cursor.value
-      }
-
-      if (range) {
-        return range
-      }
-
-      if (start === from) {
-        return null
-      }
-
-      pos -= 10000 /* ChunkSize */
-    }
-  }
+export { selectNextOccurrence } from '@codemirror/search'
 
-  prevMatch(doc: Text, curFrom: number, curTo: number) {
-    return (
-      this.prevMatchInRange(doc, 0, curFrom) ||
-      this.prevMatchInRange(doc, curTo, doc.length)
-    )
-  }
-}
-
-const selectWord = (view: EditorView) => {
-  const { selection } = view.state
-  const newSelection = EditorSelection.create(
-    selection.ranges.map(
-      range =>
-        view.state.wordAt(range.head) || EditorSelection.cursor(range.head)
-    ),
-    selection.mainIndex
-  )
-
-  if (newSelection.eq(selection)) {
-    return false
-  }
-
-  view.dispatch(view.state.update({ selection: newSelection }))
-
-  return true
+const findPrevOccurence = (state: EditorState, search: string) => {
+  const searchQuery = new SearchQuery({ search, literal: true })
+  const query = new StringQuery(searchQuery)
+  const { from, to } = state.selection.main
+  return query.prevMatch(state, from, to)
 }
 
-const selectPrevOccurrence = (view: EditorView) => {
-  const { state } = view
+export const selectPrevOccurrence: StateCommand = ({ state, dispatch }) => {
   const { ranges } = state.selection
 
   if (ranges.some(range => range.from === range.to)) {
-    return selectWord(view)
+    return selectWord({ state, dispatch })
   }
 
   const searchedText = state.sliceDoc(ranges[0].from, ranges[0].to)
@@ -98,23 +28,19 @@ const selectPrevOccurrence = (view: EditorView) => {
     return false
   }
 
-  const query = new StringQuery({ unquoted: searchedText })
-  const { main } = state.selection
-  const range = query.prevMatch(state.doc, main.from, main.to)
-
+  const range = findPrevOccurence(state, searchedText)
   if (!range) {
     return false
   }
 
-  view.dispatch({
-    selection: state.selection.addRange(
-      EditorSelection.range(range.from, range.to)
-    ),
-    effects: EditorView.scrollIntoView(range.to),
-  })
+  dispatch(
+    state.update({
+      selection: state.selection.addRange(
+        EditorSelection.range(range.from, range.to)
+      ),
+      effects: EditorView.scrollIntoView(range.to),
+    })
+  )
 
   return true
 }
-
-export const selectOccurrence = (forward: boolean) => (view: EditorView) =>
-  forward ? selectNextOccurrence(view) : selectPrevOccurrence(view)

+ 3 - 3
services/web/frontend/js/features/source-editor/extensions/shortcuts.ts

@@ -16,7 +16,7 @@ import {
   selectSyntaxRight,
 } from '@codemirror/commands'
 import { changeCase, duplicateSelection } from '../commands/ranges'
-import { selectOccurrence } from '../commands/select'
+import { selectNextOccurrence, selectPrevOccurrence } from '../commands/select'
 import { cloneSelectionVertically } from '../commands/cursor'
 import { dispatchEditorEvent } from './changes/change-manager'
 import {
@@ -165,12 +165,12 @@ export const shortcuts = Prec.high(
     {
       key: 'Ctrl-Alt-ArrowLeft',
       preventDefault: true,
-      run: selectOccurrence(false),
+      run: selectPrevOccurrence,
     },
     {
       key: 'Ctrl-Alt-ArrowRight',
       preventDefault: true,
-      run: selectOccurrence(true),
+      run: selectNextOccurrence,
     },
     {
       key: 'Mod-Shift-d',

+ 1 - 1
services/web/package.json

@@ -76,7 +76,7 @@
     "@codemirror/lang-markdown": "^6.1.1",
     "@codemirror/language": "^6.6.0",
     "@codemirror/lint": "^6.2.1",
-    "@codemirror/search": "github:overleaf/codemirror-search#ea83364b22ad66455fc94babea7d576fa9f76a93",
+    "@codemirror/search": "github:overleaf/codemirror-search#6a09ea7eaad138d810f989753036eabce23cc969",
     "@codemirror/state": "^6.2.0",
     "@codemirror/view": "^6.14.0",
     "@contentful/rich-text-html-renderer": "^16.0.2",