Document
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