mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-11 04:48:44 +03:00
Changes made with multiple cursors are undone/redone in parallel
This commit is contained in:
parent
6ad50b379f
commit
48b4008cab
@ -404,7 +404,7 @@ describe 'Buffer', ->
|
||||
expect(buffer.positionForCharacterIndex(61)).toEqual [2, 0]
|
||||
expect(buffer.positionForCharacterIndex(408)).toEqual [12, 2]
|
||||
|
||||
|
||||
describe "undo methods", ->
|
||||
describe "path-change event", ->
|
||||
it "emits path-change event when path is changed", ->
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
|
@ -1612,6 +1612,27 @@ describe "Editor", ->
|
||||
editor.trigger 'redo'
|
||||
expect(buffer.lineForRow(0)).toContain "foo"
|
||||
|
||||
it "batches the undo / redo of changes caused by multiple cursors", ->
|
||||
editor.setCursorScreenPosition([0, 0])
|
||||
editor.addCursorAtScreenPosition([1, 0])
|
||||
|
||||
editor.insertText("foo")
|
||||
editor.backspace()
|
||||
|
||||
expect(buffer.lineForRow(0)).toContain "fovar"
|
||||
expect(buffer.lineForRow(1)).toContain "fo "
|
||||
|
||||
editor.trigger 'undo'
|
||||
|
||||
expect(buffer.lineForRow(0)).toContain "foo"
|
||||
expect(buffer.lineForRow(1)).toContain "foo"
|
||||
|
||||
editor.trigger 'undo'
|
||||
|
||||
expect(buffer.lineForRow(0)).not.toContain "foo"
|
||||
expect(buffer.lineForRow(1)).not.toContain "foo"
|
||||
|
||||
|
||||
describe "when multiple lines are removed from the buffer (regression)", ->
|
||||
it "removes all of them from the dom", ->
|
||||
buffer.change(new Range([6, 24], [12, 0]), '')
|
||||
|
@ -370,4 +370,4 @@ describe "RootView", ->
|
||||
it "sets title to 'untitled' when buffer's path is null", ->
|
||||
editor = rootView.activeEditor()
|
||||
editor.setBuffer(new Buffer())
|
||||
expect(document.title).toBe "untitled"
|
||||
expect(document.title).toBe "untitled"
|
||||
|
@ -59,3 +59,72 @@ describe "UndoManager", ->
|
||||
|
||||
undoManager.redo()
|
||||
expect(buffer.getText()).toContain 'qsport'
|
||||
|
||||
describe "startUndoBatch()", ->
|
||||
it "causes all changes before a call to .endUndoBatch to be undone at the same time", ->
|
||||
buffer.insert([0, 0], "foo")
|
||||
undoManager.startUndoBatch()
|
||||
buffer.insert([1, 2], "111")
|
||||
buffer.insert([1, 9], "222")
|
||||
undoManager.endUndoBatch()
|
||||
|
||||
expect(buffer.lineForRow(1)).toBe ' 111var 222sort = function(items) {'
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
|
||||
expect(buffer.lineForRow(0)).toContain 'foo'
|
||||
|
||||
undoManager.undo()
|
||||
|
||||
expect(buffer.lineForRow(0)).not.toContain 'foo'
|
||||
|
||||
undoManager.redo()
|
||||
expect(buffer.lineForRow(0)).toContain 'foo'
|
||||
|
||||
undoManager.redo()
|
||||
expect(buffer.lineForRow(1)).toBe ' 111var 222sort = function(items) {'
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
|
||||
|
||||
it "old: causes all changes before a call to .endUndoBatch to be undone at the same time", ->
|
||||
buffer.insert([0, 0], "foo")
|
||||
undoManager.startUndoBatch()
|
||||
buffer.insert([1, 0], "bar")
|
||||
buffer.insert([2, 0], "bar")
|
||||
buffer.delete([[3, 4], [3, 8]])
|
||||
undoManager.endUndoBatch()
|
||||
buffer.change([[4, 4], [4, 9]], "slongaz")
|
||||
|
||||
expect(buffer.lineForRow(4)).not.toContain("while")
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(4)).toContain("while")
|
||||
|
||||
expect(buffer.lineForRow(1)).toContain("bar")
|
||||
expect(buffer.lineForRow(2)).toContain("bar")
|
||||
expect(buffer.lineForRow(3)).not.toContain("var")
|
||||
|
||||
undoManager.undo()
|
||||
|
||||
expect(buffer.lineForRow(1)).not.toContain("bar")
|
||||
expect(buffer.lineForRow(2)).not.toContain("bar")
|
||||
expect(buffer.lineForRow(3)).toContain("var")
|
||||
|
||||
undoManager.undo()
|
||||
|
||||
expect(buffer.lineForRow(0)).not.toContain("foo")
|
||||
|
||||
undoManager.redo()
|
||||
|
||||
expect(buffer.lineForRow(0)).toContain("foo")
|
||||
|
||||
undoManager.redo()
|
||||
|
||||
expect(buffer.lineForRow(1)).toContain("bar")
|
||||
expect(buffer.lineForRow(2)).toContain("bar")
|
||||
expect(buffer.lineForRow(3)).not.toContain("var")
|
||||
|
||||
undoManager.redo()
|
||||
|
||||
expect(buffer.lineForRow(4)).not.toContain("while")
|
||||
expect(buffer.lineForRow(4)).toContain("slongaz")
|
||||
|
@ -127,6 +127,12 @@ class Buffer
|
||||
@lines[oldRange.start.row..oldRange.end.row] = newTextLines
|
||||
@trigger 'change', { oldRange, newRange, oldText, newText }
|
||||
|
||||
startUndoBatch: ->
|
||||
@undoManager.startUndoBatch()
|
||||
|
||||
endUndoBatch: ->
|
||||
@undoManager.endUndoBatch()
|
||||
|
||||
undo: ->
|
||||
@undoManager.undo()
|
||||
|
||||
|
@ -77,20 +77,29 @@ class CompositeSeleciton
|
||||
fn(selection) for selection in @getSelections()
|
||||
@mergeIntersectingSelections(reverse: true)
|
||||
|
||||
mutateSelectedText: (fn) ->
|
||||
selections = @getSelections()
|
||||
if selections.length > 1
|
||||
@editor.buffer.startUndoBatch()
|
||||
fn(selection) for selection in selections
|
||||
@editor.buffer.endUndoBatch()
|
||||
else
|
||||
fn(selections[0])
|
||||
|
||||
insertText: (text) ->
|
||||
selection.insertText(text) for selection in @getSelections()
|
||||
@mutateSelectedText (selection) -> selection.insertText(text)
|
||||
|
||||
backspace: ->
|
||||
selection.backspace() for selection in @getSelections()
|
||||
@mutateSelectedText (selection) -> selection.backspace()
|
||||
|
||||
backspaceToBeginningOfWord: ->
|
||||
selection.backspaceToBeginningOfWord() for selection in @getSelections()
|
||||
@mutateSelectedText (selection) -> selection.backspaceToBeginningOfWord()
|
||||
|
||||
delete: ->
|
||||
selection.delete() for selection in @getSelections()
|
||||
@mutateSelectedText (selection) -> selection.delete()
|
||||
|
||||
deleteToEndOfWord: ->
|
||||
selection.deleteToEndOfWord() for selection in @getSelections()
|
||||
@mutateSelectedText (selection) -> selection.deleteToEndOfWord()
|
||||
|
||||
selectToScreenPosition: (position) ->
|
||||
@getLastSelection().selectToScreenPosition(position)
|
||||
|
@ -2,6 +2,7 @@ module.exports =
|
||||
class UndoManager
|
||||
undoHistory: null
|
||||
redoHistory: null
|
||||
currentBatch: null
|
||||
preserveHistory: false
|
||||
|
||||
constructor: (@buffer) ->
|
||||
@ -9,22 +10,37 @@ class UndoManager
|
||||
@redoHistory = []
|
||||
@buffer.on 'change', (op) =>
|
||||
unless @preserveHistory
|
||||
@undoHistory.push(op)
|
||||
if @currentBatch
|
||||
@currentBatch.push(op)
|
||||
else
|
||||
@undoHistory.push([op])
|
||||
@redoHistory = []
|
||||
|
||||
undo: ->
|
||||
if op = @undoHistory.pop()
|
||||
if ops = @undoHistory.pop()
|
||||
@preservingHistory =>
|
||||
@buffer.change op.newRange, op.oldText
|
||||
@redoHistory.push op
|
||||
opsInReverse = new Array(ops...)
|
||||
opsInReverse.reverse()
|
||||
for op in opsInReverse
|
||||
@buffer.change op.newRange, op.oldText
|
||||
@redoHistory.push ops
|
||||
|
||||
redo: ->
|
||||
if op = @redoHistory.pop()
|
||||
if ops = @redoHistory.pop()
|
||||
@preservingHistory =>
|
||||
@buffer.change op.oldRange, op.newText
|
||||
@undoHistory.push op
|
||||
for op in ops
|
||||
@buffer.change op.oldRange, op.newText
|
||||
@undoHistory.push ops
|
||||
|
||||
startUndoBatch: ->
|
||||
@currentBatch = []
|
||||
|
||||
endUndoBatch: ->
|
||||
@undoHistory.push(@currentBatch)
|
||||
@currentBatch = null
|
||||
|
||||
preservingHistory: (fn) ->
|
||||
@preserveHistory = true
|
||||
fn()
|
||||
@preserveHistory = false
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user