From 8570e8f7e7e9da662b9d1e730e540309668d1ecc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 30 Aug 2009 17:24:17 +0000 Subject: [PATCH] - implemented IBracketSearcher for C# - added BracketHighlightRenderer (disabled by default) - renamed ErrorDrawer to ErrorPainter git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4840 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/CSharpBinding.csproj | 1 + .../Project/Src/CSharpBracketSearcher.cs | 342 ++++++++++++++++++ .../Project/Src/CSharpLanguageBinding.cs | 4 + .../CSharpFormattingStrategy.cs | 1 - .../AvalonEdit.AddIn/AvalonEdit.AddIn.csproj | 1 + .../Src/BracketHighlightRenderer.cs | 67 ++++ .../AvalonEdit.AddIn/Src/CodeEditor.cs | 123 ++++--- .../Rendering/TextView.cs | 13 + .../Project/ICSharpCode.SharpDevelop.csproj | 3 +- .../Project/Src/Editor/IBracketSearcher.cs | 55 +++ .../AggregatedLanguageBinding.cs | 11 + .../LanguageBinding/DefaultLanguageBinding.cs | 6 + .../LanguageBinding/ILanguageBinding.cs | 7 + .../Tasks/{ErrorDrawer.cs => ErrorPainter.cs} | 6 +- 14 files changed, 582 insertions(+), 58 deletions(-) create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpBracketSearcher.cs create mode 100644 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/BracketHighlightRenderer.cs create mode 100644 src/Main/Base/Project/Src/Editor/IBracketSearcher.cs rename src/Main/Base/Project/Src/Services/Tasks/{ErrorDrawer.cs => ErrorPainter.cs} (96%) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 07088239aa..4bd679437f 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -55,6 +55,7 @@ + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpBracketSearcher.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpBracketSearcher.cs new file mode 100644 index 0000000000..2222586e03 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpBracketSearcher.cs @@ -0,0 +1,342 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using ICSharpCode.SharpDevelop.Editor; + +namespace CSharpBinding +{ + /// + /// Description of CSharpBracketSearcher. + /// + public class CSharpBracketSearcher : IBracketSearcher + { + string openingBrackets = "([{<"; + string closingBrackets = ")]}>"; + + public BracketSearchResult SearchBracket(IDocument document, int offset) + { + if (offset > 0) { + char c = document.GetCharAt(offset - 1); + int index = openingBrackets.IndexOf(c); + int otherOffset = -1; + if (index > -1) + otherOffset = SearchBracketForward(document, offset, openingBrackets[index], closingBrackets[index]); + index = closingBrackets.IndexOf(c); + if (index > -1) + otherOffset = SearchBracketBackward(document, offset - 2, openingBrackets[index], closingBrackets[index]); + + if (otherOffset > -1) + return new BracketSearchResult(Math.Min(offset - 1, otherOffset), 1, Math.Max(offset - 1, otherOffset), 1); + } + + return null; + } + + #region SearchBracket helper functions + static int ScanLineStart(IDocument document, int offset) + { + for (int i = offset - 1; i > 0; --i) { + if (document.GetCharAt(i) == '\n') + return i + 1; + } + return 0; + } + + /// + /// Gets the type of code at offset.
+ /// 0 = Code,
+ /// 1 = Comment,
+ /// 2 = String
+ /// Block comments and multiline strings are not supported. + ///
+ static int GetStartType(IDocument document, int linestart, int offset) + { + bool inString = false; + bool inChar = false; + bool verbatim = false; + for(int i = linestart; i < offset; i++) { + switch (document.GetCharAt(i)) { + case '/': + if (!inString && !inChar && i + 1 < document.TextLength) { + if (document.GetCharAt(i + 1) == '/') { + return 1; + } + } + break; + case '"': + if (!inChar) { + if (inString && verbatim) { + if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { + ++i; // skip escaped quote + inString = false; // let the string go on + } else { + verbatim = false; + } + } else if (!inString && i > 0 && document.GetCharAt(i - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!inString) inChar = !inChar; + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++i; // skip next character + break; + } + } + return (inString || inChar) ? 2 : 0; + } + #endregion + + #region SearchBracketBackward + int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + if (offset + 1 >= document.TextLength) return -1; + // this method parses a c# document backwards to find the matching bracket + + // first try "quick find" - find the matching bracket if there is no string/comment in the way + int quickResult = QuickSearchBracketBackward(document, offset, openBracket, closingBracket); + if (quickResult >= 0) return quickResult; + + // we need to parse the line from the beginning, so get the line start position + int linestart = ScanLineStart(document, offset + 1); + + // we need to know where offset is - in a string/comment or in normal code? + // ignore cases where offset is in a block comment + int starttype = GetStartType(document, linestart, offset + 1); + if (starttype != 0) { + return -1; // start position is in a comment/string + } + + // I don't see any possibility to parse a C# document backwards... + // We have to do it forwards and push all bracket positions on a stack. + Stack bracketStack = new Stack(); + bool blockComment = false; + bool lineComment = false; + bool inChar = false; + bool inString = false; + bool verbatim = false; + + for(int i = 0; i <= offset; ++i) { + char ch = document.GetCharAt(i); + switch (ch) { + case '\r': + case '\n': + lineComment = false; + inChar = false; + if (!verbatim) inString = false; + break; + case '/': + if (blockComment) { + Debug.Assert(i > 0); + if (document.GetCharAt(i - 1) == '*') { + blockComment = false; + } + } + if (!inString && !inChar && i + 1 < document.TextLength) { + if (!blockComment && document.GetCharAt(i + 1) == '/') { + lineComment = true; + } + if (!lineComment && document.GetCharAt(i + 1) == '*') { + blockComment = true; + } + } + break; + case '"': + if (!(inChar || lineComment || blockComment)) { + if (inString && verbatim) { + if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { + ++i; // skip escaped quote + inString = false; // let the string go + } else { + verbatim = false; + } + } else if (!inString && offset > 0 && document.GetCharAt(i - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!(inString || lineComment || blockComment)) { + inChar = !inChar; + } + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++i; // skip next character + break; + default : + if (ch == openBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + bracketStack.Push(i); + } + } else if (ch == closingBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + if (bracketStack.Count > 0) + bracketStack.Pop(); + } + } + break; + } + } + if (bracketStack.Count > 0) return (int)bracketStack.Pop(); + return -1; + } + #endregion + + #region SearchBracketForward + int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + bool inString = false; + bool inChar = false; + bool verbatim = false; + + bool lineComment = false; + bool blockComment = false; + + if (offset < 0) return -1; + + // first try "quick find" - find the matching bracket if there is no string/comment in the way + int quickResult = QuickSearchBracketForward(document, offset, openBracket, closingBracket); + if (quickResult >= 0) return quickResult; + + // we need to parse the line from the beginning, so get the line start position + int linestart = ScanLineStart(document, offset); + + // we need to know where offset is - in a string/comment or in normal code? + // ignore cases where offset is in a block comment + int starttype = GetStartType(document, linestart, offset); + if (starttype != 0) return -1; // start position is in a comment/string + + int brackets = 1; + + while (offset < document.TextLength) { + char ch = document.GetCharAt(offset); + switch (ch) { + case '\r': + case '\n': + lineComment = false; + inChar = false; + if (!verbatim) inString = false; + break; + case '/': + if (blockComment) { + Debug.Assert(offset > 0); + if (document.GetCharAt(offset - 1) == '*') { + blockComment = false; + } + } + if (!inString && !inChar && offset + 1 < document.TextLength) { + if (!blockComment && document.GetCharAt(offset + 1) == '/') { + lineComment = true; + } + if (!lineComment && document.GetCharAt(offset + 1) == '*') { + blockComment = true; + } + } + break; + case '"': + if (!(inChar || lineComment || blockComment)) { + if (inString && verbatim) { + if (offset + 1 < document.TextLength && document.GetCharAt(offset + 1) == '"') { + ++offset; // skip escaped quote + inString = false; // let the string go + } else { + verbatim = false; + } + } else if (!inString && offset > 0 && document.GetCharAt(offset - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!(inString || lineComment || blockComment)) { + inChar = !inChar; + } + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++offset; // skip next character + break; + default : + if (ch == openBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + ++brackets; + } + } else if (ch == closingBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + --brackets; + if (brackets == 0) { + return offset; + } + } + } + break; + } + ++offset; + } + return -1; + } + #endregion + + int QuickSearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + int brackets = -1; + // first try "quick find" - find the matching bracket if there is no string/comment in the way + for (int i = offset; i >= 0; --i) { + char ch = document.GetCharAt(i); + if (ch == openBracket) { + ++brackets; + if (brackets == 0) return i; + } else if (ch == closingBracket) { + --brackets; + } else if (ch == '"') { + break; + } else if (ch == '\'') { + break; + } else if (ch == '/' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + if (document.GetCharAt(i - 1) == '*') break; + } + } + return -1; + } + + int QuickSearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + int brackets = 1; + // try "quick find" - find the matching bracket if there is no string/comment in the way + for (int i = offset; i < document.TextLength; ++i) { + char ch = document.GetCharAt(i); + if (ch == openBracket) { + ++brackets; + } else if (ch == closingBracket) { + --brackets; + if (brackets == 0) return i; + } else if (ch == '"') { + break; + } else if (ch == '\'') { + break; + } else if (ch == '/' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + } else if (ch == '*' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + } + } + return -1; + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs index 4db22c1da6..b0c1793915 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs @@ -25,5 +25,9 @@ namespace CSharpBinding public override LanguageProperties Properties { get { return LanguageProperties.CSharp; } } + + public override IBracketSearcher BracketSearcher { + get { return new CSharpBracketSearcher(); } + } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs index 5be5c8e985..0c40160a8b 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs @@ -541,7 +541,6 @@ namespace CSharpBinding.FormattingStrategy { SurroundSelectionWithSingleLineComment(editor, "//"); } - /* #region SearchBracketBackward public override int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj index 21605eaf35..249af29c30 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj @@ -65,6 +65,7 @@ + diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/BracketHighlightRenderer.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/BracketHighlightRenderer.cs new file mode 100644 index 0000000000..2bda1e5556 --- /dev/null +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/BracketHighlightRenderer.cs @@ -0,0 +1,67 @@ +// +// +// +// +// $Revision$ +// +using ICSharpCode.AvalonEdit.Document; +using System; +using System.Windows.Media; +using ICSharpCode.AvalonEdit.Rendering; +using ICSharpCode.SharpDevelop.Editor; + +namespace ICSharpCode.AvalonEdit.AddIn +{ + public class BracketHighlightRenderer : IBackgroundRenderer + { + BracketSearchResult result; + Pen borderPen; + TextView textView; + + public void SetHighlight(BracketSearchResult result) + { + this.result = result; + var layer = textView.GetKnownLayer(Layer); + if (layer != null) + layer.InvalidateVisual(); + } + + public BracketHighlightRenderer(TextView textView) + { + if (textView == null) + throw new ArgumentNullException("textView"); + + this.borderPen = new Pen(Brushes.Blue, 1); + this.borderPen.Freeze(); + this.textView = textView; + + //this.textView.BackgroundRenderers.Add(this); + } + + public KnownLayer Layer { + get { + return KnownLayer.Selection; + } + } + + public void Draw(TextView textView, DrawingContext drawingContext) + { + if (this.result == null) + return; + + BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder(); + + builder.CornerRadius = 1; + + builder.AddSegment(textView, new TextSegment() { StartOffset = result.OpeningBracketOffset, Length = result.OpeningBracketLength }); + builder.AddSegment(textView, new TextSegment() { StartOffset = result.ClosingBracketOffset, Length = result.ClosingBracketLength }); + + PathGeometry geometry = builder.CreateGeometry(); + + if (geometry != null) { + geometry.Freeze(); + drawingContext.DrawGeometry(Brushes.LightBlue, borderPen, geometry); + } + } + } +} diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index 8cc908cb6d..22e966b95c 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -51,7 +51,10 @@ namespace ICSharpCode.AvalonEdit.AddIn CodeEditorAdapter secondaryTextEditorAdapter; readonly IconBarManager iconBarManager; readonly TextMarkerService textMarkerService; - readonly ErrorDrawer errorDrawer; + readonly ErrorPainter errorPainter; + + BracketHighlightRenderer primaryBracketRenderer; + BracketHighlightRenderer secondaryBracketRenderer; public TextEditor PrimaryTextEditor { get { return primaryTextEditor; } @@ -146,7 +149,9 @@ namespace ICSharpCode.AvalonEdit.AddIn primaryTextEditorAdapter = (CodeEditorAdapter)primaryTextEditor.TextArea.GetService(typeof(ITextEditor)); Debug.Assert(primaryTextEditorAdapter != null); - this.errorDrawer = new ErrorDrawer(primaryTextEditorAdapter); + this.errorPainter = new ErrorPainter(primaryTextEditorAdapter); + + this.primaryBracketRenderer = new BracketHighlightRenderer(primaryTextEditor.TextArea.TextView); this.Document = primaryTextEditor.Document; primaryTextEditor.SetBinding(TextEditor.DocumentProperty, new Binding("Document") { Source = this }); @@ -169,13 +174,13 @@ namespace ICSharpCode.AvalonEdit.AddIn textEditor.Background = Brushes.White; textEditor.FontFamily = new FontFamily("Consolas"); textEditor.FontSize = 13; - textEditor.TextArea.TextEntering += TextArea_TextEntering; - textEditor.TextArea.TextEntered += TextArea_TextEntered; - textEditor.MouseHover += textEditor_MouseHover; - textEditor.MouseHoverStopped += textEditor_MouseHoverStopped; - textEditor.MouseLeave += textEditor_MouseLeave; - textView.MouseDown += textView_MouseDown; - textEditor.TextArea.Caret.PositionChanged += caret_PositionChanged; + textEditor.TextArea.TextEntering += TextAreaTextEntering; + textEditor.TextArea.TextEntered += TextAreaTextEntered; + textEditor.MouseHover += TextEditorMouseHover; + textEditor.MouseHoverStopped += TextEditorMouseHoverStopped; + textEditor.MouseLeave += TextEditorMouseLeave; + textView.MouseDown += TextViewMouseDown; + textEditor.TextArea.Caret.PositionChanged += TextAreaCaretPositionChanged; textEditor.TextArea.DefaultInputHandler.CommandBindings.Add( new CommandBinding(CustomCommands.CtrlSpaceCompletion, OnCodeCompletion)); @@ -191,8 +196,8 @@ namespace ICSharpCode.AvalonEdit.AddIn textView.Services.AddService(typeof(ISyntaxHighlighter), new AvalonEditSyntaxHighlighterAdapter(textView)); - textEditor.TextArea.TextView.MouseRightButtonDown += textEditor_TextArea_TextView_MouseRightButtonDown; - textEditor.TextArea.TextView.ContextMenuOpening += textEditor_TextArea_TextView_ContextMenuOpening; + textEditor.TextArea.TextView.MouseRightButtonDown += TextViewMouseRightButtonDown; + textEditor.TextArea.TextView.ContextMenuOpening += TextViewContextMenuOpening; return textEditor; } @@ -203,13 +208,13 @@ namespace ICSharpCode.AvalonEdit.AddIn textEditor.TextArea.LeftMargins.OfType().Single().TextView = null; } - void textEditor_TextArea_TextView_ContextMenuOpening(object sender, ContextMenuEventArgs e) + void TextViewContextMenuOpening(object sender, ContextMenuEventArgs e) { ITextEditor adapter = GetAdapterFromSender(sender); MenuService.CreateContextMenu(adapter, contextMenuPath).IsOpen = true; } - void textEditor_TextArea_TextView_MouseRightButtonDown(object sender, MouseButtonEventArgs e) + void TextViewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { TextEditor textEditor = GetTextEditorFromSender(sender); var position = textEditor.GetPositionFromPoint(e.GetPosition(textEditor)); @@ -246,6 +251,8 @@ namespace ICSharpCode.AvalonEdit.AddIn SetRow(secondaryTextEditor, 2); this.Children.Add(secondaryTextEditor); + this.secondaryBracketRenderer = new BracketHighlightRenderer(secondaryTextEditor.TextArea.TextView); + secondaryTextEditorAdapter.FileNameChanged(); } else { // remove secondary editor @@ -254,6 +261,7 @@ namespace ICSharpCode.AvalonEdit.AddIn secondaryTextEditor = null; secondaryTextEditorAdapter.Language.Detach(); secondaryTextEditorAdapter = null; + this.secondaryBracketRenderer = null; this.RowDefinitions.RemoveAt(this.RowDefinitions.Count - 1); } } @@ -261,7 +269,7 @@ namespace ICSharpCode.AvalonEdit.AddIn public event EventHandler CaretPositionChanged; bool caretPositionWasChanged; - void caret_PositionChanged(object sender, EventArgs e) + void TextAreaCaretPositionChanged(object sender, EventArgs e) { Debug.Assert(sender is Caret); if (sender == this.ActiveTextEditor.TextArea.Caret) { @@ -286,13 +294,24 @@ namespace ICSharpCode.AvalonEdit.AddIn quickClassBrowser.SelectItemAtCaretPosition(this.ActiveTextEditorAdapter.Caret.Position); } var caret = this.ActiveTextEditor.TextArea.Caret; + + var activeAdapter = this.ActiveTextEditorAdapter; + + if (activeAdapter.Language != null) { + var bracketSearchResult = activeAdapter.Language.BracketSearcher.SearchBracket(activeAdapter.Document, activeAdapter.Caret.Offset); +// if (activeAdapter == primaryTextEditorAdapter) +// this.primaryBracketRenderer.SetHighlight(bracketSearchResult); +// else +// this.secondaryBracketRenderer.SetHighlight(bracketSearchResult); + } + StatusBarService.SetCaretPosition(caret.Column, caret.Line, caret.Column); CaretPositionChanged.RaiseEvent(this, EventArgs.Empty); } public void JumpTo(int line, int column) { - tryCloseExistingPopup(true); + TryCloseExistingPopup(true); // the adapter sets the caret position and takes care of scrolling this.ActiveTextEditorAdapter.JumpTo(line, column); @@ -302,7 +321,7 @@ namespace ICSharpCode.AvalonEdit.AddIn ToolTip toolTip; Popup popup; - void textEditor_MouseHover(object sender, MouseEventArgs e) + void TextEditorMouseHover(object sender, MouseEventArgs e) { TextEditor textEditor = (TextEditor)sender; ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(GetAdapter(textEditor)); @@ -312,18 +331,21 @@ namespace ICSharpCode.AvalonEdit.AddIn args.LogicalPosition = AvalonEditDocumentAdapter.ToLocation(pos.Value); } - var markersAtOffset = textMarkerService.GetMarkersAtOffset(args.Editor.Document.PositionToOffset(args.LogicalPosition.Line, args.LogicalPosition.Column)); - - ITextMarker markerWithToolTip = markersAtOffset.FirstOrDefault(marker => marker.ToolTip != null); - - if (markerWithToolTip != null) { - if (toolTip == null) { - toolTip = new ToolTip(); - toolTip.Closed += toolTip_Closed; + if (args.InDocument) { + var markersAtOffset = textMarkerService.GetMarkersAtOffset(args.Editor.Document.PositionToOffset(args.LogicalPosition.Line, args.LogicalPosition.Column)); + + ITextMarker markerWithToolTip = markersAtOffset.FirstOrDefault(marker => marker.ToolTip != null); + + if (markerWithToolTip != null) { + if (toolTip == null) { + toolTip = new ToolTip(); + toolTip.Closed += ToolTipClosed; + } + toolTip.Content = markerWithToolTip.ToolTip; + toolTip.IsOpen = true; + e.Handled = true; + return; } - toolTip.Content = markerWithToolTip.ToolTip; - toolTip.IsOpen = true; - e.Handled = true; } ToolTipRequestService.RequestToolTip(args); @@ -336,14 +358,14 @@ namespace ICSharpCode.AvalonEdit.AddIn throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow); } if (popup == null) { - popup = createPopup(); + popup = CreatePopup(); } - if (tryCloseExistingPopup(false)) { + if (TryCloseExistingPopup(false)) { // when popup content decides to close, close the popup contentToShowITooltip.Closed += (closedSender, closedArgs) => { popup.IsOpen = false; }; popup.Child = (UIElement)args.ContentToShow; //ICSharpCode.SharpDevelop.Debugging.DebuggerService.CurrentDebugger.IsProcessRunningChanged - setPopupPosition(popup, textEditor, e); + SetPopupPosition(popup, textEditor, e); popup.IsOpen = true; } e.Handled = true; @@ -351,7 +373,7 @@ namespace ICSharpCode.AvalonEdit.AddIn else { if (toolTip == null) { toolTip = new ToolTip(); - toolTip.Closed += toolTip_Closed; + toolTip.Closed += ToolTipClosed; } toolTip.Content = args.ContentToShow; toolTip.IsOpen = true; @@ -363,11 +385,11 @@ namespace ICSharpCode.AvalonEdit.AddIn if (popup != null) { e.Handled = true; } - tryCloseExistingPopup(false); + TryCloseExistingPopup(false); } } - bool tryCloseExistingPopup(bool mouseClick) + bool TryCloseExistingPopup(bool mouseClick) { bool canClose = true; if (popup != null) { @@ -382,15 +404,15 @@ namespace ICSharpCode.AvalonEdit.AddIn return canClose; } - void setPopupPosition(Popup popup, TextEditor textEditor, MouseEventArgs mouseArgs) + void SetPopupPosition(Popup popup, TextEditor textEditor, MouseEventArgs mouseArgs) { - var popupPosition = getPopupPosition(textEditor, mouseArgs); + var popupPosition = GetPopupPosition(textEditor, mouseArgs); popup.HorizontalOffset = popupPosition.X; popup.VerticalOffset = popupPosition.Y; } /// Returns Popup position based on mouse position, in device independent units - Point getPopupPosition(TextEditor textEditor, MouseEventArgs mouseArgs) + Point GetPopupPosition(TextEditor textEditor, MouseEventArgs mouseArgs) { Point mousePos = mouseArgs.GetPosition(textEditor); Point positionInPixels; @@ -410,16 +432,16 @@ namespace ICSharpCode.AvalonEdit.AddIn return positionInPixels.TransformFromDevice(textEditor); } - Popup createPopup() + Popup CreatePopup() { popup = new Popup(); - popup.Closed += popup_Closed; + popup.Closed += PopupClosed; popup.Placement = PlacementMode.Absolute; popup.StaysOpen = true; return popup; } - void textEditor_MouseHoverStopped(object sender, MouseEventArgs e) + void TextEditorMouseHoverStopped(object sender, MouseEventArgs e) { if (toolTip != null) { toolTip.IsOpen = false; @@ -427,18 +449,18 @@ namespace ICSharpCode.AvalonEdit.AddIn } } - void textEditor_MouseLeave(object sender, MouseEventArgs e) + void TextEditorMouseLeave(object sender, MouseEventArgs e) { if (popup != null && !popup.IsMouseOver) { // do not close popup if mouse moved from editor to popup - tryCloseExistingPopup(false); + TryCloseExistingPopup(false); } } - void textView_MouseDown(object sender, MouseButtonEventArgs e) + void TextViewMouseDown(object sender, MouseButtonEventArgs e) { // close existing popup immediately on text editor mouse down - tryCloseExistingPopup(false); + TryCloseExistingPopup(false); if (e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.Control) { TextEditor editor = GetTextEditorFromSender(sender); var position = editor.GetPositionFromPoint(e.GetPosition(editor)); @@ -448,18 +470,13 @@ namespace ICSharpCode.AvalonEdit.AddIn } } } - - /*void textArea_GotFocus(object sender, RoutedEventArgs e) - { - // close popup whenever TextEditor.TextArea gets focus - eg. on debugger step (CodeEditor.JumpTo calls focus) - }*/ - void toolTip_Closed(object sender, RoutedEventArgs e) + void ToolTipClosed(object sender, RoutedEventArgs e) { toolTip = null; } - void popup_Closed(object sender, EventArgs e) + void PopupClosed(object sender, EventArgs e) { popup = null; } @@ -475,7 +492,7 @@ namespace ICSharpCode.AvalonEdit.AddIn } } - void TextArea_TextEntering(object sender, TextCompositionEventArgs e) + void TextAreaTextEntering(object sender, TextCompositionEventArgs e) { // don't start new code completion if there is still a completion window open if (completionWindow != null) @@ -514,7 +531,7 @@ namespace ICSharpCode.AvalonEdit.AddIn } } - void TextArea_TextEntered(object sender, TextCompositionEventArgs e) + void TextAreaTextEntered(object sender, TextCompositionEventArgs e) { if (e.Text.Length > 0 && !e.Handled) { ILanguageBinding languageBinding = GetAdapterFromSender(sender).Language; @@ -693,7 +710,7 @@ namespace ICSharpCode.AvalonEdit.AddIn if (secondaryTextEditorAdapter != null) secondaryTextEditorAdapter.Language.Detach(); - errorDrawer.Dispose(); + errorPainter.Dispose(); this.Document = null; } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs index 1de66bcd66..bd088f9c00 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs @@ -247,6 +247,19 @@ namespace ICSharpCode.AvalonEdit.Rendering get { return layers; } } + /// + /// Retrieves a known layer from the Layers collection. + /// + public UIElement GetKnownLayer(KnownLayer knownLayer) + { + foreach (UIElement layer in layers) { + LayerPosition p = LayerPosition.GetLayerPosition(layer); + if (p != null && p.KnownLayer == knownLayer) + return layer; + } + return null; + } + /// /// Inserts a new layer at a position specified relative to an existing layer. /// diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index eac97a710a..30be583409 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -98,6 +98,7 @@ + @@ -259,7 +260,7 @@ - + diff --git a/src/Main/Base/Project/Src/Editor/IBracketSearcher.cs b/src/Main/Base/Project/Src/Editor/IBracketSearcher.cs new file mode 100644 index 0000000000..b0cf77d162 --- /dev/null +++ b/src/Main/Base/Project/Src/Editor/IBracketSearcher.cs @@ -0,0 +1,55 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace ICSharpCode.SharpDevelop.Editor +{ + /// + /// Allows language specific search for matching brackets. + /// + public interface IBracketSearcher + { + /// + /// Searches for a matching bracket from the given offset to the start of the document. + /// + /// A BracketSearchResult that contains the positions and lengths of the brackets. + BracketSearchResult SearchBracket(IDocument document, int offset); + } + + public class DefaultBracketSearcher : IBracketSearcher + { + public static readonly DefaultBracketSearcher DefaultInstance = new DefaultBracketSearcher(); + + public BracketSearchResult SearchBracket(IDocument document, int offset) + { + return null; + } + } + + /// + /// Describes a pair of matching brackets found by IBracketSearcher. + /// + public class BracketSearchResult + { + public int OpeningBracketOffset { get; private set; } + + public int OpeningBracketLength { get; private set; } + + public int ClosingBracketOffset { get; private set; } + + public int ClosingBracketLength { get; private set; } + + public BracketSearchResult(int openingBracketOffset, int openingBracketLength, + int closingBracketOffset, int closingBracketLength) + { + this.OpeningBracketOffset = openingBracketOffset; + this.OpeningBracketLength = openingBracketLength; + this.ClosingBracketOffset = closingBracketOffset; + this.ClosingBracketLength = closingBracketLength; + } + } +} diff --git a/src/Main/Base/Project/Src/Services/LanguageBinding/AggregatedLanguageBinding.cs b/src/Main/Base/Project/Src/Services/LanguageBinding/AggregatedLanguageBinding.cs index b0b34b531e..ed7d0d6c61 100644 --- a/src/Main/Base/Project/Src/Services/LanguageBinding/AggregatedLanguageBinding.cs +++ b/src/Main/Base/Project/Src/Services/LanguageBinding/AggregatedLanguageBinding.cs @@ -58,5 +58,16 @@ namespace ICSharpCode.SharpDevelop foreach (ILanguageBinding binding in allBindings) binding.Detach(); } + + public IBracketSearcher BracketSearcher { + get { + foreach (ILanguageBinding binding in allBindings) { + if (binding.BracketSearcher != null) + return binding.BracketSearcher; + } + + return DefaultBracketSearcher.DefaultInstance; + } + } } } diff --git a/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs b/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs index 24ca870a77..14a407b5a6 100644 --- a/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs +++ b/src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs @@ -32,5 +32,11 @@ namespace ICSharpCode.SharpDevelop public virtual void Detach() { } + + public virtual IBracketSearcher BracketSearcher { + get { + return null; + } + } } } diff --git a/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs b/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs index 8ef1bbbf19..293b9bc6fa 100644 --- a/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs +++ b/src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs @@ -30,6 +30,13 @@ namespace ICSharpCode.SharpDevelop get; } + /// + /// Provides access to the bracket search logic for this language. + /// + IBracketSearcher BracketSearcher { + get; + } + /// /// Callback function for backend bindings to add services to ITextEditor. /// This is called when the file name of an ITextEditor changes. diff --git a/src/Main/Base/Project/Src/Services/Tasks/ErrorDrawer.cs b/src/Main/Base/Project/Src/Services/Tasks/ErrorPainter.cs similarity index 96% rename from src/Main/Base/Project/Src/Services/Tasks/ErrorDrawer.cs rename to src/Main/Base/Project/Src/Services/Tasks/ErrorPainter.cs index c1e6a805c7..ccfdf562d9 100644 --- a/src/Main/Base/Project/Src/Services/Tasks/ErrorDrawer.cs +++ b/src/Main/Base/Project/Src/Services/Tasks/ErrorPainter.cs @@ -15,12 +15,12 @@ namespace ICSharpCode.SharpDevelop /// /// Synchronizes the ITextEditors with the TaskService. It adds and removes error markers. /// - public class ErrorDrawer : IDisposable + public class ErrorPainter : IDisposable { ITextEditor textEditor; ITextMarkerService markerService; - public ErrorDrawer(ITextEditor textEditor) + public ErrorPainter(ITextEditor textEditor) { this.textEditor = textEditor; this.markerService = this.textEditor.GetService(typeof(ITextMarkerService)) as ITextMarkerService; @@ -124,7 +124,7 @@ namespace ICSharpCode.SharpDevelop markerColor = Colors.Blue; break; case TaskType.Warning: - markerColor = Colors.Yellow; + markerColor = Colors.Orange; break; }