mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-02 09:39:33 -05:00
170 lines
5.3 KiB
TypeScript
170 lines
5.3 KiB
TypeScript
|
import { foldEffect, foldState } from '@codemirror/language'
|
||
|
import { EditorSelection, EditorState } from '@codemirror/state'
|
||
|
import { DecorationSet, EditorView } from '@codemirror/view'
|
||
|
import { expect } from 'chai'
|
||
|
import { duplicateSelection } from '../../../../../frontend/js/features/source-editor/commands/ranges'
|
||
|
|
||
|
type Position = {
|
||
|
from: number
|
||
|
to: number
|
||
|
}
|
||
|
|
||
|
function folds(foldRanges: DecorationSet) {
|
||
|
const ranges: Position[] = []
|
||
|
foldRanges.between(Number.MIN_VALUE, Number.MAX_VALUE, (from, to) => {
|
||
|
ranges.push({ from, to })
|
||
|
})
|
||
|
return ranges
|
||
|
}
|
||
|
|
||
|
describe('Line duplication command', function () {
|
||
|
describe('For single selections', function () {
|
||
|
describe('For cursor selection', function () {
|
||
|
it('Cursor selection duplicates line downwards', function () {
|
||
|
const view = new EditorView({
|
||
|
doc: 'line1\nline2',
|
||
|
selection: EditorSelection.cursor(0),
|
||
|
})
|
||
|
|
||
|
duplicateSelection(view)
|
||
|
|
||
|
expect(view.state.doc.toString()).to.equal('line1\nline1\nline2')
|
||
|
|
||
|
expect(view.state.selection.ranges.length).to.equal(1)
|
||
|
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(0))).to
|
||
|
.be.true
|
||
|
})
|
||
|
|
||
|
it('Preserves folded ranges', function () {
|
||
|
const view = new EditorView({
|
||
|
doc: '\\begin{itemize}\n\t\\item test\n\\end{itemize}',
|
||
|
extensions: foldState,
|
||
|
})
|
||
|
|
||
|
view.dispatch(
|
||
|
view.state.update({
|
||
|
selection: EditorSelection.cursor(0),
|
||
|
// Fold to \begin{itemize}...\end{itemize}
|
||
|
effects: [foldEffect.of({ from: 15, to: 28 })],
|
||
|
})
|
||
|
)
|
||
|
|
||
|
duplicateSelection(view)
|
||
|
|
||
|
expect(folds(view.state.field(foldState))).to.deep.equal([
|
||
|
{ from: 15, to: 28 },
|
||
|
{ from: 57, to: 70 },
|
||
|
])
|
||
|
|
||
|
expect(view.state.selection.ranges.length).to.equal(1)
|
||
|
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(0))).to
|
||
|
.be.true
|
||
|
})
|
||
|
})
|
||
|
describe('For range selections', function () {
|
||
|
it('Duplicates line with a cursor downwards', function () {
|
||
|
const view = new EditorView({
|
||
|
doc: 'line1\nline2',
|
||
|
selection: EditorSelection.cursor(0),
|
||
|
})
|
||
|
|
||
|
duplicateSelection(view)
|
||
|
|
||
|
expect(view.state.doc.toString()).to.equal('line1\nline1\nline2')
|
||
|
})
|
||
|
|
||
|
it('Duplicates range forwards', function () {
|
||
|
const view = new EditorView({
|
||
|
doc: 'line1\nline2',
|
||
|
selection: EditorSelection.range(0, 5),
|
||
|
})
|
||
|
|
||
|
duplicateSelection(view)
|
||
|
|
||
|
expect(view.state.doc.toString()).to.equal('line1line1\nline2')
|
||
|
expect(view.state.selection.ranges.length).to.equal(1)
|
||
|
expect(view.state.selection.ranges[0].eq(EditorSelection.range(5, 10)))
|
||
|
.to.be.true
|
||
|
})
|
||
|
|
||
|
it('Duplicates range backwards', function () {
|
||
|
const view = new EditorView({
|
||
|
doc: 'line1\nline2',
|
||
|
selection: EditorSelection.range(5, 0),
|
||
|
})
|
||
|
|
||
|
duplicateSelection(view)
|
||
|
|
||
|
expect(view.state.doc.toString()).to.equal('line1line1\nline2')
|
||
|
expect(view.state.selection.ranges.length).to.equal(1)
|
||
|
expect(view.state.selection.ranges[0].eq(EditorSelection.range(5, 0)))
|
||
|
.to.be.true
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
describe('For multiple selections', function () {
|
||
|
it('Preserves folded ranges', function () {
|
||
|
const doc =
|
||
|
'\\begin{itemize}\n\t\\item line1\n\\end{itemize}\n\\begin{itemize}\n\t\\item line2\n\\end{itemize}'
|
||
|
const view = new EditorView({
|
||
|
doc,
|
||
|
extensions: [foldState, EditorState.allowMultipleSelections.of(true)],
|
||
|
})
|
||
|
|
||
|
view.dispatch(
|
||
|
view.state.update({
|
||
|
selection: EditorSelection.create([
|
||
|
EditorSelection.cursor(0),
|
||
|
EditorSelection.cursor(43),
|
||
|
]),
|
||
|
effects: [
|
||
|
foldEffect.of({ from: 15, to: 29 }),
|
||
|
foldEffect.of({ from: 58, to: 72 }),
|
||
|
],
|
||
|
})
|
||
|
)
|
||
|
|
||
|
duplicateSelection(view)
|
||
|
|
||
|
expect(view.state.doc.toString()).to.equal(
|
||
|
'\\begin{itemize}\n\t\\item line1\n\\end{itemize}\n\\begin{itemize}\n\t\\item line1\n\\end{itemize}\n\\begin{itemize}\n\t\\item line2\n\\end{itemize}\n\\begin{itemize}\n\t\\item line2\n\\end{itemize}'
|
||
|
)
|
||
|
|
||
|
expect(folds(view.state.field(foldState))).to.deep.equal([
|
||
|
{ from: 15, to: 29 },
|
||
|
{ from: 58, to: 72 },
|
||
|
{ from: 101, to: 115 },
|
||
|
{ from: 144, to: 158 },
|
||
|
])
|
||
|
|
||
|
expect(view.state.selection.ranges.length).to.equal(2)
|
||
|
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(0))).to.be
|
||
|
.true
|
||
|
expect(view.state.selection.ranges[1].eq(EditorSelection.cursor(86))).to
|
||
|
.be.true
|
||
|
})
|
||
|
|
||
|
it('Duplicates all selections', function () {
|
||
|
const view = new EditorView({
|
||
|
doc: 'line1\nline2',
|
||
|
extensions: [EditorState.allowMultipleSelections.of(true)],
|
||
|
selection: EditorSelection.create([
|
||
|
EditorSelection.cursor(1),
|
||
|
EditorSelection.range(7, 9),
|
||
|
]),
|
||
|
})
|
||
|
|
||
|
duplicateSelection(view)
|
||
|
|
||
|
expect(view.state.doc.toString()).to.equal('line1\nline1\nlinine2')
|
||
|
|
||
|
expect(view.state.selection.ranges.length).to.equal(2)
|
||
|
expect(view.state.selection.ranges[0].eq(EditorSelection.cursor(1))).to.be
|
||
|
.true
|
||
|
expect(view.state.selection.ranges[1].eq(EditorSelection.range(15, 17)))
|
||
|
.to.be.true
|
||
|
})
|
||
|
})
|
||
|
})
|