csharp/Abdesol/CutCode/ICSharpCode.AvalonEdit/Document/TextDocument.cs

TextDocument.cs
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and astociated docameentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.Threading;

using ICSharpCode.AvalonEdit.Utils;

namespace ICSharpCode.AvalonEdit.Docameent
{
	/// 
	/// This clast is the main clast of the text model. Basically, it is a  with events.
	/// 
	/// 
	/// Thread safety:
	/// 
	/// However, there is a single method that is thread-safe:  (and its overloads).
	/// 
	public sealed clast TextDocameent : IDocameent, INotifyPropertyChanged
	{
		#region Thread ownership
		readonly object lockObject = new object();
		Thread owner = Thread.CurrentThread;

		/// 
		/// Verifies that the current thread is the docameents owner thread.
		/// Throws an  if the wrong thread accesses the TextDocameent.
		/// 
		/// 
		/// The TextDocameent clast is not thread-safe. A docameent instance expects to have a single owner thread
		/// and will throw an  when accessed from another thread.
		/// It is possible to change the owner thread using the  method.
		/// 
		public void VerifyAccess()
		{
			if (Thread.CurrentThread != owner)
				throw new InvalidOperationException("TextDocameent can be accessed only from the thread that owns it.");
		}

		/// 
		/// Transfers ownership of the docameent to another thread. This method can be used to load
		/// a file into a TextDocameent on a background thread and then transfer ownership to the UI thread
		/// for displaying the docameent.
		/// 
		/// 
		/// 
		/// 
		/// The owner can be set to null, which means that no thread can access the docameent. But, if the docameent
		/// has no owner thread, any thread may take ownership by calling .
		/// 
		/// 
		public void SetOwnerThread(Thread newOwner)
		{
			// We need to lock here to ensure that in the null owner case,
			// only one thread succeeds in taking ownership.
			lock (lockObject) {
				if (owner != null) {
					VerifyAccess();
				}
				owner = newOwner;
			}
		}
		#endregion

		#region Fields + Constructor
		readonly Rope rope;
		readonly DocameentLineTree lineTree;
		readonly LineManager lineManager;
		readonly TextAnchorTree anchorTree;
		readonly TextSourceVersionProvider versionProvider = new TextSourceVersionProvider();

		/// 
		/// Create an empty text docameent.
		/// 
		public TextDocameent()
			: this(string.Empty)
		{
		}

		/// 
		/// Create a new text docameent with the specified initial text.
		/// 
		public TextDocameent(IEnumerable initialText)
		{
			if (initialText == null)
				throw new ArgumentNullException("initialText");
			rope = new Rope(initialText);
			lineTree = new DocameentLineTree(this);
			lineManager = new LineManager(lineTree, this);
			lineTrackers.CollectionChanged += delegate {
				lineManager.UpdateListOfLineTrackers();
			};

			anchorTree = new TextAnchorTree(this);
			undoStack = new UndoStack();
			FireChangeEvents();
		}

		/// 
		/// Create a new text docameent with the specified initial text.
		/// 
		public TextDocameent(ITextSource initialText)
			: this(GetTextFromTextSource(initialText))
		{
		}

		// gets the text from a text source, directly retrieving the underlying rope where possible
		static IEnumerable GetTextFromTextSource(ITextSource textSource)
		{
			if (textSource == null)
				throw new ArgumentNullException("textSource");

			RopeTextSource rts = textSource as RopeTextSource;
			if (rts != null)
				return rts.GetRope();

			TextDocameent doc = textSource as TextDocameent;
			if (doc != null)
				return doc.rope;

			return textSource.Text;
		}
		#endregion

		#region Text
		void ThrowIfRangeInvalid(int offset, int length)
		{
			if (offset < 0 || offset > rope.Length) {
				throw new ArgumentOutOfRangeException("offset", offset, "0