Browse Source
Usage: TextAnchor anchor = document.GetLineSegment(lineNumber).CreateAnchor(columnNumber); anchor.LineNumber and anchor.ColumnNumber are updated when text is inserted/removed in the document. anchor.IsDeleted will be true when the text location containing the anchor was removed. LineSegment gets an IsDeleted property, so a LineSegment reference can be used as anchor for a line. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2683 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
12 changed files with 541 additions and 159 deletions
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.TextEditor.Document |
||||
{ |
||||
/// <summary>
|
||||
/// Description of TextAnchor.
|
||||
/// </summary>
|
||||
public sealed class TextAnchor |
||||
{ |
||||
static Exception AnchorDeletedError() |
||||
{ |
||||
return new InvalidOperationException("The text containing the anchor was deleted"); |
||||
} |
||||
|
||||
LineSegment lineSegment; |
||||
int columnNumber; |
||||
|
||||
public LineSegment Line { |
||||
get { |
||||
if (lineSegment == null) throw AnchorDeletedError(); |
||||
return lineSegment; |
||||
} |
||||
internal set { |
||||
lineSegment = value; |
||||
} |
||||
} |
||||
|
||||
public bool IsDeleted { |
||||
get { |
||||
return lineSegment == null; |
||||
} |
||||
} |
||||
|
||||
public int LineNumber { |
||||
get { |
||||
return this.Line.LineNumber; |
||||
} |
||||
} |
||||
|
||||
public int ColumnNumber { |
||||
get { |
||||
if (lineSegment == null) throw AnchorDeletedError(); |
||||
return columnNumber; |
||||
} |
||||
internal set { |
||||
columnNumber = value; |
||||
} |
||||
} |
||||
|
||||
public TextLocation Location { |
||||
get { |
||||
return new TextLocation(this.ColumnNumber, this.LineNumber); |
||||
} |
||||
} |
||||
|
||||
public int Offset { |
||||
get { |
||||
return this.Line.Offset + columnNumber; |
||||
} |
||||
} |
||||
|
||||
internal void Deleted() |
||||
{ |
||||
lineSegment = null; |
||||
} |
||||
|
||||
internal TextAnchor(LineSegment lineSegment, int columnNumber) |
||||
{ |
||||
this.lineSegment = lineSegment; |
||||
this.columnNumber = columnNumber; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
if (this.IsDeleted) |
||||
return "[TextAnchor (deleted)]"; |
||||
else |
||||
return "[TextAnchor " + this.Location.ToString() + "]"; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,136 @@
@@ -0,0 +1,136 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace ICSharpCode.TextEditor.Util |
||||
{ |
||||
/// <summary>
|
||||
/// A collection that does not allows its elements to be garbage-collected (unless there are other
|
||||
/// references to the elements). Elements will disappear from the collection when they are
|
||||
/// garbage-collected.
|
||||
///
|
||||
/// The WeakCollection is not thread-safe, not even for read-only access!
|
||||
/// No methods may be called on the WeakCollection while it is enumerated, not even a Contains or
|
||||
/// creating a second enumerator.
|
||||
/// The WeakCollection does not preserve any order among its contents; the ordering may be different each
|
||||
/// time the collection is enumerated.
|
||||
///
|
||||
/// Since items may disappear at any time when they are garbage collected, this class
|
||||
/// cannot provide a useful implementation for Count and thus cannot implement the ICollection interface.
|
||||
/// </summary>
|
||||
public class WeakCollection<T> : IEnumerable<T> where T : class |
||||
{ |
||||
readonly List<WeakReference> innerList = new List<WeakReference>(); |
||||
|
||||
/// <summary>
|
||||
/// Adds an element to the collection. Runtime: O(n).
|
||||
/// </summary>
|
||||
public void Add(T item) |
||||
{ |
||||
if (item == null) |
||||
throw new ArgumentNullException("item"); |
||||
CheckNoEnumerator(); |
||||
if (innerList.Count == innerList.Capacity || (innerList.Count % 32) == 31) |
||||
innerList.RemoveAll(delegate(WeakReference r) { return !r.IsAlive; }); |
||||
innerList.Add(new WeakReference(item)); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Removes all elements from the collection. Runtime: O(n).
|
||||
/// </summary>
|
||||
public void Clear() |
||||
{ |
||||
innerList.Clear(); |
||||
CheckNoEnumerator(); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Checks if the collection contains an item. Runtime: O(n).
|
||||
/// </summary>
|
||||
public bool Contains(T item) |
||||
{ |
||||
if (item == null) |
||||
throw new ArgumentNullException("item"); |
||||
CheckNoEnumerator(); |
||||
foreach (T element in this) { |
||||
if (item.Equals(element)) |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Removes an element from the collection. Returns true if the item is found and removed,
|
||||
/// false when the item is not found.
|
||||
/// Runtime: O(n).
|
||||
/// </summary>
|
||||
public bool Remove(T item) |
||||
{ |
||||
if (item == null) |
||||
throw new ArgumentNullException("item"); |
||||
CheckNoEnumerator(); |
||||
for (int i = 0; i < innerList.Count;) { |
||||
T element = (T)innerList[i].Target; |
||||
if (element == null) { |
||||
RemoveAt(i); |
||||
} else if (element == item) { |
||||
RemoveAt(i); |
||||
return true; |
||||
} else { |
||||
i++; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
void RemoveAt(int i) |
||||
{ |
||||
int lastIndex = innerList.Count - 1; |
||||
innerList[i] = innerList[lastIndex]; |
||||
innerList.RemoveAt(lastIndex); |
||||
} |
||||
|
||||
bool hasEnumerator; |
||||
|
||||
void CheckNoEnumerator() |
||||
{ |
||||
if (hasEnumerator) |
||||
throw new InvalidOperationException("The WeakCollection is already being enumerated, it cannot be modified at the same time. Ensure you dispose the first enumerator before modifying the WeakCollection."); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Enumerates the collection.
|
||||
/// Each MoveNext() call on the enumerator is O(1), thus the enumeration is O(n).
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator() |
||||
{ |
||||
if (hasEnumerator) |
||||
throw new InvalidOperationException("The WeakCollection is already being enumerated, it cannot be enumerated twice at the same time. Ensure you dispose the first enumerator before using another enumerator."); |
||||
try { |
||||
hasEnumerator = true; |
||||
for (int i = 0; i < innerList.Count;) { |
||||
T element = (T)innerList[i].Target; |
||||
if (element == null) { |
||||
RemoveAt(i); |
||||
} else { |
||||
yield return element; |
||||
i++; |
||||
} |
||||
} |
||||
} finally { |
||||
hasEnumerator = false; |
||||
} |
||||
} |
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||
{ |
||||
return GetEnumerator(); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue