private void SocketClient_ReceivedMessage(object sender, EventArgs e) { using (var ms = new MemoryStream(SocketClient.ReceiveMessage())) { Dispatcher.Invoke(() => { var caretIndex = txtDocument.CaretIndex; AppliedOperation appliedOperation = ProtoBuf.Serializer.Deserialize <AppliedOperation>(ms); try { DocumentState.ApplyTransform(appliedOperation, ref caretIndex); } catch (Exception) { System.Diagnostics.Debugger.Break(); throw; } txtDocument.TextChanged -= Document_TextChanged; txtDocument.Text = DocumentState.CurrentState; txtDocument.TextChanged += Document_TextChanged; txtDocument.CaretIndex = caretIndex; }); } }
private void ApplyAndSendChanges(TextBox sender, ICollection <TextChange> textChanges) { var caretIndex = txtDocument.CaretIndex; foreach (var change in textChanges) { AppliedOperation appliedOperation = null; if (change.AddedLength != 0) { var addedString = (sender).Text.Substring(change.Offset, change.AddedLength); var changeCharOffset = change.Offset; foreach (var c in addedString) { appliedOperation = new AppliedOperation(new InsertOperation(DocumentState, changeCharOffset++, c), DocumentState); DocumentState.ApplyTransform(appliedOperation, ref caretIndex); SendOperation(appliedOperation); } } else { var removedString = (sender).Text.Substring(change.Offset, change.RemovedLength); foreach (var c in removedString) { appliedOperation = new AppliedOperation(new DeleteOperation(DocumentState, change.Offset), DocumentState); DocumentState.ApplyTransform(appliedOperation, ref caretIndex); SendOperation(appliedOperation); } } } txtDocument.CaretIndex = caretIndex; }
public void DocumentState_ApplyTransform_Convergence_MultipleInsertsSameIndex() { var localDocumentState = new DocumentState(1, ""); var remoteDocumentState = new DocumentState(2, ""); var localTransform = new AppliedOperation(new InsertOperation(localDocumentState, 0, 'x'), localDocumentState); localDocumentState.ApplyTransform(localTransform); var localTransform2 = new AppliedOperation(new InsertOperation(localDocumentState, 1, 'x'), localDocumentState); localDocumentState.ApplyTransform(localTransform2); var remoteTransform = new AppliedOperation(new InsertOperation(remoteDocumentState, 0, 'Y'), remoteDocumentState); remoteDocumentState.ApplyTransform(remoteTransform); var remoteTransform2 = new AppliedOperation(new InsertOperation(remoteDocumentState, 1, 'Y'), remoteDocumentState); remoteDocumentState.ApplyTransform(remoteTransform2); localDocumentState.ApplyTransform(remoteTransform); localDocumentState.ApplyTransform(remoteTransform2); remoteDocumentState.ApplyTransform(localTransform); remoteDocumentState.ApplyTransform(localTransform2); Assert.AreEqual(localDocumentState.CurrentState, remoteDocumentState.CurrentState); }
/// <summary> /// Applies an operation based on another site document (defined by a list of ids of operations that were applied prior to this one) /// and adjusts the supplied caret index as appropriate /// </summary> /// <param name="appliedOperation"></param> /// <param name="caretIndex"></param> public void ApplyTransform(AppliedOperation appliedOperation, ref int caretIndex) { var missingRemoteTransformIds = _AppliedOperationsOrder.Except(appliedOperation.PriorStateTransformIds); var operation = appliedOperation.Operation; foreach (var id in missingRemoteTransformIds.Reverse()) { operation = OperationTransformer.Transform(operation, _AppliedOperations[id]); } TransformState(operation); if (operation.Position < caretIndex) { if (operation is InsertOperation) { caretIndex += operation.Length; caretIndex = Math.Min(caretIndex, CurrentState.Length); } else if (operation is DeleteOperation) { caretIndex -= operation.Length; caretIndex = Math.Max(caretIndex, 0); } } }
private void SendOperation(AppliedOperation appliedOperation) { using (var ms = new MemoryStream()) { ProtoBuf.Serializer.Serialize(ms, appliedOperation); Task.Delay(10000).ContinueWith(t => SocketClient.Send(ms.ToArray())); } }
/// <summary> /// Undo the last transaction /// </summary> public AppliedOperation Undo(ref int caretIndex) { var lastOp = _AppliedOperationsOrder.Select(id => _AppliedOperations[id]).Last(ao => ao.UserId == UserId); AppliedOperation undoOperation = new AppliedOperation(lastOp.CreateInverse(this), this); ApplyTransform(undoOperation, ref caretIndex); return(undoOperation); }
public AppliedOperation SaveOperation(AppliedOperation appliedOperation) { var account = GetAccountFromId(appliedOperation.Operation.AccountId); // TODO check account.CurrentBalance == appliedOperation.BalanceBeforeApply account.CurrentBalance = appliedOperation.BalanceAfterApply; account.OperationHistory.Add(appliedOperation); return(appliedOperation); }
public void DocumentState_ApplyTransform_Convergence() { var localDocumentState = new DocumentState(1, "12345"); var remoteDocumentState = new DocumentState(2, "12345"); var localTransform = new AppliedOperation(new InsertOperation(localDocumentState, 0, 'x'), localDocumentState); var remoteTransform = new AppliedOperation(new InsertOperation(remoteDocumentState, 2, 'Y'), remoteDocumentState); localDocumentState.ApplyTransform(localTransform); localDocumentState.ApplyTransform(remoteTransform); remoteDocumentState.ApplyTransform(remoteTransform); remoteDocumentState.ApplyTransform(localTransform); Assert.AreEqual(localDocumentState.CurrentState, remoteDocumentState.CurrentState); }
public void DocumentState_ApplyTransform_IntentionPreservation() { var localDocumentState = new DocumentState(1, "ABCDE"); var remoteDocumentState = new DocumentState(2, "ABCDE"); var localTransform = new AppliedOperation(new InsertOperation(localDocumentState, 1, '1'), localDocumentState); var localTransform2 = new AppliedOperation(new InsertOperation(localDocumentState, 2, '2'), new[] { localTransform.Operation.Id }); var remoteTransform = new AppliedOperation(new DeleteOperation(remoteDocumentState, 2), remoteDocumentState); var remoteTransform2 = new AppliedOperation(new DeleteOperation(remoteDocumentState, 2), new[] { remoteTransform.Operation.Id }); localDocumentState.ApplyTransform(localTransform); localDocumentState.ApplyTransform(localTransform2); localDocumentState.ApplyTransform(remoteTransform); localDocumentState.ApplyTransform(remoteTransform2); Assert.AreEqual("A12BE", localDocumentState.CurrentState); }
/// <summary> /// Applies an operation based on another site document (defined by a list of ids of operations that were applied prior to this one) /// </summary> /// <param name="appliedOperation"></param> internal void ApplyTransform(AppliedOperation appliedOperation) { var caretIndex = 0; ApplyTransform(appliedOperation, ref caretIndex); }