private void UndoOp(DocumentModel doc, BaseOpMessage op, ITextEdit edit) { switch (op.MessageType) { case "insert": var insert = (OpInsertMessage)op; this.UndoInsert(doc, insert, edit); break; case "delete": var delete = (OpDeleteMessage)op; this.UndoDelete(doc, delete, edit); break; case "replace": var replace = (OpReplaceMessage)op; this.UndoReplace(doc, replace, edit); break; } }
private int RedoOp(DocumentModel doc, BaseOpMessage op, ITextEdit edit) { var len = 0; switch (op.MessageType) { case "insert": var insert = (OpInsertMessage)op; len = insert.Content.Length; this.Insert(doc, insert, edit); break; case "delete": var delete = (OpDeleteMessage)op; len = delete.Index - delete.End; this.Delete(doc, delete, edit); break; case "replace": var replace = (OpReplaceMessage)op; len = replace.OldContent.Length - replace.Content.Length; this.Replace(doc, replace, edit); break; } return len; }
private int DoOp(DocumentModel doc, ITextEdit edit, BaseOpMessage op) { var len = 0; switch (op.MessageType) { case "insert": var insert = op as OpInsertMessage; len = insert.Content.Length; doc.Changes.Add(insert); this.Insert(doc, insert, edit); break; case "delete": var delete = op as OpDeleteMessage; len = delete.Index - delete.End; doc.Changes.Add(delete); this.Delete(doc, delete, edit); break; case "replace": var replace = op as OpReplaceMessage; len = replace.OldContent.Length - replace.Content.Length; doc.Changes.Add(replace); this.Replace(doc, replace, edit); break; } return len; }
private int MoveOp(BaseOpMessage winner, BaseOpMessage loser) { var offset = 0; // the loser only needs to move if its position is after the winner if (loser.Index >= winner.Index) { switch (winner.MessageType) { case "insert": var i = winner as OpInsertMessage; offset = i.Content.Length; break; case "delete": var d = winner as OpDeleteMessage; offset = d.Index - d.End; break; case "replace": var r = winner as OpReplaceMessage; offset = r.Content.Length - r.OldContent.Length; break; } switch (loser.MessageType) { case "insert": loser.Index += offset; break; case "delete": var d = winner as OpDeleteMessage; d.Index += offset; d.End += offset; break; case "replace": var r = winner as OpReplaceMessage; r.Index += offset; r.End += offset; break; } } return offset; }
/// <summary> /// Resolves the operations. /// </summary> /// <param name="doc">The doc.</param> /// <param name="remote">The remote.</param> /// <param name="local">The local.</param> public static void Resolve(DocumentModel doc, BaseOpMessage remoteOp, BaseOpMessage localOp) { if (remoteOp is OpDeleteMessage) { var remote = remoteOp as OpDeleteMessage; Debug.WriteLine("Remote: {0} Delete", remote.User); if (localOp is OpDeleteMessage) { var local = localOp as OpDeleteMessage; int noOpLength = 0; if (remote.Index < local.Index) { // no overlap, remote OK as is, local needs modifications local.Index = local.Index - remote.ReplacementLength; } else if (remote.Index + remote.ReplacementLength < local.Index + local.ReplacementLength) { // remote del at lower index reaches into locally // applied del, local del reaches further out // --> shorten remote del appropriately, move and // shorten local del left remote.Length = (remote.Index + remote.ReplacementLength) - local.Index; local.Length = (remote.Index + remote.ReplacementLength) - local.Index; local.Index = remote.Index; } else if ((remote.Index + remote.ReplacementLength >= local.Index + local.ReplacementLength)) { // remote del at lower index, remote del fully extends // over local del // --> shorten remote by local length // make local no-op remote.ReplacementLength = remote.ReplacementLength - local.ReplacementLength; // TODO: verify this... local.Index = remote.Index; local.ReplacementLength = noOpLength; } else if (remote.Index == local.Index) { if (remote.Index + remote.ReplacementLength < local.Index + local.ReplacementLength) { // start indeces are equal, remote is shorter // --> make remote no-op remote.ReplacementLength = noOpLength; // --> shorten local to only delete // non-overlapping region local.ReplacementLength = local.ReplacementLength - remote.ReplacementLength; } else if (remote.Index + remote.ReplacementLength == local.Index + local.ReplacementLength) { // same endIndex, same deletion // --> make remote op be no-op remote.ReplacementLength = noOpLength; // --> make local op appear as no-op local.ReplacementLength = noOpLength; } else if (remote.Index + remote.ReplacementLength > local.Index + local.ReplacementLength) { // remote del extends over local del // --> shorten remote del by length of local del, // index/offset does not need to be updated remote.ReplacementLength = remote.ReplacementLength - local.ReplacementLength; // --> make local del appear as no-op local.ReplacementLength = noOpLength; } } else if (remote.Index > local.Index) { if (remote.Index > local.Index + local.ReplacementLength) { // move remote delete left by length of local deletion remote.Index = remote.Index - local.ReplacementLength; } else if (remote.Index + remote.ReplacementLength < local.Index + local.ReplacementLength) { //remote is fully contained in/overlapping with local del // --> remote is no-op remote.ReplacementLength = noOpLength; // --> local needs to be shortened by length of remote local.ReplacementLength = local.ReplacementLength - remote.ReplacementLength; } else if (remote.Index < local.Index + local.ReplacementLength) { // remote del starts within local del, but extends further // --> shorten remote by overlap and move left // to index of local del remote.ReplacementLength = remote.ReplacementLength - (local.Index + local.ReplacementLength) - remote.Index; } } } else if (localOp is OpInsertMessage) { var local = localOp as OpInsertMessage; if (remote.Index < localOp.Index) { if (remote.Index + remote.ReplacementLength < local.Index) { // remote remains unchanged, deletion happens fully // before local insertion // local insert needs to be moved left by full length of deletion local.Index -= remote.ReplacementLength; } else if (remote.Index + remote.ReplacementLength >= local.Index) { // TODO ??? } else if (remote.Index >= local.Index) { // remote del needs to be moved right by full length of insertion remote.Index += local.Content.Length; } } } } else if (remoteOp is OpInsertMessage) { var remote = remoteOp as OpInsertMessage; if (localOp is OpInsertMessage) { var local = localOp as OpInsertMessage; if (remote.Index < local.Index) { local.Index += remote.Content.Length; } else if (remote.Index == local.Index) { var compare = local.User.CompareTo(remote.User); if (compare < 0) { remote.Index += local.Content.Length; } else { local.Index += remote.Content.Length; } } else if (remote.Index > local.Index) { remote.Index += local.Content.Length; } } else if (localOp is OpDeleteMessage) { var local = localOp as OpDeleteMessage; if (remote.Index <= localOp.Index) { localOp.Index += remote.Content.Length; } else if (remote.Index > localOp.Index) { if (remote.Index > local.Index + local.ReplacementLength) { remote.Index -= local.ReplacementLength; } else if (remote.Index <= local.Index + local.ReplacementLength) { // TODO ??? } } } } }