Browse Source

Fixed forum-7979 (breakpoint disappears when the line above is deleted).

Bookmarks now don't bind to a line, but use a TextAnchor.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3272 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
2f65a05f2b
  1. 2
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/DocumentAccessor.cs
  2. 5
      src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs
  3. 1
      src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj
  4. 8
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/BookmarkActions.cs
  5. 93
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/Bookmark.cs
  6. 40
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/BookmarkManager.cs
  7. 48
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs
  8. 44
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/DeferredEventList.cs
  9. 29
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineManager.cs
  10. 10
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegment.cs
  11. 13
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextAnchor.cs
  12. 8
      src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs
  13. 4
      src/Main/Base/Project/Src/Services/Debugger/CurrentLineBookmark.cs
  14. 4
      src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs
  15. 8
      src/Main/Base/Project/Src/TextEditor/Bookmarks/Bookmark.cs
  16. 14
      src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkConverter.cs
  17. 1
      src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs
  18. 24
      src/Main/Base/Project/Src/TextEditor/Bookmarks/ClassMemberBookmark.cs

2
src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/DocumentAccessor.cs

@ -99,7 +99,7 @@ namespace CSharpBinding.FormattingStrategy
public bool Next() public bool Next()
{ {
if (lineDirty) { if (lineDirty) {
doc.Replace(line.Offset, line.Length, text); DefaultFormattingStrategy.SmartReplaceLine(doc, line, text);
lineDirty = false; lineDirty = false;
++changedLines; ++changedLines;
} }

5
src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs

@ -156,10 +156,11 @@ namespace SearchAndReplace
textAreas.Add(textArea); textAreas.Add(textArea);
} }
textArea.ActiveTextAreaControl.Caret.Position = textArea.Document.OffsetToPosition(result.Offset); textArea.ActiveTextAreaControl.Caret.Position = textArea.Document.OffsetToPosition(result.Offset);
int lineNr = textArea.Document.GetLineNumberForOffset(result.Offset); LineSegment segment = textArea.Document.GetLineSegmentForOffset(result.Offset);
int lineNr = segment.LineNumber;
if (!textArea.Document.BookmarkManager.IsMarked(lineNr)) { if (!textArea.Document.BookmarkManager.IsMarked(lineNr)) {
textArea.Document.BookmarkManager.ToggleMarkAt(lineNr); textArea.Document.BookmarkManager.ToggleMarkAt(new TextLocation(result.Offset - segment.Offset, lineNr));
} }
} }
} }

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

@ -59,6 +59,7 @@
<Compile Include="Src\Document\HighlightingStrategy\HighlightingDefinitionInvalidException.cs" /> <Compile Include="Src\Document\HighlightingStrategy\HighlightingDefinitionInvalidException.cs" />
<Compile Include="Src\Document\IDocument.cs" /> <Compile Include="Src\Document\IDocument.cs" />
<Compile Include="Src\Document\ISegment.cs" /> <Compile Include="Src\Document\ISegment.cs" />
<Compile Include="Src\Document\LineManager\DeferredEventList.cs" />
<Compile Include="Src\Document\LineManager\LineSegmentTree.cs" /> <Compile Include="Src\Document\LineManager\LineSegmentTree.cs" />
<Compile Include="Src\Document\TextAnchor.cs" /> <Compile Include="Src\Document\TextAnchor.cs" />
<Compile Include="Src\Document\TextLocation.cs" /> <Compile Include="Src\Document\TextLocation.cs" />

8
src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/BookmarkActions.cs

@ -14,7 +14,7 @@ namespace ICSharpCode.TextEditor.Actions
{ {
public override void Execute(TextArea textArea) public override void Execute(TextArea textArea)
{ {
textArea.Document.BookmarkManager.ToggleMarkAt(textArea.Caret.Line); textArea.Document.BookmarkManager.ToggleMarkAt(textArea.Caret.Position);
textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, textArea.Caret.Line)); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, textArea.Caret.Line));
textArea.Document.CommitUpdate(); textArea.Document.CommitUpdate();
@ -34,8 +34,9 @@ namespace ICSharpCode.TextEditor.Actions
{ {
Bookmark mark = textArea.Document.BookmarkManager.GetPrevMark(textArea.Caret.Line, predicate); Bookmark mark = textArea.Document.BookmarkManager.GetPrevMark(textArea.Caret.Line, predicate);
if (mark != null) { if (mark != null) {
textArea.Caret.Line = mark.LineNumber; textArea.Caret.Position = mark.Location;
textArea.SelectionManager.ClearSelection(); textArea.SelectionManager.ClearSelection();
textArea.SetDesiredColumn();
} }
} }
} }
@ -53,8 +54,9 @@ namespace ICSharpCode.TextEditor.Actions
{ {
Bookmark mark = textArea.Document.BookmarkManager.GetNextMark(textArea.Caret.Line, predicate); Bookmark mark = textArea.Document.BookmarkManager.GetNextMark(textArea.Caret.Line, predicate);
if (mark != null) { if (mark != null) {
textArea.Caret.Line = mark.LineNumber; textArea.Caret.Position = mark.Location;
textArea.SelectionManager.ClearSelection(); textArea.SelectionManager.ClearSelection();
textArea.SetDesiredColumn();
} }
} }
} }

93
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/Bookmark.cs

@ -17,8 +17,8 @@ namespace ICSharpCode.TextEditor.Document
public class Bookmark public class Bookmark
{ {
IDocument document; IDocument document;
LineSegment line; TextAnchor anchor;
int lineNumber; TextLocation location;
bool isEnabled = true; bool isEnabled = true;
public IDocument Document { public IDocument Document {
@ -27,19 +27,54 @@ namespace ICSharpCode.TextEditor.Document
} }
set { set {
if (document != value) { if (document != value) {
if (line != null) { if (anchor != null) {
lineNumber = line.LineNumber; location = anchor.Location;
line = null; anchor = null;
} }
document = value; document = value;
if (document != null) { CreateAnchor();
line = document.GetLineSegment(Math.Min(lineNumber, document.TotalNumberOfLines-1));
}
OnDocumentChanged(EventArgs.Empty); OnDocumentChanged(EventArgs.Empty);
} }
} }
} }
void CreateAnchor()
{
if (document != null) {
LineSegment line = document.GetLineSegment(Math.Max(0, Math.Min(location.Line, document.TotalNumberOfLines-1)));
anchor = line.CreateAnchor(Math.Max(0, Math.Min(location.Column, line.Length)));
// after insertion: keep bookmarks after the initial whitespace (see DefaultFormattingStrategy.SmartReplaceLine)
anchor.MovementType = AnchorMovementType.AfterInsertion;
anchor.Deleted += AnchorDeleted;
}
}
void AnchorDeleted(object sender, EventArgs e)
{
document.BookmarkManager.RemoveMark(this);
}
/// <summary>
/// Gets the TextAnchor used for this bookmark.
/// Is null if the bookmark is not connected to a document.
/// </summary>
public TextAnchor Anchor {
get { return anchor; }
}
public TextLocation Location {
get {
if (anchor != null)
return anchor.Location;
else
return location;
}
set {
location = value;
CreateAnchor();
}
}
public event EventHandler DocumentChanged; public event EventHandler DocumentChanged;
protected virtual void OnDocumentChanged(EventArgs e) protected virtual void OnDocumentChanged(EventArgs e)
@ -57,7 +92,7 @@ namespace ICSharpCode.TextEditor.Document
if (isEnabled != value) { if (isEnabled != value) {
isEnabled = value; isEnabled = value;
if (document != null) { if (document != null) {
document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineNumber)); document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, LineNumber));
document.CommitUpdate(); document.CommitUpdate();
} }
OnIsEnabledChanged(EventArgs.Empty); OnIsEnabledChanged(EventArgs.Empty);
@ -74,29 +109,21 @@ namespace ICSharpCode.TextEditor.Document
} }
} }
/// <summary>
/// Gets the line the bookmark belongs to.
/// Is null if the bookmark is not connected to a document.
/// </summary>
public LineSegment Line {
get { return line; }
}
public int LineNumber { public int LineNumber {
get { get {
if (line != null) if (anchor != null)
return line.LineNumber; return anchor.LineNumber;
else else
return lineNumber; return location.Line;
} }
set { }
if (value < 0)
throw new ArgumentOutOfRangeException("value", value, "line number must be >= 0"); public int ColumnNumber {
if (document == null) { get {
lineNumber = value; if (anchor != null)
} else { return anchor.ColumnNumber;
line = document.GetLineSegment(value); else
} return location.Column;
} }
} }
@ -109,15 +136,15 @@ namespace ICSharpCode.TextEditor.Document
} }
} }
public Bookmark(IDocument document, int lineNumber) : this(document, lineNumber, true) public Bookmark(IDocument document, TextLocation location) : this(document, location, true)
{ {
} }
public Bookmark(IDocument document, int lineNumber, bool isEnabled) public Bookmark(IDocument document, TextLocation location, bool isEnabled)
{ {
this.document = document; this.document = document;
this.isEnabled = isEnabled; this.isEnabled = isEnabled;
this.LineNumber = lineNumber; this.Location = location;
} }
public virtual bool Click(SWF.Control parent, SWF.MouseEventArgs e) public virtual bool Click(SWF.Control parent, SWF.MouseEventArgs e)

40
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/BookmarkManager.cs

@ -14,7 +14,7 @@ namespace ICSharpCode.TextEditor.Document
{ {
public interface IBookmarkFactory public interface IBookmarkFactory
{ {
Bookmark CreateBookmark(IDocument document, int lineNumber); Bookmark CreateBookmark(IDocument document, TextLocation location);
} }
/// <summary> /// <summary>
@ -50,46 +50,32 @@ namespace ICSharpCode.TextEditor.Document
internal BookmarkManager(IDocument document, LineManager lineTracker) internal BookmarkManager(IDocument document, LineManager lineTracker)
{ {
this.document = document; this.document = document;
lineTracker.LineDeleted += delegate(object sender, LineEventArgs e) {
for (int i = 0; i < bookmark.Count; i++) {
Bookmark b = bookmark[i];
if (b.Line == e.LineSegment) {
bookmark.RemoveAt(i--);
OnRemoved(new BookmarkEventArgs(b));
}
}
};
} }
IBookmarkFactory factory; /// <summary>
/// Gets/Sets the bookmark factory used to create bookmarks for "ToggleMarkAt".
public IBookmarkFactory Factory { /// </summary>
get { public IBookmarkFactory Factory { get; set;}
return factory;
}
set {
factory = value;
}
}
/// <summary> /// <summary>
/// Sets the mark at the line <code>lineNr</code> if it is not set, if the /// Sets the mark at the line <code>location.Line</code> if it is not set, if the
/// line is already marked the mark is cleared. /// line is already marked the mark is cleared.
/// </summary> /// </summary>
public void ToggleMarkAt(int lineNr) public void ToggleMarkAt(TextLocation location)
{ {
Bookmark newMark; Bookmark newMark;
if (factory != null) if (Factory != null) {
newMark = factory.CreateBookmark(document, lineNr); newMark = Factory.CreateBookmark(document, location);
else } else {
newMark = new Bookmark(document, lineNr); newMark = new Bookmark(document, location);
}
Type newMarkType = newMark.GetType(); Type newMarkType = newMark.GetType();
for (int i = 0; i < bookmark.Count; ++i) { for (int i = 0; i < bookmark.Count; ++i) {
Bookmark mark = bookmark[i]; Bookmark mark = bookmark[i];
if (mark.LineNumber == lineNr && mark.CanToggle && mark.GetType() == newMarkType) { if (mark.LineNumber == location.Line && mark.CanToggle && mark.GetType() == newMarkType) {
bookmark.RemoveAt(i); bookmark.RemoveAt(i);
OnRemoved(new BookmarkEventArgs(mark)); OnRemoved(new BookmarkEventArgs(mark));
return; return;

48
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs

@ -55,11 +55,57 @@ namespace ICSharpCode.TextEditor.Document
if(indentation.Length > 0) { if(indentation.Length > 0) {
string newLineText = indentation + TextUtilities.GetLineAsString(textArea.Document, lineNumber).Trim(); string newLineText = indentation + TextUtilities.GetLineAsString(textArea.Document, lineNumber).Trim();
LineSegment oldLine = textArea.Document.GetLineSegment(lineNumber); LineSegment oldLine = textArea.Document.GetLineSegment(lineNumber);
textArea.Document.Replace(oldLine.Offset, oldLine.Length, newLineText); SmartReplaceLine(textArea.Document, oldLine, newLineText);
} }
return indentation.Length; return indentation.Length;
} }
static readonly char[] whitespaceChars = {' ', '\t'};
/// <summary>
/// Replaces the text in a line.
/// If only whitespace at the beginning and end of the line was changed, this method
/// only adjusts the whitespace and doesn't replace the other text.
/// </summary>
public static void SmartReplaceLine(IDocument document, LineSegment line, string newLineText)
{
if (document == null)
throw new ArgumentNullException("document");
if (line == null)
throw new ArgumentNullException("line");
if (newLineText == null)
throw new ArgumentNullException("newLineText");
string newLineTextTrim = newLineText.Trim(whitespaceChars);
string oldLineText = document.GetText(line);
if (oldLineText == newLineText)
return;
int pos = oldLineText.IndexOf(newLineTextTrim);
if (newLineTextTrim.Length > 0 && pos >= 0) {
document.UndoStack.StartUndoGroup();
try {
// find whitespace at beginning
int startWhitespaceLength = 0;
while (startWhitespaceLength < newLineText.Length) {
char c = newLineText[startWhitespaceLength];
if (c != ' ' && c != '\t')
break;
startWhitespaceLength++;
}
// find whitespace at end
int endWhitespaceLength = newLineText.Length - newLineTextTrim.Length - startWhitespaceLength;
// replace whitespace sections
int lineOffset = line.Offset;
document.Replace(lineOffset + pos + newLineTextTrim.Length, line.Length - pos - newLineTextTrim.Length, newLineText.Substring(newLineText.Length - endWhitespaceLength));
document.Replace(lineOffset, pos, newLineText.Substring(0, startWhitespaceLength));
} finally {
document.UndoStack.EndUndoGroup();
}
} else {
document.Replace(line.Offset, line.Length, newLineText);
}
}
/// <summary> /// <summary>
/// Could be overwritten to define more complex indenting. /// Could be overwritten to define more complex indenting.
/// </summary> /// </summary>

44
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/DeferredEventList.cs

@ -0,0 +1,44 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
namespace ICSharpCode.TextEditor.Document
{
/// <summary>
/// A list of events that are fired after the line manager has finished working.
/// </summary>
struct DeferredEventList
{
internal List<LineSegment> removedLines;
internal List<TextAnchor> textAnchor;
public void AddRemovedLine(LineSegment line)
{
if (removedLines == null)
removedLines = new List<LineSegment>();
removedLines.Add(line);
}
public void AddDeletedAnchor(TextAnchor anchor)
{
if (textAnchor == null)
textAnchor = new List<TextAnchor>();
textAnchor.Add(anchor);
}
public void RaiseEvents()
{
// removedLines is raised by the LineManager
if (textAnchor != null) {
foreach (TextAnchor a in textAnchor) {
a.RaiseDeleted();
}
}
}
}
}

29
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineManager.cs

@ -77,10 +77,11 @@ namespace ICSharpCode.TextEditor.Document
public void Replace(int offset, int length, string text) public void Replace(int offset, int length, string text)
{ {
// Console.WriteLine("Replace offset="+offset+" length="+length+" text.Length="+text.Length); Debug.WriteLine("Replace offset="+offset+" length="+length+" text.Length="+text.Length);
int lineStart = GetLineNumberForOffset(offset); int lineStart = GetLineNumberForOffset(offset);
int oldNumberOfLines = this.TotalNumberOfLines; int oldNumberOfLines = this.TotalNumberOfLines;
List<LineSegment> removedLines = RemoveInternal(offset, length); DeferredEventList deferredEventList = new DeferredEventList();
RemoveInternal(ref deferredEventList, offset, length);
int numberOfLinesAfterRemoving = this.TotalNumberOfLines; int numberOfLinesAfterRemoving = this.TotalNumberOfLines;
if (!string.IsNullOrEmpty(text)) { if (!string.IsNullOrEmpty(text)) {
InsertInternal(offset, text); InsertInternal(offset, text);
@ -94,33 +95,34 @@ namespace ICSharpCode.TextEditor.Document
// Only fire events after RemoveInternal+InsertInternal finished completely: // Only fire events after RemoveInternal+InsertInternal finished completely:
// Otherwise we would expose inconsistent state to the event handlers. // Otherwise we would expose inconsistent state to the event handlers.
RunHighlighter(lineStart, 1 + Math.Max(0, this.TotalNumberOfLines - numberOfLinesAfterRemoving)); RunHighlighter(lineStart, 1 + Math.Max(0, this.TotalNumberOfLines - numberOfLinesAfterRemoving));
if (removedLines != null) { if (deferredEventList.removedLines != null) {
foreach (LineSegment ls in removedLines) foreach (LineSegment ls in deferredEventList.removedLines)
OnLineDeleted(new LineEventArgs(document, ls)); OnLineDeleted(new LineEventArgs(document, ls));
} }
if (this.TotalNumberOfLines != oldNumberOfLines) { if (this.TotalNumberOfLines != oldNumberOfLines) {
OnLineCountChanged(new LineCountChangeEventArgs(document, lineStart, this.TotalNumberOfLines - oldNumberOfLines)); OnLineCountChanged(new LineCountChangeEventArgs(document, lineStart, this.TotalNumberOfLines - oldNumberOfLines));
} }
deferredEventList.RaiseEvents();
} }
List<LineSegment> RemoveInternal(int offset, int length) void RemoveInternal(ref DeferredEventList deferredEventList, int offset, int length)
{ {
Debug.Assert(length >= 0); Debug.Assert(length >= 0);
if (length == 0) return null; if (length == 0) return;
LineSegmentTree.Enumerator it = lineCollection.GetEnumeratorForOffset(offset); LineSegmentTree.Enumerator it = lineCollection.GetEnumeratorForOffset(offset);
LineSegment startSegment = it.Current; LineSegment startSegment = it.Current;
int startSegmentOffset = startSegment.Offset; int startSegmentOffset = startSegment.Offset;
if (offset + length < startSegmentOffset + startSegment.TotalLength) { if (offset + length < startSegmentOffset + startSegment.TotalLength) {
// just removing a part of this line segment // just removing a part of this line segment
startSegment.RemovedLinePart(offset - startSegmentOffset, length); startSegment.RemovedLinePart(ref deferredEventList, offset - startSegmentOffset, length);
SetSegmentLength(startSegment, startSegment.TotalLength - length); SetSegmentLength(startSegment, startSegment.TotalLength - length);
return null; return;
} }
// merge startSegment with another line segment because startSegment's delimiter was deleted // merge startSegment with another line segment because startSegment's delimiter was deleted
// possibly remove lines in between if multiple delimiters were deleted // possibly remove lines in between if multiple delimiters were deleted
int charactersRemovedInStartLine = startSegmentOffset + startSegment.TotalLength - offset; int charactersRemovedInStartLine = startSegmentOffset + startSegment.TotalLength - offset;
Debug.Assert(charactersRemovedInStartLine > 0); Debug.Assert(charactersRemovedInStartLine > 0);
startSegment.RemovedLinePart(offset - startSegmentOffset, charactersRemovedInStartLine); startSegment.RemovedLinePart(ref deferredEventList, offset - startSegmentOffset, charactersRemovedInStartLine);
LineSegment endSegment = lineCollection.GetByOffset(offset + length); LineSegment endSegment = lineCollection.GetByOffset(offset + length);
@ -128,26 +130,23 @@ namespace ICSharpCode.TextEditor.Document
// special case: we are removing a part of the last line up to the // special case: we are removing a part of the last line up to the
// end of the document // end of the document
SetSegmentLength(startSegment, startSegment.TotalLength - length); SetSegmentLength(startSegment, startSegment.TotalLength - length);
return null; return;
} }
int endSegmentOffset = endSegment.Offset; int endSegmentOffset = endSegment.Offset;
int charactersLeftInEndLine = endSegmentOffset + endSegment.TotalLength - (offset + length); int charactersLeftInEndLine = endSegmentOffset + endSegment.TotalLength - (offset + length);
endSegment.RemovedLinePart(0, endSegment.TotalLength - charactersLeftInEndLine); endSegment.RemovedLinePart(ref deferredEventList, 0, endSegment.TotalLength - charactersLeftInEndLine);
startSegment.MergedWith(endSegment, offset - startSegmentOffset); startSegment.MergedWith(endSegment, offset - startSegmentOffset);
SetSegmentLength(startSegment, startSegment.TotalLength - charactersRemovedInStartLine + charactersLeftInEndLine); SetSegmentLength(startSegment, startSegment.TotalLength - charactersRemovedInStartLine + charactersLeftInEndLine);
startSegment.DelimiterLength = endSegment.DelimiterLength; startSegment.DelimiterLength = endSegment.DelimiterLength;
// remove all segments between startSegment (excl.) and endSegment (incl.) // remove all segments between startSegment (excl.) and endSegment (incl.)
it.MoveNext(); it.MoveNext();
List<LineSegment> removedLines = new List<LineSegment>();
LineSegment segmentToRemove; LineSegment segmentToRemove;
do { do {
segmentToRemove = it.Current; segmentToRemove = it.Current;
it.MoveNext(); it.MoveNext();
lineCollection.RemoveSegment(segmentToRemove); lineCollection.RemoveSegment(segmentToRemove);
removedLines.Add(segmentToRemove); segmentToRemove.Deleted(ref deferredEventList);
segmentToRemove.Deleted();
} while (segmentToRemove != endSegment); } while (segmentToRemove != endSegment);
return removedLines;
} }
void InsertInternal(int offset, string text) void InsertInternal(int offset, string text)

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

@ -117,6 +117,8 @@ namespace ICSharpCode.TextEditor.Document
public TextAnchor CreateAnchor(int column) public TextAnchor CreateAnchor(int column)
{ {
if (column < 0 || column > Length)
throw new ArgumentOutOfRangeException("column");
TextAnchor anchor = new TextAnchor(this, column); TextAnchor anchor = new TextAnchor(this, column);
AddAnchor(anchor); AddAnchor(anchor);
return anchor; return anchor;
@ -135,13 +137,13 @@ namespace ICSharpCode.TextEditor.Document
/// <summary> /// <summary>
/// Is called when the LineSegment is deleted. /// Is called when the LineSegment is deleted.
/// </summary> /// </summary>
internal void Deleted() internal void Deleted(ref DeferredEventList deferredEventList)
{ {
//Console.WriteLine("Deleted"); //Console.WriteLine("Deleted");
treeEntry = LineSegmentTree.Enumerator.Invalid; treeEntry = LineSegmentTree.Enumerator.Invalid;
if (anchors != null) { if (anchors != null) {
foreach (TextAnchor a in anchors) { foreach (TextAnchor a in anchors) {
a.Deleted(); a.Delete(ref deferredEventList);
} }
anchors = null; anchors = null;
} }
@ -150,7 +152,7 @@ namespace ICSharpCode.TextEditor.Document
/// <summary> /// <summary>
/// Is called when a part of the line is removed. /// Is called when a part of the line is removed.
/// </summary> /// </summary>
internal void RemovedLinePart(int startColumn, int length) internal void RemovedLinePart(ref DeferredEventList deferredEventList, int startColumn, int length)
{ {
if (length == 0) if (length == 0)
return; return;
@ -166,7 +168,7 @@ namespace ICSharpCode.TextEditor.Document
} else { } else {
if (deletedAnchors == null) if (deletedAnchors == null)
deletedAnchors = new List<TextAnchor>(); deletedAnchors = new List<TextAnchor>();
a.Deleted(); a.Delete(ref deferredEventList);
deletedAnchors.Add(a); deletedAnchors.Add(a);
} }
} }

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

@ -85,9 +85,20 @@ namespace ICSharpCode.TextEditor.Document
/// </summary> /// </summary>
public AnchorMovementType MovementType { get; set; } public AnchorMovementType MovementType { get; set; }
internal void Deleted() public event EventHandler Deleted;
internal void Delete(ref DeferredEventList deferredEventList)
{ {
// we cannot fire an event here because this method is called while the LineManager adjusts the
// lineCollection, so an event handler could see inconsistent state
lineSegment = null; lineSegment = null;
deferredEventList.AddDeletedAnchor(this);
}
internal void RaiseDeleted()
{
if (Deleted != null)
Deleted(this, EventArgs.Empty);
} }
internal TextAnchor(LineSegment lineSegment, int columnNumber) internal TextAnchor(LineSegment lineSegment, int columnNumber)

8
src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs

@ -26,7 +26,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
} }
set { set {
isHealthy = value; isHealthy = value;
if (Document != null && !Line.IsDeleted) { if (Document != null && !Anchor.IsDeleted) {
Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, LineNumber)); Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, LineNumber));
Document.CommitUpdate(); Document.CommitUpdate();
} }
@ -38,7 +38,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
set { tooltip = value; } set { tooltip = value; }
} }
public BreakpointBookmark(string fileName, IDocument document, int lineNumber) : base(fileName, document, lineNumber) public BreakpointBookmark(string fileName, IDocument document, TextLocation location) : base(fileName, document, location)
{ {
} }
@ -49,9 +49,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
protected override TextMarker CreateMarker() protected override TextMarker CreateMarker()
{ {
if (LineNumber >= Document.TotalNumberOfLines) LineSegment lineSeg = Anchor.Line;
LineNumber = Document.TotalNumberOfLines - 1;
LineSegment lineSeg = Document.GetLineSegment(LineNumber);
TextMarker marker = new TextMarker(lineSeg.Offset, lineSeg.Length, TextMarkerType.SolidBlock, defaultColor, Color.White); TextMarker marker = new TextMarker(lineSeg.Offset, lineSeg.Length, TextMarkerType.SolidBlock, defaultColor, Color.White);
Document.MarkerStrategy.AddMarker(marker); Document.MarkerStrategy.AddMarker(marker);
return marker; return marker;

4
src/Main/Base/Project/Src/Services/Debugger/CurrentLineBookmark.cs

@ -43,7 +43,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
endColumn = makerEndColumn; endColumn = makerEndColumn;
LineSegment line = document.GetLineSegment(startLine - 1); LineSegment line = document.GetLineSegment(startLine - 1);
instance = new CurrentLineBookmark(fileName, document, startLine - 1); instance = new CurrentLineBookmark(fileName, document, new TextLocation(startColumn - 1, startLine - 1));
document.BookmarkManager.AddMark(instance); document.BookmarkManager.AddMark(instance);
document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, startLine - 1, endLine - 1)); document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, startLine - 1, endLine - 1));
document.CommitUpdate(); document.CommitUpdate();
@ -64,7 +64,7 @@ namespace ICSharpCode.SharpDevelop.Debugging
} }
} }
public CurrentLineBookmark(string fileName, IDocument document, int startLine) : base(fileName, document, startLine) public CurrentLineBookmark(string fileName, IDocument document, TextLocation location) : base(fileName, document, location)
{ {
this.IsSaved = false; this.IsSaved = false;
this.IsVisibleInBookmarkPad = false; this.IsVisibleInBookmarkPad = false;

4
src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs

@ -230,13 +230,15 @@ namespace ICSharpCode.SharpDevelop.Debugging
} }
} }
} }
int column = 0;
foreach (char ch in document.GetText(document.GetLineSegment(lineNumber))) { foreach (char ch in document.GetText(document.GetLineSegment(lineNumber))) {
if (!char.IsWhiteSpace(ch)) { if (!char.IsWhiteSpace(ch)) {
document.BookmarkManager.AddMark(new BreakpointBookmark(fileName, document, lineNumber)); document.BookmarkManager.AddMark(new BreakpointBookmark(fileName, document, new TextLocation(column, lineNumber)));
document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineNumber)); document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineNumber));
document.CommitUpdate(); document.CommitUpdate();
break; break;
} }
column++;
} }
} }

8
src/Main/Base/Project/Src/TextEditor/Bookmarks/Bookmark.cs

@ -18,7 +18,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
[TypeConverter(typeof(BookmarkConverter))] [TypeConverter(typeof(BookmarkConverter))]
public class SDBookmark : Bookmark public class SDBookmark : Bookmark
{ {
public SDBookmark(string fileName, IDocument document, int lineNumber) : base(document, lineNumber) public SDBookmark(string fileName, IDocument document, TextLocation location) : base(document, location)
{ {
this.fileName = fileName; this.fileName = fileName;
} }
@ -96,7 +96,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
/// </summary> /// </summary>
public abstract class SDMarkerBookmark : SDBookmark public abstract class SDMarkerBookmark : SDBookmark
{ {
public SDMarkerBookmark(string fileName, IDocument document, int lineNumber) : base(fileName, document, lineNumber) public SDMarkerBookmark(string fileName, IDocument document, TextLocation location) : base(fileName, document, location)
{ {
SetMarker(); SetMarker();
} }
@ -169,9 +169,9 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
} }
} }
public Bookmark CreateBookmark(IDocument document, int lineNumber) public Bookmark CreateBookmark(IDocument document, TextLocation location)
{ {
return new SDBookmark(fileName, document, lineNumber); return new SDBookmark(fileName, document, location);
} }
} }
} }

14
src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkConverter.cs

@ -5,6 +5,7 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.TextEditor;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
@ -27,20 +28,25 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
{ {
if (value is string) { if (value is string) {
string[] v = ((string)value).Split('|'); string[] v = ((string)value).Split('|');
if (v.Length != 5)
return null;
string fileName = v[1]; string fileName = v[1];
int lineNumber = int.Parse(v[2], culture); int lineNumber = int.Parse(v[2], culture);
int columnNumber = int.Parse(v[3], culture);
if (lineNumber < 0) if (lineNumber < 0)
return null; return null;
if (columnNumber < 0)
return null;
SDBookmark bookmark; SDBookmark bookmark;
switch (v[0]) { switch (v[0]) {
case "Breakpoint": case "Breakpoint":
bookmark = new Debugging.BreakpointBookmark(fileName, null, lineNumber); bookmark = new Debugging.BreakpointBookmark(fileName, null, new TextLocation(columnNumber, lineNumber));
break; break;
default: default:
bookmark = new SDBookmark(fileName, null, lineNumber); bookmark = new SDBookmark(fileName, null, new TextLocation(columnNumber, lineNumber));
break; break;
} }
bookmark.IsEnabled = bool.Parse(v[3]); bookmark.IsEnabled = bool.Parse(v[4]);
return bookmark; return bookmark;
} else { } else {
return base.ConvertFrom(context, culture, value); return base.ConvertFrom(context, culture, value);
@ -62,6 +68,8 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
b.Append('|'); b.Append('|');
b.Append(bookmark.LineNumber); b.Append(bookmark.LineNumber);
b.Append('|'); b.Append('|');
b.Append(bookmark.ColumnNumber);
b.Append('|');
b.Append(bookmark.IsEnabled.ToString()); b.Append(bookmark.IsEnabled.ToString());
return b.ToString(); return b.ToString();
} else { } else {

1
src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs

@ -40,6 +40,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
public static void AddMark(SDBookmark bookmark) public static void AddMark(SDBookmark bookmark)
{ {
if (bookmark == null) return;
if (bookmarks.Contains(bookmark)) return; if (bookmarks.Contains(bookmark)) return;
if (bookmarks.Exists(b => IsEqualBookmark(b, bookmark))) return; if (bookmarks.Exists(b => IsEqualBookmark(b, bookmark))) return;
bookmarks.Add(bookmark); bookmarks.Add(bookmark);

24
src/Main/Base/Project/Src/TextEditor/Bookmarks/ClassMemberBookmark.cs

@ -32,20 +32,14 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
} }
public ClassMemberBookmark(IDocument document, IMember member) public ClassMemberBookmark(IDocument document, IMember member)
: base(document, GetLineNumberFromMember(document, member)) : base(document, GetTextLocationFromMember(document, member))
{ {
this.member = member; this.member = member;
} }
static int GetLineNumberFromMember(IDocument document, IMember member) static TextLocation GetTextLocationFromMember(IDocument document, IMember member)
{ {
int line = member.Region.BeginLine - 1; return new TextLocation(member.Region.BeginColumn - 1, member.Region.BeginLine - 1);
if (line < 0)
return 0;
else if (document != null && line >= document.TotalNumberOfLines)
return document.TotalNumberOfLines - 1;
else
return line;
} }
public const string ContextMenuPath = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassMemberContextMenu"; public const string ContextMenuPath = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassMemberContextMenu";
@ -84,20 +78,14 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
} }
public ClassBookmark(IDocument document, IClass @class) public ClassBookmark(IDocument document, IClass @class)
: base(document, GetLineNumberFromClass(document, @class)) : base(document, GetTextLocationFromClass(document, @class))
{ {
this.@class = @class; this.@class = @class;
} }
static int GetLineNumberFromClass(IDocument document, IClass @class) static TextLocation GetTextLocationFromClass(IDocument document, IClass @class)
{ {
int line = @class.Region.BeginLine - 1; return new TextLocation(@class.Region.BeginColumn - 1, @class.Region.BeginLine - 1);
if (line < 0)
return 0;
else if (document != null && line >= document.TotalNumberOfLines)
return document.TotalNumberOfLines - 1;
else
return line;
} }
public const string ContextMenuPath = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassBookmarkContextMenu"; public const string ContextMenuPath = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassBookmarkContextMenu";

Loading…
Cancel
Save