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.on "change", onChange
sharejs_doc.on "remoteop.recordRemote", (op, oldSnapshot, msg) ->
undoManager.nextUpdateIsRemote = true
trackChangesManager.nextUpdateMetaData = msg?.meta
editor.initing = true
sharejs_doc.attachToAce(editor)
editor.initing = false

View file

@ -11,10 +11,10 @@ define [
show_remote_warning: false
@reset()
@nextUpdateIsRemote = false
@editor.on "changeSession", (e) =>
@reset()
@session = e.session
e.session.setUndoManager(@)
showUndoConflictWarning: () ->
@ -38,20 +38,44 @@ define [
@firstUpdate = false
return
aceDeltaSets = options.args[0]
@session = options.args[1]
return if !aceDeltaSets?
@session = options.args[1]
lines = @session.getDocument().getAllLines()
linesBeforeChange = @_revertAceDeltaSetsOnDocLines(aceDeltaSets, lines)
simpleDeltaSets = @_aceDeltaSetsToSimpleDeltaSets(aceDeltaSets, linesBeforeChange)
@undoStack.push(
deltaSets: simpleDeltaSets
remote: @nextUpdateIsRemote
)
# We need to split the delta sets into local or remote groups before pushing onto
# the undo stack, since these are treated differently.
splitDeltaSets = []
currentDeltaSet = null # Make global to this function
do newDeltaSet = () ->
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 = []
@nextUpdateIsRemote = false
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()
return if !localUpdatesMade
@ -206,19 +230,16 @@ define [
throw "Unknown delta type"
return doc.split("\n")
_aceDeltaSetsToSimpleDeltaSets: (aceDeltaSets, docLines) ->
simpleDeltaSets = []
for deltaSet in aceDeltaSets
if deltaSet.group == "doc" # ignore fold changes
simpleDeltas = []
for delta in deltaSet.deltas
simpleDeltas.push @_aceDeltaToSimpleDelta(delta, docLines)
docLines = @_applyAceDeltasToDocLines([delta], docLines)
simpleDeltaSets.push {
deltas: simpleDeltas
group: deltaSet.group
}
return simpleDeltaSets
_aceDeltaSetToSimpleDeltaSet: (deltaSet, docLines) ->
simpleDeltas = []
for delta in deltaSet.deltas
simpleDeltas.push @_aceDeltaToSimpleDelta(delta, docLines)
docLines = @_applyAceDeltasToDocLines([delta], docLines)
simpleDeltaSet = {
deltas: simpleDeltas
group: deltaSet.group
}
return {simpleDeltaSet, docLines}
_simpleDeltaSetsToAceDeltaSets: (simpleDeltaSets, docLines) ->
for deltaSet in simpleDeltaSets

View file

@ -110,16 +110,46 @@ window.sharejs.extendDoc 'attach_ace', (editor, keepEditorContents, maxDocLength
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) ->
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
editorDoc.insert offsetToPos(pos), text
editorDoc.applyDelta({
start: start,
end: end,
action: "insert",
lines: lines,
remote: true
});
suppress = false
check()
doc.on 'delete', (pos, text) ->
suppress = true
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
check()