diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/DocumentAccessor.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/DocumentAccessor.cs index 58fe0a1f41..6204559784 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/DocumentAccessor.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/DocumentAccessor.cs @@ -99,7 +99,7 @@ namespace CSharpBinding.FormattingStrategy public bool Next() { if (lineDirty) { - doc.Replace(line.Offset, line.Length, text); + DefaultFormattingStrategy.SmartReplaceLine(doc, line, text); lineDirty = false; ++changedLines; } diff --git a/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs b/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs index 0a829c32cd..daf014d8e6 100644 --- a/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs +++ b/src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs @@ -156,10 +156,11 @@ namespace SearchAndReplace textAreas.Add(textArea); } 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)) { - textArea.Document.BookmarkManager.ToggleMarkAt(lineNr); + textArea.Document.BookmarkManager.ToggleMarkAt(new TextLocation(result.Offset - segment.Offset, lineNr)); } } } diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj b/src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj index 345f9f3d86..6234fa2bd0 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj +++ b/src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj @@ -59,6 +59,7 @@ + diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/BookmarkActions.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/BookmarkActions.cs index f01faf6094..bc4f8d5e49 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/BookmarkActions.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/BookmarkActions.cs @@ -14,7 +14,7 @@ namespace ICSharpCode.TextEditor.Actions { 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.CommitUpdate(); @@ -34,8 +34,9 @@ namespace ICSharpCode.TextEditor.Actions { Bookmark mark = textArea.Document.BookmarkManager.GetPrevMark(textArea.Caret.Line, predicate); if (mark != null) { - textArea.Caret.Line = mark.LineNumber; + textArea.Caret.Position = mark.Location; textArea.SelectionManager.ClearSelection(); + textArea.SetDesiredColumn(); } } } @@ -53,8 +54,9 @@ namespace ICSharpCode.TextEditor.Actions { Bookmark mark = textArea.Document.BookmarkManager.GetNextMark(textArea.Caret.Line, predicate); if (mark != null) { - textArea.Caret.Line = mark.LineNumber; + textArea.Caret.Position = mark.Location; textArea.SelectionManager.ClearSelection(); + textArea.SetDesiredColumn(); } } } diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/Bookmark.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/Bookmark.cs index 17aba3eeda..ed5e251d58 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/Bookmark.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/Bookmark.cs @@ -17,8 +17,8 @@ namespace ICSharpCode.TextEditor.Document public class Bookmark { IDocument document; - LineSegment line; - int lineNumber; + TextAnchor anchor; + TextLocation location; bool isEnabled = true; public IDocument Document { @@ -27,19 +27,54 @@ namespace ICSharpCode.TextEditor.Document } set { if (document != value) { - if (line != null) { - lineNumber = line.LineNumber; - line = null; + if (anchor != null) { + location = anchor.Location; + anchor = null; } document = value; - if (document != null) { - line = document.GetLineSegment(Math.Min(lineNumber, document.TotalNumberOfLines-1)); - } + CreateAnchor(); 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); + } + + /// + /// Gets the TextAnchor used for this bookmark. + /// Is null if the bookmark is not connected to a document. + /// + 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; protected virtual void OnDocumentChanged(EventArgs e) @@ -57,7 +92,7 @@ namespace ICSharpCode.TextEditor.Document if (isEnabled != value) { isEnabled = value; if (document != null) { - document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineNumber)); + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, LineNumber)); document.CommitUpdate(); } OnIsEnabledChanged(EventArgs.Empty); @@ -74,29 +109,21 @@ namespace ICSharpCode.TextEditor.Document } } - /// - /// Gets the line the bookmark belongs to. - /// Is null if the bookmark is not connected to a document. - /// - public LineSegment Line { - get { return line; } - } - public int LineNumber { get { - if (line != null) - return line.LineNumber; + if (anchor != null) + return anchor.LineNumber; else - return lineNumber; + return location.Line; } - set { - if (value < 0) - throw new ArgumentOutOfRangeException("value", value, "line number must be >= 0"); - if (document == null) { - lineNumber = value; - } else { - line = document.GetLineSegment(value); - } + } + + public int ColumnNumber { + get { + if (anchor != null) + return anchor.ColumnNumber; + 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.isEnabled = isEnabled; - this.LineNumber = lineNumber; + this.document = document; + this.isEnabled = isEnabled; + this.Location = location; } public virtual bool Click(SWF.Control parent, SWF.MouseEventArgs e) diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/BookmarkManager.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/BookmarkManager.cs index 8ffa35f14e..7105065671 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/BookmarkManager.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/BookmarkManager/BookmarkManager.cs @@ -14,7 +14,7 @@ namespace ICSharpCode.TextEditor.Document { public interface IBookmarkFactory { - Bookmark CreateBookmark(IDocument document, int lineNumber); + Bookmark CreateBookmark(IDocument document, TextLocation location); } /// @@ -50,46 +50,32 @@ namespace ICSharpCode.TextEditor.Document internal BookmarkManager(IDocument document, LineManager lineTracker) { 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; - - public IBookmarkFactory Factory { - get { - return factory; - } - set { - factory = value; - } - } + /// + /// Gets/Sets the bookmark factory used to create bookmarks for "ToggleMarkAt". + /// + public IBookmarkFactory Factory { get; set;} /// - /// Sets the mark at the line lineNr if it is not set, if the + /// Sets the mark at the line location.Line if it is not set, if the /// line is already marked the mark is cleared. /// - public void ToggleMarkAt(int lineNr) + public void ToggleMarkAt(TextLocation location) { Bookmark newMark; - if (factory != null) - newMark = factory.CreateBookmark(document, lineNr); - else - newMark = new Bookmark(document, lineNr); + if (Factory != null) { + newMark = Factory.CreateBookmark(document, location); + } else { + newMark = new Bookmark(document, location); + } Type newMarkType = newMark.GetType(); for (int i = 0; i < bookmark.Count; ++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); OnRemoved(new BookmarkEventArgs(mark)); return; diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs index 0147e407bb..7408e296f5 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs @@ -55,11 +55,57 @@ namespace ICSharpCode.TextEditor.Document if(indentation.Length > 0) { string newLineText = indentation + TextUtilities.GetLineAsString(textArea.Document, lineNumber).Trim(); LineSegment oldLine = textArea.Document.GetLineSegment(lineNumber); - textArea.Document.Replace(oldLine.Offset, oldLine.Length, newLineText); + SmartReplaceLine(textArea.Document, oldLine, newLineText); } return indentation.Length; } + static readonly char[] whitespaceChars = {' ', '\t'}; + + /// + /// 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. + /// + 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); + } + } + /// /// Could be overwritten to define more complex indenting. /// diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/DeferredEventList.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/DeferredEventList.cs new file mode 100644 index 0000000000..0ab08f1322 --- /dev/null +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/DeferredEventList.cs @@ -0,0 +1,44 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// A list of events that are fired after the line manager has finished working. + /// + struct DeferredEventList + { + internal List removedLines; + internal List textAnchor; + + public void AddRemovedLine(LineSegment line) + { + if (removedLines == null) + removedLines = new List(); + removedLines.Add(line); + } + + public void AddDeletedAnchor(TextAnchor anchor) + { + if (textAnchor == null) + textAnchor = new List(); + textAnchor.Add(anchor); + } + + public void RaiseEvents() + { + // removedLines is raised by the LineManager + if (textAnchor != null) { + foreach (TextAnchor a in textAnchor) { + a.RaiseDeleted(); + } + } + } + } +} diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineManager.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineManager.cs index f114b304de..d00f136b08 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineManager.cs +++ b/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) { -// 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 oldNumberOfLines = this.TotalNumberOfLines; - List removedLines = RemoveInternal(offset, length); + DeferredEventList deferredEventList = new DeferredEventList(); + RemoveInternal(ref deferredEventList, offset, length); int numberOfLinesAfterRemoving = this.TotalNumberOfLines; if (!string.IsNullOrEmpty(text)) { InsertInternal(offset, text); @@ -94,33 +95,34 @@ namespace ICSharpCode.TextEditor.Document // Only fire events after RemoveInternal+InsertInternal finished completely: // Otherwise we would expose inconsistent state to the event handlers. RunHighlighter(lineStart, 1 + Math.Max(0, this.TotalNumberOfLines - numberOfLinesAfterRemoving)); - if (removedLines != null) { - foreach (LineSegment ls in removedLines) + if (deferredEventList.removedLines != null) { + foreach (LineSegment ls in deferredEventList.removedLines) OnLineDeleted(new LineEventArgs(document, ls)); } if (this.TotalNumberOfLines != oldNumberOfLines) { OnLineCountChanged(new LineCountChangeEventArgs(document, lineStart, this.TotalNumberOfLines - oldNumberOfLines)); } + deferredEventList.RaiseEvents(); } - List RemoveInternal(int offset, int length) + void RemoveInternal(ref DeferredEventList deferredEventList, int offset, int length) { Debug.Assert(length >= 0); - if (length == 0) return null; + if (length == 0) return; LineSegmentTree.Enumerator it = lineCollection.GetEnumeratorForOffset(offset); LineSegment startSegment = it.Current; int startSegmentOffset = startSegment.Offset; if (offset + length < startSegmentOffset + startSegment.TotalLength) { // 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); - return null; + return; } // merge startSegment with another line segment because startSegment's delimiter was deleted // 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); + startSegment.RemovedLinePart(ref deferredEventList, offset - startSegmentOffset, charactersRemovedInStartLine); 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 // end of the document SetSegmentLength(startSegment, startSegment.TotalLength - length); - return null; + return; } int endSegmentOffset = endSegment.Offset; 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); SetSegmentLength(startSegment, startSegment.TotalLength - charactersRemovedInStartLine + charactersLeftInEndLine); startSegment.DelimiterLength = endSegment.DelimiterLength; // remove all segments between startSegment (excl.) and endSegment (incl.) it.MoveNext(); - List removedLines = new List(); LineSegment segmentToRemove; do { segmentToRemove = it.Current; it.MoveNext(); lineCollection.RemoveSegment(segmentToRemove); - removedLines.Add(segmentToRemove); - segmentToRemove.Deleted(); + segmentToRemove.Deleted(ref deferredEventList); } while (segmentToRemove != endSegment); - return removedLines; } void InsertInternal(int offset, string text) diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegment.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegment.cs index 554df72aea..2370e74dee 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegment.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/LineManager/LineSegment.cs @@ -117,6 +117,8 @@ namespace ICSharpCode.TextEditor.Document public TextAnchor CreateAnchor(int column) { + if (column < 0 || column > Length) + throw new ArgumentOutOfRangeException("column"); TextAnchor anchor = new TextAnchor(this, column); AddAnchor(anchor); return anchor; @@ -135,13 +137,13 @@ namespace ICSharpCode.TextEditor.Document /// /// Is called when the LineSegment is deleted. /// - internal void Deleted() + internal void Deleted(ref DeferredEventList deferredEventList) { //Console.WriteLine("Deleted"); treeEntry = LineSegmentTree.Enumerator.Invalid; if (anchors != null) { foreach (TextAnchor a in anchors) { - a.Deleted(); + a.Delete(ref deferredEventList); } anchors = null; } @@ -150,7 +152,7 @@ namespace ICSharpCode.TextEditor.Document /// /// Is called when a part of the line is removed. /// - internal void RemovedLinePart(int startColumn, int length) + internal void RemovedLinePart(ref DeferredEventList deferredEventList, int startColumn, int length) { if (length == 0) return; @@ -166,7 +168,7 @@ namespace ICSharpCode.TextEditor.Document } else { if (deletedAnchors == null) deletedAnchors = new List(); - a.Deleted(); + a.Delete(ref deferredEventList); deletedAnchors.Add(a); } } diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextAnchor.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextAnchor.cs index 066a316daa..8747e3c711 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextAnchor.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextAnchor.cs @@ -85,9 +85,20 @@ namespace ICSharpCode.TextEditor.Document /// 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; + deferredEventList.AddDeletedAnchor(this); + } + + internal void RaiseDeleted() + { + if (Deleted != null) + Deleted(this, EventArgs.Empty); } internal TextAnchor(LineSegment lineSegment, int columnNumber) diff --git a/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs b/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs index be9be3ebc7..dbb530dfc0 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs +++ b/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs @@ -26,7 +26,7 @@ namespace ICSharpCode.SharpDevelop.Debugging } set { isHealthy = value; - if (Document != null && !Line.IsDeleted) { + if (Document != null && !Anchor.IsDeleted) { Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, LineNumber)); Document.CommitUpdate(); } @@ -38,7 +38,7 @@ namespace ICSharpCode.SharpDevelop.Debugging 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() { - if (LineNumber >= Document.TotalNumberOfLines) - LineNumber = Document.TotalNumberOfLines - 1; - LineSegment lineSeg = Document.GetLineSegment(LineNumber); + LineSegment lineSeg = Anchor.Line; TextMarker marker = new TextMarker(lineSeg.Offset, lineSeg.Length, TextMarkerType.SolidBlock, defaultColor, Color.White); Document.MarkerStrategy.AddMarker(marker); return marker; diff --git a/src/Main/Base/Project/Src/Services/Debugger/CurrentLineBookmark.cs b/src/Main/Base/Project/Src/Services/Debugger/CurrentLineBookmark.cs index d494f42f6c..1e4bd9fd46 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/CurrentLineBookmark.cs +++ b/src/Main/Base/Project/Src/Services/Debugger/CurrentLineBookmark.cs @@ -43,7 +43,7 @@ namespace ICSharpCode.SharpDevelop.Debugging endColumn = makerEndColumn; 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.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, startLine - 1, endLine - 1)); 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.IsVisibleInBookmarkPad = false; diff --git a/src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs b/src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs index 6d1d1a7303..38aca55462 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs +++ b/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))) { 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.CommitUpdate(); break; } + column++; } } diff --git a/src/Main/Base/Project/Src/TextEditor/Bookmarks/Bookmark.cs b/src/Main/Base/Project/Src/TextEditor/Bookmarks/Bookmark.cs index 486f9f426a..57a6cf06f7 100644 --- a/src/Main/Base/Project/Src/TextEditor/Bookmarks/Bookmark.cs +++ b/src/Main/Base/Project/Src/TextEditor/Bookmarks/Bookmark.cs @@ -18,7 +18,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks [TypeConverter(typeof(BookmarkConverter))] 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; } @@ -96,7 +96,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks /// 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(); } @@ -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); } } } diff --git a/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkConverter.cs b/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkConverter.cs index 9e19244d5a..ab41b1ecaa 100644 --- a/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkConverter.cs +++ b/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkConverter.cs @@ -5,6 +5,7 @@ // $Revision$ // +using ICSharpCode.TextEditor; using System; using System.ComponentModel; using System.Globalization; @@ -27,20 +28,25 @@ namespace ICSharpCode.SharpDevelop.Bookmarks { if (value is string) { string[] v = ((string)value).Split('|'); + if (v.Length != 5) + return null; string fileName = v[1]; int lineNumber = int.Parse(v[2], culture); + int columnNumber = int.Parse(v[3], culture); if (lineNumber < 0) return null; + if (columnNumber < 0) + return null; SDBookmark bookmark; switch (v[0]) { case "Breakpoint": - bookmark = new Debugging.BreakpointBookmark(fileName, null, lineNumber); + bookmark = new Debugging.BreakpointBookmark(fileName, null, new TextLocation(columnNumber, lineNumber)); break; default: - bookmark = new SDBookmark(fileName, null, lineNumber); + bookmark = new SDBookmark(fileName, null, new TextLocation(columnNumber, lineNumber)); break; } - bookmark.IsEnabled = bool.Parse(v[3]); + bookmark.IsEnabled = bool.Parse(v[4]); return bookmark; } else { return base.ConvertFrom(context, culture, value); @@ -62,6 +68,8 @@ namespace ICSharpCode.SharpDevelop.Bookmarks b.Append('|'); b.Append(bookmark.LineNumber); b.Append('|'); + b.Append(bookmark.ColumnNumber); + b.Append('|'); b.Append(bookmark.IsEnabled.ToString()); return b.ToString(); } else { diff --git a/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs b/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs index cee5478c65..e52291e4e2 100644 --- a/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs +++ b/src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks public static void AddMark(SDBookmark bookmark) { + if (bookmark == null) return; if (bookmarks.Contains(bookmark)) return; if (bookmarks.Exists(b => IsEqualBookmark(b, bookmark))) return; bookmarks.Add(bookmark); diff --git a/src/Main/Base/Project/Src/TextEditor/Bookmarks/ClassMemberBookmark.cs b/src/Main/Base/Project/Src/TextEditor/Bookmarks/ClassMemberBookmark.cs index 8902f1c92b..a7c037d855 100644 --- a/src/Main/Base/Project/Src/TextEditor/Bookmarks/ClassMemberBookmark.cs +++ b/src/Main/Base/Project/Src/TextEditor/Bookmarks/ClassMemberBookmark.cs @@ -32,20 +32,14 @@ namespace ICSharpCode.SharpDevelop.Bookmarks } public ClassMemberBookmark(IDocument document, IMember member) - : base(document, GetLineNumberFromMember(document, member)) + : base(document, GetTextLocationFromMember(document, member)) { this.member = member; } - static int GetLineNumberFromMember(IDocument document, IMember member) + static TextLocation GetTextLocationFromMember(IDocument document, IMember member) { - int line = member.Region.BeginLine - 1; - if (line < 0) - return 0; - else if (document != null && line >= document.TotalNumberOfLines) - return document.TotalNumberOfLines - 1; - else - return line; + return new TextLocation(member.Region.BeginColumn - 1, member.Region.BeginLine - 1); } public const string ContextMenuPath = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassMemberContextMenu"; @@ -84,20 +78,14 @@ namespace ICSharpCode.SharpDevelop.Bookmarks } public ClassBookmark(IDocument document, IClass @class) - : base(document, GetLineNumberFromClass(document, @class)) + : base(document, GetTextLocationFromClass(document, @class)) { this.@class = @class; } - static int GetLineNumberFromClass(IDocument document, IClass @class) + static TextLocation GetTextLocationFromClass(IDocument document, IClass @class) { - int line = @class.Region.BeginLine - 1; - if (line < 0) - return 0; - else if (document != null && line >= document.TotalNumberOfLines) - return document.TotalNumberOfLines - 1; - else - return line; + return new TextLocation(@class.Region.BeginColumn - 1, @class.Region.BeginLine - 1); } public const string ContextMenuPath = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassBookmarkContextMenu";