Browse Source

Add TextAnchor to text editor.

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-cef0b8235c61
shortcuts
Daniel Grunwald 19 years ago
parent
commit
1eabdccf12
  1. 2
      src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj
  2. 10
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/DefaultTextEditorProperties.cs
  3. 142
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs
  4. 23
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/DefaultLineManager.cs
  5. 242
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegment.cs
  6. 6
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegmentTree.cs
  7. 88
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextAnchor.cs
  8. 4
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs
  9. 6
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextArea.cs
  10. 6
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs
  11. 35
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlBase.cs
  12. 136
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Util/WeakCollection.cs

2
src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj

@ -60,6 +60,7 @@ @@ -60,6 +60,7 @@
<Compile Include="Src\Document\IDocument.cs" />
<Compile Include="Src\Document\ISegment.cs" />
<Compile Include="Src\Document\LineManager\LineSegmentTree.cs" />
<Compile Include="Src\Document\TextAnchor.cs" />
<Compile Include="Src\Document\TextLocation.cs" />
<Compile Include="Src\Document\TextUtilities.cs" />
<Compile Include="Src\Document\BookmarkManager\BookmarkManager.cs" />
@ -207,6 +208,7 @@ @@ -207,6 +208,7 @@
<Link>Configuration\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Src\Document\HighlightingStrategy\SpanStack.cs" />
<Compile Include="Src\Util\WeakCollection.cs" />
<EmbeddedResource Include="Resources\Patch-Mode.xshd" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />

10
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/DefaultTextEditorProperties.cs

@ -40,13 +40,13 @@ namespace ICSharpCode.TextEditor.Document @@ -40,13 +40,13 @@ namespace ICSharpCode.TextEditor.Document
bool showMatchingBracket = true;
bool showLineNumbers = true;
bool showSpaces = true;
bool showTabs = true;
bool showEOLMarker = true;
bool showSpaces = false;
bool showTabs = false;
bool showEOLMarker = false;
bool showInvalidLines = true;
bool showInvalidLines = false;
bool isIconBarVisible = true;
bool isIconBarVisible = false;
bool enableFolding = true;
bool showHorizontalRuler = false;
bool showVerticalRuler = true;

142
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
@ -620,9 +621,9 @@ namespace ICSharpCode.TextEditor.Document @@ -620,9 +621,9 @@ namespace ICSharpCode.TextEditor.Document
// Check for SPAN ENDs
if (inSpan) {
if (activeSpan.End != null && activeSpan.End.Length > 0) {
if (currentLine.MatchExpr(activeSpan.End, i, document, activeSpan.IgnoreCase)) {
if (MatchExpr(currentLine, activeSpan.End, i, document, activeSpan.IgnoreCase)) {
PushCurWord(document, ref markNext, words);
string regex = currentLine.GetRegString(activeSpan.End, i, document);
string regex = GetRegString(currentLine, activeSpan.End, i, document);
currentLength += regex.Length;
words.Add(new TextWord(document, currentLine, currentOffset, currentLength, activeSpan.EndColor, false));
currentOffset += currentLength;
@ -640,9 +641,9 @@ namespace ICSharpCode.TextEditor.Document @@ -640,9 +641,9 @@ namespace ICSharpCode.TextEditor.Document
foreach (Span span in activeRuleSet.Spans) {
if ((!span.IsBeginSingleWord || currentLength == 0)
&& (!span.IsBeginStartOfLine.HasValue || span.IsBeginStartOfLine.Value == (currentLength == 0 && words.TrueForAll(delegate(TextWord textWord) { return textWord.Type != TextWordType.Word; })))
&& currentLine.MatchExpr(span.Begin, i, document, activeRuleSet.IgnoreCase)) {
&& MatchExpr(currentLine, span.Begin, i, document, activeRuleSet.IgnoreCase)) {
PushCurWord(document, ref markNext, words);
string regex = currentLine.GetRegString(span.Begin, i, document);
string regex = GetRegString(currentLine, span.Begin, i, document);
if (!OverrideSpan(regex, document, words, span, ref i)) {
currentLength += regex.Length;
@ -768,5 +769,138 @@ namespace ICSharpCode.TextEditor.Document @@ -768,5 +769,138 @@ namespace ICSharpCode.TextEditor.Document
currentLength = 0;
}
}
#region Matching
/// <summary>
/// get the string, which matches the regular expression expr,
/// in string s2 at index
/// </summary>
static string GetRegString(LineSegment lineSegment, char[] expr, int index, IDocument document)
{
int j = 0;
StringBuilder regexpr = new StringBuilder();
for (int i = 0; i < expr.Length; ++i, ++j) {
if (index + j >= lineSegment.Length)
break;
switch (expr[i]) {
case '@': // "special" meaning
++i;
switch (expr[i]) {
case '!': // don't match the following expression
StringBuilder whatmatch = new StringBuilder();
++i;
while (i < expr.Length && expr[i] != '@') {
whatmatch.Append(expr[i++]);
}
break;
case '@': // matches @
regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j));
break;
}
break;
default:
if (expr[i] != document.GetCharAt(lineSegment.Offset + index + j)) {
return regexpr.ToString();
}
regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j));
break;
}
}
return regexpr.ToString();
}
/// <summary>
/// returns true, if the get the string s2 at index matches the expression expr
/// </summary>
static bool MatchExpr(LineSegment lineSegment, char[] expr, int index, IDocument document, bool ignoreCase)
{
for (int i = 0, j = 0; i < expr.Length; ++i, ++j) {
switch (expr[i]) {
case '@': // "special" meaning
++i;
if (i < expr.Length) {
switch (expr[i]) {
case 'C': // match whitespace or punctuation
if (index + j == lineSegment.Offset || index + j >= lineSegment.Offset + lineSegment.Length) {
// nothing (EOL or SOL)
} else {
char ch = document.GetCharAt(lineSegment.Offset + index + j);
if (!Char.IsWhiteSpace(ch) && !Char.IsPunctuation(ch)) {
return false;
}
}
break;
case '!': // don't match the following expression
{
StringBuilder whatmatch = new StringBuilder();
++i;
while (i < expr.Length && expr[i] != '@') {
whatmatch.Append(expr[i++]);
}
if (lineSegment.Offset + index + j + whatmatch.Length < document.TextLength) {
int k = 0;
for (; k < whatmatch.Length; ++k) {
char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index + j + k)) : document.GetCharAt(lineSegment.Offset + index + j + k);
char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
if (docChar != spanChar) {
break;
}
}
if (k >= whatmatch.Length) {
return false;
}
}
// --j;
break;
}
case '-': // don't match the expression before
{
StringBuilder whatmatch = new StringBuilder();
++i;
while (i < expr.Length && expr[i] != '@') {
whatmatch.Append(expr[i++]);
}
if (index - whatmatch.Length >= 0) {
int k = 0;
for (; k < whatmatch.Length; ++k) {
char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k)) : document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k);
char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
if (docChar != spanChar)
break;
}
if (k >= whatmatch.Length) {
return false;
}
}
// --j;
break;
}
case '@': // matches @
if (index + j >= lineSegment.Length || '@' != document.GetCharAt(lineSegment.Offset + index + j)) {
return false;
}
break;
}
}
break;
default:
{
if (index + j >= lineSegment.Length) {
return false;
}
char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index + j)) : document.GetCharAt(lineSegment.Offset + index + j);
char spanChar = ignoreCase ? Char.ToUpperInvariant(expr[i]) : expr[i];
if (docChar != spanChar) {
return false;
}
break;
}
}
}
return true;
}
#endregion
}
}

23
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/DefaultLineManager.cs

@ -77,7 +77,7 @@ namespace ICSharpCode.TextEditor.Document @@ -77,7 +77,7 @@ namespace ICSharpCode.TextEditor.Document
public void Replace(int offset, int length, string text)
{
// Console.WriteLine("Replace offset="+offset+" length="+length+" text.Length="+text.Length);
Console.WriteLine("Replace offset="+offset+" length="+length+" text.Length="+text.Length);
int lineStart = GetLineNumberForOffset(offset);
int oldNumberOfLines = this.TotalNumberOfLines;
RemoveInternal(offset, length);
@ -106,6 +106,7 @@ namespace ICSharpCode.TextEditor.Document @@ -106,6 +106,7 @@ namespace ICSharpCode.TextEditor.Document
int startSegmentOffset = startSegment.Offset;
if (offset + length < startSegmentOffset + startSegment.TotalLength) {
// just removing a part of this line segment
startSegment.RemovedLinePart(offset - startSegmentOffset, length);
SetSegmentLength(startSegment, startSegment.TotalLength - length);
return;
}
@ -113,6 +114,7 @@ namespace ICSharpCode.TextEditor.Document @@ -113,6 +114,7 @@ namespace ICSharpCode.TextEditor.Document
// possibly remove lines in between if multiple delimiters were deleted
int charactersRemovedInStartLine = startSegmentOffset + startSegment.TotalLength - offset;
Debug.Assert(charactersRemovedInStartLine > 0);
startSegment.RemovedLinePart(offset - startSegmentOffset, charactersRemovedInStartLine);
LineSegment endSegment = lineCollection.GetByOffset(offset + length);
@ -124,6 +126,8 @@ namespace ICSharpCode.TextEditor.Document @@ -124,6 +126,8 @@ namespace ICSharpCode.TextEditor.Document
}
int endSegmentOffset = endSegment.Offset;
int charactersLeftInEndLine = endSegmentOffset + endSegment.TotalLength - (offset + length);
endSegment.RemovedLinePart(0, endSegment.TotalLength - charactersLeftInEndLine);
startSegment.MergedWith(endSegment, offset - startSegmentOffset);
SetSegmentLength(startSegment, startSegment.TotalLength - charactersRemovedInStartLine + charactersLeftInEndLine);
startSegment.DelimiterLength = endSegment.DelimiterLength;
// remove all segments between startSegment (excl.) and endSegment (incl.)
@ -132,6 +136,7 @@ namespace ICSharpCode.TextEditor.Document @@ -132,6 +136,7 @@ namespace ICSharpCode.TextEditor.Document
do {
segmentToRemove = it.Current;
it.MoveNext();
segmentToRemove.Deleted();
lineCollection.RemoveSegment(segmentToRemove);
} while (segmentToRemove != endSegment);
}
@ -139,9 +144,17 @@ namespace ICSharpCode.TextEditor.Document @@ -139,9 +144,17 @@ namespace ICSharpCode.TextEditor.Document
void InsertInternal(int offset, string text)
{
LineSegment segment = lineCollection.GetByOffset(offset);
DelimiterSegment ds = NextDelimiter(text, 0);
if (ds == null) {
// no newline is being inserted, all text is inserted in a single line
segment.InsertedLinePart(offset - segment.Offset, text.Length);
SetSegmentLength(segment, segment.TotalLength + text.Length);
return;
}
LineSegment firstLine = segment;
firstLine.InsertedLinePart(offset - firstLine.Offset, ds.Offset);
int lastDelimiterEnd = 0;
DelimiterSegment ds;
while ((ds = NextDelimiter(text, lastDelimiterEnd)) != null) {
while (ds != null) {
// split line segment at line delimiter
int lineBreakOffset = offset + ds.Offset + ds.Length;
int segmentOffset = segment.Offset;
@ -152,9 +165,13 @@ namespace ICSharpCode.TextEditor.Document @@ -152,9 +165,13 @@ namespace ICSharpCode.TextEditor.Document
segment = newSegment;
lastDelimiterEnd = ds.Offset + ds.Length;
ds = NextDelimiter(text, lastDelimiterEnd);
}
firstLine.SplitTo(segment);
// insert rest after last delimiter
if (lastDelimiterEnd != text.Length) {
segment.InsertedLinePart(0, text.Length - lastDelimiterEnd);
SetSegmentLength(segment, segment.TotalLength + text.Length - lastDelimiterEnd);
}
}

242
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegment.cs

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.Text;
namespace ICSharpCode.TextEditor.Document
@ -32,6 +33,10 @@ namespace ICSharpCode.TextEditor.Document @@ -32,6 +33,10 @@ namespace ICSharpCode.TextEditor.Document
return null;
}
public bool IsDeleted {
get { return !treeEntry.IsValid; }
}
public int LineNumber {
get { return treeEntry.CurrentIndex; }
}
@ -60,7 +65,7 @@ namespace ICSharpCode.TextEditor.Document @@ -60,7 +65,7 @@ namespace ICSharpCode.TextEditor.Document
public int DelimiterLength {
get { return delimiterLength; }
set { delimiterLength = value; }
internal set { delimiterLength = value; }
}
// highlighting information
@ -104,137 +109,140 @@ namespace ICSharpCode.TextEditor.Document @@ -104,137 +109,140 @@ namespace ICSharpCode.TextEditor.Document
return "[LineSegment: Offset = "+ Offset +", Length = " + Length + ", TotalLength = " + TotalLength + ", DelimiterLength = " + delimiterLength + "]";
}
// Svante Lidman: reconsider whether it was the right descision to move these methids here.
#region Anchor management
Util.WeakCollection<TextAnchor> anchors;
public TextAnchor CreateAnchor(int column)
{
TextAnchor anchor = new TextAnchor(this, column);
AddAnchor(anchor);
return anchor;
}
void AddAnchor(TextAnchor anchor)
{
Debug.Assert(anchor.Line == this);
if (anchors == null)
anchors = new Util.WeakCollection<TextAnchor>();
anchors.Add(anchor);
}
/// <summary>
/// Is called when the LineSegment is deleted.
/// </summary>
internal void Deleted()
{
//Console.WriteLine("Deleted");
treeEntry = LineSegmentTree.Enumerator.Invalid;
if (anchors != null) {
foreach (TextAnchor a in anchors) {
a.Deleted();
}
anchors = null;
}
}
/// <summary>
/// get the string, which matches the regular expression expr,
/// in string s2 at index
/// Is called when a part of the line is removed.
/// </summary>
internal string GetRegString(char[] expr, int index, IDocument document)
internal void RemovedLinePart(int startColumn, int length)
{
int j = 0;
StringBuilder regexpr = new StringBuilder();;
if (length == 0)
return;
Debug.Assert(length > 0);
for (int i = 0; i < expr.Length; ++i, ++j) {
if (index + j >= this.Length)
break;
switch (expr[i]) {
case '@': // "special" meaning
++i;
switch (expr[i]) {
case '!': // don't match the following expression
StringBuilder whatmatch = new StringBuilder();
++i;
while (i < expr.Length && expr[i] != '@') {
whatmatch.Append(expr[i++]);
}
break;
case '@': // matches @
regexpr.Append(document.GetCharAt(this.Offset + index + j));
break;
//Console.WriteLine("RemovedLinePart " + startColumn + ", " + length);
if (anchors != null) {
List<TextAnchor> deletedAnchors = null;
foreach (TextAnchor a in anchors) {
if (a.ColumnNumber > startColumn) {
if (a.ColumnNumber >= startColumn + length) {
a.ColumnNumber -= length;
} else {
if (deletedAnchors == null)
deletedAnchors = new List<TextAnchor>();
a.Deleted();
deletedAnchors.Add(a);
}
break;
default:
if (expr[i] != document.GetCharAt(this.Offset + index + j)) {
return regexpr.ToString();
}
regexpr.Append(document.GetCharAt(this.Offset + index + j));
break;
}
}
if (deletedAnchors != null) {
foreach (TextAnchor a in deletedAnchors) {
anchors.Remove(a);
}
}
}
return regexpr.ToString();
}
/// <summary>
/// returns true, if the get the string s2 at index matches the expression expr
/// Is called when a part of the line is inserted.
/// </summary>
internal bool MatchExpr(char[] expr, int index, IDocument document, bool ignoreCase)
internal void InsertedLinePart(int startColumn, int length)
{
for (int i = 0, j = 0; i < expr.Length; ++i, ++j) {
switch (expr[i]) {
case '@': // "special" meaning
++i;
if (i < expr.Length) {
switch (expr[i]) {
case 'C': // match whitespace or punctuation
if (index + j == this.Offset || index + j >= this.Offset + this.Length) {
// nothing (EOL or SOL)
} else {
char ch = document.GetCharAt(this.Offset + index + j);
if (!Char.IsWhiteSpace(ch) && !Char.IsPunctuation(ch)) {
return false;
}
}
break;
case '!': // don't match the following expression
{
StringBuilder whatmatch = new StringBuilder();
++i;
while (i < expr.Length && expr[i] != '@') {
whatmatch.Append(expr[i++]);
}
if (this.Offset + index + j + whatmatch.Length < document.TextLength) {
int k = 0;
for (; k < whatmatch.Length; ++k) {
char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(this.Offset + index + j + k)) : document.GetCharAt(this.Offset + index + j + k);
char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
if (docChar != spanChar) {
break;
}
}
if (k >= whatmatch.Length) {
return false;
}
}
// --j;
break;
}
case '-': // don't match the expression before
{
StringBuilder whatmatch = new StringBuilder();
++i;
while (i < expr.Length && expr[i] != '@') {
whatmatch.Append(expr[i++]);
}
if (index - whatmatch.Length >= 0) {
int k = 0;
for (; k < whatmatch.Length; ++k) {
char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(this.Offset + index - whatmatch.Length + k)) : document.GetCharAt(this.Offset + index - whatmatch.Length + k);
char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
if (docChar != spanChar)
break;
}
if (k >= whatmatch.Length) {
return false;
}
}
// --j;
break;
}
case '@': // matches @
if (index + j >= this.Length || '@' != document.GetCharAt(this.Offset + index + j)) {
return false;
}
break;
}
}
break;
default:
{
if (index + j >= this.Length) {
return false;
}
char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(this.Offset + index + j)) : document.GetCharAt(this.Offset + index + j);
char spanChar = ignoreCase ? Char.ToUpperInvariant(expr[i]) : expr[i];
if (docChar != spanChar) {
return false;
}
break;
}
if (length == 0)
return;
Debug.Assert(length > 0);
//Console.WriteLine("InsertedLinePart " + startColumn + ", " + length);
if (anchors != null) {
foreach (TextAnchor a in anchors) {
if (a.ColumnNumber >= startColumn) {
a.ColumnNumber += length;
}
}
}
}
/// <summary>
/// Is called after another line's content is appended to this line because the newline in between
/// was deleted.
/// The DefaultLineManager will call Deleted() on the deletedLine after the MergedWith call.
///
/// firstLineLength: the length of the line before the merge.
/// </summary>
internal void MergedWith(LineSegment deletedLine, int firstLineLength)
{
//Console.WriteLine("MergedWith");
if (deletedLine.anchors != null) {
foreach (TextAnchor a in deletedLine.anchors) {
a.Line = this;
AddAnchor(a);
a.ColumnNumber += firstLineLength;
}
deletedLine.anchors = null;
}
}
/// <summary>
/// Is called after a newline was inserted into this line, splitting it into this and followingLine.
/// </summary>
internal void SplitTo(LineSegment followingLine)
{
//Console.WriteLine("SplitTo");
if (anchors != null) {
List<TextAnchor> movedAnchors = null;
foreach (TextAnchor a in anchors) {
if (a.ColumnNumber > this.Length) {
a.Line = followingLine;
followingLine.AddAnchor(a);
a.ColumnNumber -= this.Length;
if (movedAnchors == null)
movedAnchors = new List<TextAnchor>();
movedAnchors.Add(a);
}
}
if (movedAnchors != null) {
foreach (TextAnchor a in movedAnchors) {
anchors.Remove(a);
}
}
}
return true;
}
#endregion
}
}

6
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegmentTree.cs

@ -377,6 +377,12 @@ namespace ICSharpCode.TextEditor.Document @@ -377,6 +377,12 @@ namespace ICSharpCode.TextEditor.Document
public struct Enumerator : IEnumerator<LineSegment>
{
/// <summary>
/// An invalid enumerator value. Calling MoveNext on the invalid enumerator
/// will always return false, accessing Current will throw an exception.
/// </summary>
public static readonly Enumerator Invalid = default(Enumerator);
internal RedBlackTreeIterator<RBNode> it;
internal Enumerator(RedBlackTreeIterator<RBNode> it)

88
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextAnchor.cs

@ -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() + "]";
}
}
}

4
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/Caret.cs

@ -195,7 +195,7 @@ namespace ICSharpCode.TextEditor @@ -195,7 +195,7 @@ namespace ICSharpCode.TextEditor
void GotFocus(object sender, EventArgs e)
{
hidden = false;
if (!textArea.MotherTextEditorControl.IsUpdating) {
if (!textArea.MotherTextEditorControl.IsInUpdate) {
CreateCaret();
UpdateCaretPosition();
}
@ -226,7 +226,7 @@ namespace ICSharpCode.TextEditor @@ -226,7 +226,7 @@ namespace ICSharpCode.TextEditor
oldLine = line;
if (hidden || textArea.MotherTextEditorControl.IsUpdating) {
if (hidden || textArea.MotherTextEditorControl.IsInUpdate) {
return;
}
if (!caretCreated) {

6
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextArea.cs

@ -741,7 +741,7 @@ namespace ICSharpCode.TextEditor @@ -741,7 +741,7 @@ namespace ICSharpCode.TextEditor
/// </remarks>
public void InsertChar(char ch)
{
bool updating = motherTextEditorControl.IsUpdating;
bool updating = motherTextEditorControl.IsInUpdate;
if (!updating) {
BeginUpdate();
}
@ -783,7 +783,7 @@ namespace ICSharpCode.TextEditor @@ -783,7 +783,7 @@ namespace ICSharpCode.TextEditor
/// </remarks>
public void InsertString(string str)
{
bool updating = motherTextEditorControl.IsUpdating;
bool updating = motherTextEditorControl.IsInUpdate;
if (!updating) {
BeginUpdate();
}
@ -824,7 +824,7 @@ namespace ICSharpCode.TextEditor @@ -824,7 +824,7 @@ namespace ICSharpCode.TextEditor
/// </remarks>
public void ReplaceChar(char ch)
{
bool updating = motherTextEditorControl.IsUpdating;
bool updating = motherTextEditorControl.IsInUpdate;
if (!updating) {
BeginUpdate();
}

6
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs

@ -29,6 +29,7 @@ namespace ICSharpCode.TextEditor @@ -29,6 +29,7 @@ namespace ICSharpCode.TextEditor
PrintDocument printDocument = null;
[Browsable(false)]
public PrintDocument PrintDocument {
get {
if (printDocument == null) {
@ -128,11 +129,14 @@ namespace ICSharpCode.TextEditor @@ -128,11 +129,14 @@ namespace ICSharpCode.TextEditor
}
}
[Browsable(false)]
public bool EnableUndo {
get {
return Document.UndoStack.CanUndo;
}
}
[Browsable(false)]
public bool EnableRedo {
get {
return Document.UndoStack.CanRedo;
@ -216,7 +220,7 @@ namespace ICSharpCode.TextEditor @@ -216,7 +220,7 @@ namespace ICSharpCode.TextEditor
void CommitUpdateRequested(object sender, EventArgs e)
{
if (IsUpdating) {
if (IsInUpdate) {
return;
}
foreach (TextAreaUpdate update in Document.UpdateQueue) {

35
src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControlBase.cs

@ -37,6 +37,7 @@ namespace ICSharpCode.TextEditor @@ -37,6 +37,7 @@ namespace ICSharpCode.TextEditor
/// </summary>
protected Dictionary<Keys, IEditAction> editactions = new Dictionary<Keys, IEditAction>();
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ITextEditorProperties TextEditorProperties {
get {
@ -83,17 +84,6 @@ namespace ICSharpCode.TextEditor @@ -83,17 +84,6 @@ namespace ICSharpCode.TextEditor
}
}
/// <value>
/// true, if the textarea is updating it's status, while
/// it updates it status no redraw operation occurs.
/// </value>
[Browsable(false)]
public bool IsUpdating {
get {
return updateLevel > 0;
}
}
/// <value>
/// The current document
/// </value>
@ -122,6 +112,7 @@ namespace ICSharpCode.TextEditor @@ -122,6 +112,7 @@ namespace ICSharpCode.TextEditor
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(System.Drawing.Design.UITypeEditor))]
public override string Text {
get {
return Document.TextContent;
@ -158,10 +149,14 @@ namespace ICSharpCode.TextEditor @@ -158,10 +149,14 @@ namespace ICSharpCode.TextEditor
}
}
/// <value>
/// true, if the textarea is updating it's status, while
/// it updates it status no redraw operation occurs.
/// </value>
[Browsable(false)]
public bool IsInUpdate {
get {
return this.updateLevel > 0;
return updateLevel > 0;
}
}
@ -260,7 +255,7 @@ namespace ICSharpCode.TextEditor @@ -260,7 +255,7 @@ namespace ICSharpCode.TextEditor
/// If true the vertical ruler is shown in the textarea
/// </value>
[Category("Appearance")]
[DefaultValue(false)]
[DefaultValue(true)]
[Description("If true the vertical ruler is shown in the textarea")]
public bool ShowVRuler {
get {
@ -308,7 +303,7 @@ namespace ICSharpCode.TextEditor @@ -308,7 +303,7 @@ namespace ICSharpCode.TextEditor
/// If true invalid lines are marked in the textarea
/// </value>
[Category("Appearance")]
[DefaultValue(true)]
[DefaultValue(false)]
[Description("If true invalid lines are marked in the textarea")]
public bool ShowInvalidLines {
get {
@ -350,7 +345,7 @@ namespace ICSharpCode.TextEditor @@ -350,7 +345,7 @@ namespace ICSharpCode.TextEditor
}
[Category("Appearance")]
[DefaultValue(true)]
[DefaultValue(false)]
[Description("If true the icon bar is displayed")]
public bool IsIconBarVisible {
get {
@ -732,7 +727,7 @@ namespace ICSharpCode.TextEditor @@ -732,7 +727,7 @@ namespace ICSharpCode.TextEditor
/// </remarks>
public override void Refresh()
{
if (IsUpdating) {
if (IsInUpdate) {
return;
}
base.Refresh();
@ -755,14 +750,6 @@ namespace ICSharpCode.TextEditor @@ -755,14 +750,6 @@ namespace ICSharpCode.TextEditor
}
}
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null) {
Changed(this, e);
}
}
public event EventHandler FileNameChanged;
public event EventHandler Changed;
}
}

136
src/Libraries/ICSharpCode.TextEditor/Project/Src/Util/WeakCollection.cs

@ -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…
Cancel
Save