A checkpoint that allows tracking changes to a TextDocument. Use TextDocument.CreateSnapshot(out ChangeTrackingCheckpoint) to create a checkpoint.
예제 #1
0
 /// <summary>
 /// Compares the age of this checkpoint to the other checkpoint.
 /// </summary>
 /// <remarks>This method is thread-safe.</remarks>
 /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
 /// <returns>-1 if this checkpoint is older than <paramref name="other"/>.
 /// 0 if <c>this</c>==<paramref name="other"/>.
 /// 1 if this checkpoint is newer than <paramref name="other"/>.</returns>
 public int CompareAge(ChangeTrackingCheckpoint other)
 {
     if (other == null)
         throw new ArgumentNullException(nameof(other));
     if (other.documentIdentifier != documentIdentifier)
         throw new ArgumentException("Checkpoints do not belong to the same document.");
     // We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1.
     // This is guaranteed on x86 because so many checkpoints don't fit into memory.
     return Math.Sign(unchecked(id - other.id));
 }
예제 #2
0
		void DoReplace(int offset, int length, string newText, OffsetChangeMap offsetChangeMap)
		{
			if (length == 0 && newText.Length == 0)
				return;
			
			// trying to replace a single character in 'Normal' mode?
			// for single characters, 'CharacterReplace' mode is equivalent, but more performant
			// (we don't have to touch the anchorTree at all in 'CharacterReplace' mode)
			if (length == 1 && newText.Length == 1 && offsetChangeMap == null)
				offsetChangeMap = OffsetChangeMap.Empty;
			
			string removedText = rope.ToString(offset, length);
			DocumentChangeEventArgs args = new DocumentChangeEventArgs(offset, removedText, newText, offsetChangeMap);
			
			// fire DocumentChanging event
			if (Changing != null)
				Changing(this, args);
			
			cachedText = null; // reset cache of complete document text
			fireTextChanged = true;
			DelayedEvents delayedEvents = new DelayedEvents();
			
			lock (lockObject) {
				// create linked list of checkpoints, if required
				if (currentCheckpoint != null) {
					currentCheckpoint = currentCheckpoint.Append(args);
				}
				
				// now update the textBuffer and lineTree
				if (offset == 0 && length == rope.Length) {
					// optimize replacing the whole document
					rope.Clear();
					rope.InsertText(0, newText);
					lineManager.Rebuild();
				} else {
					rope.RemoveRange(offset, length);
					lineManager.Remove(offset, length);
					#if DEBUG
					lineTree.CheckProperties();
					#endif
					rope.InsertText(offset, newText);
					lineManager.Insert(offset, newText);
					#if DEBUG
					lineTree.CheckProperties();
					#endif
				}
			}
			
			// update text anchors
			if (offsetChangeMap == null) {
				anchorTree.HandleTextChange(args.CreateSingleChangeMapEntry(), delayedEvents);
			} else {
				foreach (OffsetChangeMapEntry entry in offsetChangeMap) {
					anchorTree.HandleTextChange(entry, delayedEvents);
				}
			}
			
			// raise delayed events after our data structures are consistent again
			delayedEvents.RaiseEvents();
			
			// fire DocumentChanged event
			if (Changed != null)
				Changed(this, args);
		}
예제 #3
0
		internal ChangeTrackingCheckpoint CreateChangeTrackingCheckpoint()
		{
			lock (lockObject) {
				if (currentCheckpoint == null)
					currentCheckpoint = new ChangeTrackingCheckpoint(lockObject);
				return currentCheckpoint;
			}
		}
예제 #4
0
		public ITextSource CreateSnapshot(out ChangeTrackingCheckpoint checkpoint)
		{
			lock (lockObject) {
				if (currentCheckpoint == null)
					currentCheckpoint = new ChangeTrackingCheckpoint(lockObject);
				checkpoint = currentCheckpoint;
				return new RopeTextSource(rope.Clone());
			}
		}
예제 #5
0
 /// <summary>
 /// Gets whether this checkpoint belongs to the same document as the other checkpoint.
 /// </summary>
 public bool BelongsToSameDocumentAs(ChangeTrackingCheckpoint other)
 {
     if (other == null)
         throw new ArgumentNullException("other");
     return documentIdentifier == other.documentIdentifier;
 }
예제 #6
0
 IEnumerable<DocumentChangeEventArgs> GetForwardChanges(ChangeTrackingCheckpoint other)
 {
     // Return changes from this(exclusive) to other(inclusive).
     ChangeTrackingCheckpoint node = this;
     do {
         node = node.next;
         yield return node.value;
     } while (node != other);
 }
예제 #7
0
 internal ChangeTrackingCheckpoint Append(DocumentChangeEventArgs change)
 {
     Debug.Assert(this.next == null);
     this.next = new ChangeTrackingCheckpoint(this.documentIdentifier, change, unchecked( this.id + 1 ));
     return this.next;
 }
예제 #8
0
 /// <summary>
 /// Calculates where the offset has moved in the other buffer version.
 /// </summary>
 /// <remarks>This method is thread-safe.</remarks>
 /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
 public int MoveOffsetTo(ChangeTrackingCheckpoint other, int oldOffset, AnchorMovementType movement)
 {
     int offset = oldOffset;
     foreach (DocumentChangeEventArgs e in GetChangesTo(other)) {
         offset = e.GetNewOffset(offset, movement);
     }
     return offset;
 }
예제 #9
0
 /// <summary>
 /// Gets the changes from this checkpoint to the other checkpoint.
 /// If 'other' is older than this checkpoint, reverse changes are calculated.
 /// </summary>
 /// <remarks>This method is thread-safe.</remarks>
 /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
 public IEnumerable<DocumentChangeEventArgs> GetChangesTo(ChangeTrackingCheckpoint other)
 {
     int result = CompareAge(other);
     if (result < 0)
         return GetForwardChanges(other);
     else if (result > 0)
         return other.GetForwardChanges(this).Reverse().Select(change => change.Invert());
     else
         return Empty<DocumentChangeEventArgs>.Array;
 }
			public SnapshotVersion(ChangeTrackingCheckpoint checkpoint)
			{
				Debug.Assert(checkpoint != null);
				this.checkpoint = checkpoint;
			}
			public Snapshot(ITextSource textSource, ChangeTrackingCheckpoint checkpoint)
				: base(textSource)
			{
				this.version = new SnapshotVersion(checkpoint);
			}
예제 #12
0
        void DoReplace(int offset, int length, string newText, OffsetChangeMap offsetChangeMap)
        {
            if (length == 0 && newText.Length == 0)
            {
                return;
            }

            // trying to replace a single character in 'Normal' mode?
            // for single characters, 'CharacterReplace' mode is equivalent, but more performant
            // (we don't have to touch the anchorTree at all in 'CharacterReplace' mode)
            if (length == 1 && newText.Length == 1 && offsetChangeMap == null)
            {
                offsetChangeMap = OffsetChangeMap.Empty;
            }

            string removedText           = rope.ToString(offset, length);
            DocumentChangeEventArgs args = new DocumentChangeEventArgs(offset, removedText, newText, offsetChangeMap);

            // fire DocumentChanging event
            if (Changing != null)
            {
                Changing(this, args);
            }

            cachedText      = null;        // reset cache of complete document text
            fireTextChanged = true;
            DelayedEvents delayedEvents = new DelayedEvents();

            lock (lockObject) {
                // create linked list of checkpoints, if required
                if (currentCheckpoint != null)
                {
                    currentCheckpoint = currentCheckpoint.Append(args);
                }

                // now update the textBuffer and lineTree
                if (offset == 0 && length == rope.Length)
                {
                    // optimize replacing the whole document
                    rope.Clear();
                    rope.InsertText(0, newText);
                    lineManager.Rebuild();
                }
                else
                {
                    rope.RemoveRange(offset, length);
                    lineManager.Remove(offset, length);
                                        #if DEBUG
                    lineTree.CheckProperties();
                                        #endif
                    rope.InsertText(offset, newText);
                    lineManager.Insert(offset, newText);
                                        #if DEBUG
                    lineTree.CheckProperties();
                                        #endif
                }
            }

            // update text anchors
            if (offsetChangeMap == null)
            {
                anchorTree.HandleTextChange(args.CreateSingleChangeMapEntry(), delayedEvents);
            }
            else
            {
                foreach (OffsetChangeMapEntry entry in offsetChangeMap)
                {
                    anchorTree.HandleTextChange(entry, delayedEvents);
                }
            }

            // raise delayed events after our data structures are consistent again
            delayedEvents.RaiseEvents();

            // fire DocumentChanged event
            if (Changed != null)
            {
                Changed(this, args);
            }
        }
 internal ChangeTrackingCheckpoint Append(DocumentChangeEventArgs change)
 {
     Debug.Assert(this.next == null);
     this.next = new ChangeTrackingCheckpoint(this.documentIdentifier, change, unchecked (this.id + 1));
     return(this.next);
 }