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

GitOrigin-RevId: 23b3e6a5db8583646f378cef52f41889a73a3672
This commit is contained in:
Alf Eaton 2023-06-29 09:52:38 +01:00 committed by Copybot
parent db26446d76
commit 62533ace6d
4 changed files with 31 additions and 105 deletions

14
package-lock.json generated
View file

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

View file

@ -1,91 +1,21 @@
import { EditorView } from '@codemirror/view' import { EditorView } from '@codemirror/view'
import { EditorSelection, Text } from '@codemirror/state' import { EditorSelection, EditorState, StateCommand } from '@codemirror/state'
import { selectNextOccurrence, SearchCursor } from '@codemirror/search' import { SearchQuery, StringQuery, selectWord } from '@codemirror/search'
type Spec = { export { selectNextOccurrence } from '@codemirror/search'
caseSensitive?: boolean
unquoted: string 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 stringCursor = (spec: Spec, doc: Text, from: number, to: number) => { export const selectPrevOccurrence: StateCommand = ({ state, dispatch }) => {
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 */
}
}
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 selectPrevOccurrence = (view: EditorView) => {
const { state } = view
const { ranges } = state.selection const { ranges } = state.selection
if (ranges.some(range => range.from === range.to)) { 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) const searchedText = state.sliceDoc(ranges[0].from, ranges[0].to)
@ -98,23 +28,19 @@ const selectPrevOccurrence = (view: EditorView) => {
return false return false
} }
const query = new StringQuery({ unquoted: searchedText }) const range = findPrevOccurence(state, searchedText)
const { main } = state.selection
const range = query.prevMatch(state.doc, main.from, main.to)
if (!range) { if (!range) {
return false return false
} }
view.dispatch({ dispatch(
selection: state.selection.addRange( state.update({
EditorSelection.range(range.from, range.to) selection: state.selection.addRange(
), EditorSelection.range(range.from, range.to)
effects: EditorView.scrollIntoView(range.to), ),
}) effects: EditorView.scrollIntoView(range.to),
})
)
return true return true
} }
export const selectOccurrence = (forward: boolean) => (view: EditorView) =>
forward ? selectNextOccurrence(view) : selectPrevOccurrence(view)

View file

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

View file

@ -76,7 +76,7 @@
"@codemirror/lang-markdown": "^6.1.1", "@codemirror/lang-markdown": "^6.1.1",
"@codemirror/language": "^6.6.0", "@codemirror/language": "^6.6.0",
"@codemirror/lint": "^6.2.1", "@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/state": "^6.2.0",
"@codemirror/view": "^6.14.0", "@codemirror/view": "^6.14.0",
"@contentful/rich-text-html-renderer": "^16.0.2", "@contentful/rich-text-html-renderer": "^16.0.2",