Stop local and remote ops being batched together in the undo manager if they happen in the same flush

This commit is contained in:
James Allen 2017-03-01 16:33:04 +00:00
parent a8756432b7
commit a6679a1aeb
3 changed files with 77 additions and 30 deletions

View file

@ -322,10 +322,6 @@ define [
doc = session.getDocument() doc = session.getDocument()
doc.on "change", onChange doc.on "change", onChange
sharejs_doc.on "remoteop.recordRemote", (op, oldSnapshot, msg) ->
undoManager.nextUpdateIsRemote = true
trackChangesManager.nextUpdateMetaData = msg?.meta
editor.initing = true editor.initing = true
sharejs_doc.attachToAce(editor) sharejs_doc.attachToAce(editor)
editor.initing = false editor.initing = false

View file

@ -11,10 +11,10 @@ define [
show_remote_warning: false show_remote_warning: false
@reset() @reset()
@nextUpdateIsRemote = false
@editor.on "changeSession", (e) => @editor.on "changeSession", (e) =>
@reset() @reset()
@session = e.session
e.session.setUndoManager(@) e.session.setUndoManager(@)
showUndoConflictWarning: () -> showUndoConflictWarning: () ->
@ -38,20 +38,44 @@ define [
@firstUpdate = false @firstUpdate = false
return return
aceDeltaSets = options.args[0] aceDeltaSets = options.args[0]
@session = options.args[1]
return if !aceDeltaSets? return if !aceDeltaSets?
@session = options.args[1]
lines = @session.getDocument().getAllLines() # We need to split the delta sets into local or remote groups before pushing onto
linesBeforeChange = @_revertAceDeltaSetsOnDocLines(aceDeltaSets, lines) # the undo stack, since these are treated differently.
simpleDeltaSets = @_aceDeltaSetsToSimpleDeltaSets(aceDeltaSets, linesBeforeChange) splitDeltaSets = []
@undoStack.push( currentDeltaSet = null # Make global to this function
deltaSets: simpleDeltaSets do newDeltaSet = () ->
remote: @nextUpdateIsRemote currentDeltaSet = {group: "doc", deltas: []}
) splitDeltaSets.push currentDeltaSet
currentRemoteState = null
for deltaSet in aceDeltaSets or []
if deltaSet.group == "doc" # ignore code folding etc.
for delta in deltaSet.deltas
if currentDeltaSet.remote? and currentDeltaSet.remote != !!delta.remote
newDeltaSet()
currentDeltaSet.deltas.push delta
currentDeltaSet.remote = !!delta.remote
# The lines are currently as they are after applying all these deltas, but to turn into simple deltas,
# we need the lines before each delta group.
docLines = @session.getDocument().getAllLines()
docLines = @_revertAceDeltaSetsOnDocLines(aceDeltaSets, docLines)
for deltaSet in splitDeltaSets
{simpleDeltaSet, docLines} = @_aceDeltaSetToSimpleDeltaSet(deltaSet, docLines)
frame = {
deltaSets: [simpleDeltaSet]
remote: deltaSet.remote
}
@undoStack.push frame
@redoStack = [] @redoStack = []
@nextUpdateIsRemote = false
undo: (dontSelect) -> undo: (dontSelect) ->
# We rely on the doclines being in sync with the undo stack, so make sure
# any pending undo deltas are processed.
@session.$syncInformUndoManager()
localUpdatesMade = @_shiftLocalChangeToTopOfUndoStack() localUpdatesMade = @_shiftLocalChangeToTopOfUndoStack()
return if !localUpdatesMade return if !localUpdatesMade
@ -206,19 +230,16 @@ define [
throw "Unknown delta type" throw "Unknown delta type"
return doc.split("\n") return doc.split("\n")
_aceDeltaSetsToSimpleDeltaSets: (aceDeltaSets, docLines) -> _aceDeltaSetToSimpleDeltaSet: (deltaSet, docLines) ->
simpleDeltaSets = [] simpleDeltas = []
for deltaSet in aceDeltaSets for delta in deltaSet.deltas
if deltaSet.group == "doc" # ignore fold changes simpleDeltas.push @_aceDeltaToSimpleDelta(delta, docLines)
simpleDeltas = [] docLines = @_applyAceDeltasToDocLines([delta], docLines)
for delta in deltaSet.deltas simpleDeltaSet = {
simpleDeltas.push @_aceDeltaToSimpleDelta(delta, docLines) deltas: simpleDeltas
docLines = @_applyAceDeltasToDocLines([delta], docLines) group: deltaSet.group
simpleDeltaSets.push { }
deltas: simpleDeltas return {simpleDeltaSet, docLines}
group: deltaSet.group
}
return simpleDeltaSets
_simpleDeltaSetsToAceDeltaSets: (simpleDeltaSets, docLines) -> _simpleDeltaSetsToAceDeltaSets: (simpleDeltaSets, docLines) ->
for deltaSet in simpleDeltaSets for deltaSet in simpleDeltaSets

View file

@ -110,16 +110,46 @@ window.sharejs.extendDoc 'attach_ace', (editor, keepEditorContents, maxDocLength
row:row, column:offset row:row, column:offset
# We want to insert a remote:true into the delta if the op comes from the
# underlying sharejs doc (which means it is from a remote op), so we have to do
# the work of editorDoc.insert and editorDoc.remove manually. These methods are
# copied from ace.js doc#insert and #remove, and then inject the remote:true
# flag into the delta.
doc.on 'insert', (pos, text) -> doc.on 'insert', (pos, text) ->
if (editorDoc.getLength() <= 1)
editorDoc.$detectNewLine(text)
lines = editorDoc.$split(text)
position = offsetToPos(pos)
start = editorDoc.clippedPos(position.row, position.column)
end = {
row: start.row + lines.length - 1,
column: (if lines.length == 1 then start.column else 0) + lines[lines.length - 1].length
}
suppress = true suppress = true
editorDoc.insert offsetToPos(pos), text editorDoc.applyDelta({
start: start,
end: end,
action: "insert",
lines: lines,
remote: true
});
suppress = false suppress = false
check() check()
doc.on 'delete', (pos, text) -> doc.on 'delete', (pos, text) ->
suppress = true
range = Range.fromPoints offsetToPos(pos), offsetToPos(pos + text.length) range = Range.fromPoints offsetToPos(pos), offsetToPos(pos + text.length)
editorDoc.remove range start = editorDoc.clippedPos(range.start.row, range.start.column)
end = editorDoc.clippedPos(range.end.row, range.end.column)
suppress = true
editorDoc.applyDelta({
start: start,
end: end,
action: "remove",
lines: editorDoc.getLinesForRange({start: start, end: end})
remote: true
});
suppress = false suppress = false
check() check()