From f1b423c05e43aca8a1f0763e7d1d4d68234adc52 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 1 Jun 2009 08:28:59 +0000 Subject: [PATCH] Ported event handler completion to ITextEditor. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4188 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/CSharpBinding.csproj | 8 +- .../Project/Src/CSharpCompletionBinding.cs | 8 +- ... => EventHandlerCompletionItemProvider.cs} | 162 ++++++++---------- .../AvalonEdit.AddIn/Src/CodeEditorAdapter.cs | 4 + .../AvalonEdit/AvalonEditTextEditorAdapter.cs | 14 +- .../CodeCompletionItemProvider.cs | 7 +- .../CodeCompletion/ICompletionItemList.cs | 8 + .../Project/Src/Editor/IFormattingStrategy.cs | 2 + .../Base/Project/Src/Editor/ITextEditor.cs | 11 +- .../Src/TextEditor/Gui/TextEditorAdapter.cs | 9 + 10 files changed, 138 insertions(+), 95 deletions(-) rename src/AddIns/BackendBindings/CSharpBinding/Project/Src/{EventHandlerCompletitionDataProvider.cs => EventHandlerCompletionItemProvider.cs} (61%) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index d224d069f6..53778776b4 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -37,12 +37,18 @@ ..\..\..\..\..\AddIns\AddIns\BackendBindings\CSharpBinding\ + + 3.0 + + + 3.0 + @@ -65,7 +71,7 @@ Always - + Configuration\GlobalAssemblyInfo.cs diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs index 1b227879e1..7507adbccb 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs @@ -5,18 +5,17 @@ // $Revision$ // -using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using System; using ICSharpCode.Core; using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.Visitors; using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom.CSharp; using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; using ICSharpCode.SharpDevelop.Dom.Refactoring; using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using AST = ICSharpCode.NRefactory.Ast; namespace CSharpBinding @@ -61,9 +60,8 @@ namespace CSharpBinding if (resolveResult != null && resolveResult.ResolvedType != null) { IClass underlyingClass = resolveResult.ResolvedType.GetUnderlyingClass(); if (underlyingClass != null && underlyingClass.IsTypeInInheritanceTree(ParserService.CurrentProjectContent.GetClass("System.MulticastDelegate", 0))) { - EventHandlerCompletitionDataProvider eventHandlerProvider = new EventHandlerCompletitionDataProvider(result.Expression, resolveResult); - eventHandlerProvider.InsertSpace = true; - editor.ShowCompletionWindow(eventHandlerProvider, ch); + EventHandlerCompletionItemProvider eventHandlerProvider = new EventHandlerCompletionItemProvider(result.Expression, resolveResult); + eventHandlerProvider.ShowCompletion(editor); return CodeCompletionKeyPressResult.Completed; } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/EventHandlerCompletitionDataProvider.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/EventHandlerCompletionItemProvider.cs similarity index 61% rename from src/AddIns/BackendBindings/CSharpBinding/Project/Src/EventHandlerCompletitionDataProvider.cs rename to src/AddIns/BackendBindings/CSharpBinding/Project/Src/EventHandlerCompletionItemProvider.cs index 6f12fa1c89..094dcc1827 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/EventHandlerCompletitionDataProvider.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/EventHandlerCompletionItemProvider.cs @@ -6,30 +6,25 @@ // using System; -using System.Collections.Generic; -using System.Windows.Forms; using System.Text; - using ICSharpCode.Core; using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom.CSharp; -using ICSharpCode.TextEditor; -using ICSharpCode.TextEditor.Actions; -using ICSharpCode.TextEditor.Document; -using ICSharpCode.TextEditor.Gui.CompletionWindow; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; +using System.Windows.Input; namespace CSharpBinding { - public class EventHandlerCompletitionDataProvider : AbstractCompletionDataProvider + public class EventHandlerCompletionItemProvider : AbstractCompletionItemProvider { string expression; ResolveResult resolveResult; IReturnType resolvedReturnType; IClass resolvedClass; - public EventHandlerCompletitionDataProvider(string expression, ResolveResult resolveResult) + public EventHandlerCompletionItemProvider(string expression, ResolveResult resolveResult) { this.expression = expression; this.resolveResult = resolveResult; @@ -37,16 +32,14 @@ namespace CSharpBinding this.resolvedClass = resolvedReturnType.GetUnderlyingClass(); } - /// - /// Generates the completion data. This method is called by the text editor control. - /// - public override ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped) + public override ICompletionItemList GenerateCompletionList(ITextEditor editor) { - List completionData = new List(); + DefaultCompletionItemList result = new DefaultCompletionItemList(); + result.InsertSpace = true; // delegate { } - completionData.Add(new DelegateCompletionData("delegate { };", 3, - "${res:CSharpBinding.InsertAnonymousMethod}")); + result.Items.Add(new DelegateCompletionItem("delegate { };", 3, + "${res:CSharpBinding.InsertAnonymousMethod}")); CSharpAmbience ambience = new CSharpAmbience(); // get eventHandler type name incl. type argument list @@ -73,8 +66,8 @@ namespace CSharpBinding // delegate(object sender, EventArgs e) { }; StringBuilder anonMethodWithParametersBuilder = new StringBuilder("delegate(").Append(parameterString.ToString()).Append(") { };"); - completionData.Add(new DelegateCompletionData(anonMethodWithParametersBuilder.ToString(), 3, - "${res:CSharpBinding.InsertAnonymousMethodWithParameters}")); + result.Items.Add(new DelegateCompletionItem(anonMethodWithParametersBuilder.ToString(), 3, + "${res:CSharpBinding.InsertAnonymousMethodWithParameters}")); // new EventHandler(ClassName_EventName); IClass callingClass = resolveResult.CallingClass; @@ -109,13 +102,13 @@ namespace CSharpBinding newHandlerCodeBuilder.Append("}"); // ...and add it to the completionData. - completionData.Add(new NewEventHandlerCompletionData( + result.Items.Add(new NewEventHandlerCompletionItem( newHandlerTextBuilder.ToString(), 2+newHandlerName.Length, newHandlerName.Length, "new " + eventHandlerFullyQualifiedTypeName + "(" + newHandlerName + StringParser.Parse(")\n${res:CSharpBinding.GenerateNewHandlerInstructions}\n") - + CodeCompletionData.ConvertDocumentation(resolvedClass.Documentation), + + CodeCompletionItem.ConvertDocumentation(resolvedClass.Documentation), resolveResult, newHandlerCodeBuilder.ToString() )); @@ -139,12 +132,13 @@ namespace CSharpBinding } } if (ok) { - completionData.Add(new CodeCompletionData(method)); + result.Items.Add(new CodeCompletionItem(method)); } } } } - return completionData.ToArray(); + result.SortItems(); + return result; } string BuildHandlerName() @@ -171,107 +165,103 @@ namespace CSharpBinding return handlerNameBuilder.ToString(); } - private class DelegateCompletionData : DefaultCompletionData + sealed class DelegateCompletionItem : DefaultCompletionItem { int cursorOffset; - public DelegateCompletionData(string text, int cursorOffset, string documentation) - : base(text, StringParser.Parse(documentation), ClassBrowserIconService.Delegate.ImageIndex) + public DelegateCompletionItem(string text, int cursorOffset, string documentation) + : base(text) { this.cursorOffset = cursorOffset; + this.Description = StringParser.Parse(documentation); + this.Image = ClassBrowserIconService.Delegate; } - public override bool InsertAction(TextArea textArea, char ch) + public override void Complete(CompletionContext context) { - bool r = base.InsertAction(textArea, ch); - textArea.Caret.Column -= cursorOffset; - return r; + base.Complete(context); + context.Editor.Caret.Column -= cursorOffset; } } - private class NewEventHandlerCompletionData : DefaultCompletionData + sealed class NewEventHandlerCompletionItem : DefaultCompletionItem { int selectionBeginOffset; int selectionLength; - TextArea textArea; ResolveResult resolveResult; string newHandlerCode; - public NewEventHandlerCompletionData(string text, int selectionBeginOffset, int selectionLength, string documentation, ResolveResult resolveResult, string newHandlerCode) - : base(text, StringParser.Parse(documentation), ClassBrowserIconService.Delegate.ImageIndex) + ITextEditor editor; + + public NewEventHandlerCompletionItem(string text, int selectionBeginOffset, int selectionLength, string documentation, ResolveResult resolveResult, string newHandlerCode) + : base(text) { this.selectionBeginOffset = selectionBeginOffset; this.selectionLength = selectionLength; this.resolveResult = resolveResult; this.newHandlerCode = newHandlerCode; + + this.Description = StringParser.Parse(documentation); + this.Image = ClassBrowserIconService.Delegate; } - public override bool InsertAction(TextArea textArea, char ch) + public override void Complete(CompletionContext context) { - bool r = base.InsertAction(textArea, ch); + base.Complete(context); + // save a reference to the relevant textArea so that we can remove our event handlers after the next keystroke + editor = context.Editor; // select suggested name - textArea.Caret.Column -= this.selectionBeginOffset; - int selectBegin = textArea.Caret.Offset; - textArea.SelectionManager.SetSelection( - textArea.Document.OffsetToPosition(selectBegin), - textArea.Document.OffsetToPosition(selectBegin + this.selectionLength)); + editor.Caret.Column -= this.selectionBeginOffset; + editor.Select(editor.Caret.Offset, this.selectionLength); // TODO: refactor ToolTip architecture to allow for showing a tooltip relative to the current caret position so that we can show our "press TAB to create this method" text as a text-based tooltip // TODO: skip the auto-insert step if the method already exists, or change behavior so that it moves the caret inside the existing method. - // attatch our keydown filter to catch the next character pressed - textArea.PreviewKeyDown += new PreviewKeyDownEventHandler(NewEventHandlerPreviewKeyDown); - - // save a reference to the relevant textArea so that we can remove our keydown filter after the next keystroke - this.textArea = textArea; - - return r; + // attach our keydown filter to catch the next character pressed + editor.SelectionChanged += EditorSelectionChanged; + editor.KeyPress += EditorKeyPress; } - public void NewEventHandlerPreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + + void RemoveEventHandlers() { - if (e.KeyCode == Keys.Tab - || e.KeyCode == Keys.Enter - || e.KeyCode == Keys.Return) { - - textArea.BeginUpdate(); - textArea.Document.UndoStack.StartUndoGroup(); - - textArea.SelectionManager.ClearSelection(); - - // is there a better way to calculate the optimal insertion point? - DomRegion region = resolveResult.CallingMember.BodyRegion; - textArea.Caret.Line = region.EndLine - 1; - textArea.Caret.Column = region.EndColumn; - - textArea.InsertString(this.newHandlerCode); - - textArea.Document.FormattingStrategy.IndentLines(textArea, region.EndLine, textArea.Caret.Line); - - textArea.Caret.Line -= 1; - - LineSegment segment = textArea.Document.GetLineSegment(textArea.Caret.Line); - - textArea.SelectionManager.SetSelection( - textArea.Document.OffsetToPosition(TextUtilities.GetFirstNonWSChar(textArea.Document, segment.Offset)), - textArea.Document.OffsetToPosition(segment.Offset+segment.Length)); - - textArea.Document.UndoStack.EndUndoGroup(); - textArea.EndUpdate(); - - textArea.DoProcessDialogKey += new DialogKeyProcessor(IgnoreNextDialogKey); + if (editor != null) { + editor.SelectionChanged -= EditorSelectionChanged; + editor.KeyPress -= EditorKeyPress; + editor = null; } - - // detatch our keydown filter to return to the normal processing state - this.textArea.PreviewKeyDown -= new PreviewKeyDownEventHandler(NewEventHandlerPreviewKeyDown); - } - bool IgnoreNextDialogKey(Keys keyData) { - this.textArea.DoProcessDialogKey -= new DialogKeyProcessor(IgnoreNextDialogKey); - return true; // yes, we've processed this key + void EditorSelectionChanged(object sender, EventArgs e) + { + RemoveEventHandlers(); + } + + void EditorKeyPress(object sender, KeyEventArgs e) + { + if (e.Key == Key.Tab || e.Key == Key.Enter || e.Key == Key.Return) { + using (editor.Document.OpenUndoGroup()) { + + // is there a better way to calculate the optimal insertion point? + DomRegion region = resolveResult.CallingMember.BodyRegion; + editor.Caret.Line = region.EndLine; + editor.Caret.Column = region.EndColumn; + + editor.Document.Insert(editor.Caret.Offset, this.newHandlerCode); + + editor.FormattingStrategy.IndentLines(editor, region.EndLine, editor.Caret.Line); + + IDocumentLine line = editor.Document.GetLine(editor.Caret.Line - 1); + int indentationLength = DocumentUtilitites.GetIndentation(editor.Document, line.Offset).Length; + + editor.Select(line.Offset + indentationLength, line.Length - indentationLength); + } + e.Handled = true; + } + // detatch our keydown filter to return to the normal processing state + RemoveEventHandlers(); } } } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs index a59d6d3617..35e2c39c96 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs @@ -33,6 +33,10 @@ namespace ICSharpCode.AvalonEdit.AddIn get { return codeEditor.FileName; } } + public override IFormattingStrategy FormattingStrategy { + get { return codeEditor.FormattingStrategy; } + } + public override void ShowCompletionWindow(ICompletionItemList data) { if (data == null || !data.Items.Any()) diff --git a/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditTextEditorAdapter.cs b/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditTextEditorAdapter.cs index b55c8adbe7..0c22f8713d 100644 --- a/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditTextEditorAdapter.cs +++ b/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditTextEditorAdapter.cs @@ -5,12 +5,14 @@ // $Revision$ // -using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows; +using System.Windows.Input; + using ICSharpCode.AvalonEdit.Editing; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; namespace ICSharpCode.SharpDevelop.Editor { @@ -68,6 +70,10 @@ namespace ICSharpCode.SharpDevelop.Editor public ITextEditorCaret Caret { get; private set; } public ITextEditorOptions Options { get; private set; } + public virtual IFormattingStrategy FormattingStrategy { + get { return DefaultFormattingStrategy.DefaultInstance; } + } + sealed class CaretAdapter : ITextEditorCaret { Caret caret; @@ -159,6 +165,11 @@ namespace ICSharpCode.SharpDevelop.Editor } } + public event KeyEventHandler KeyPress { + add { textEditor.TextArea.PreviewKeyDown += value; } + remove { textEditor.TextArea.PreviewKeyDown -= value; } + } + public event EventHandler SelectionChanged { add { textEditor.TextArea.SelectionChanged += value; } remove { textEditor.TextArea.SelectionChanged -= value; } @@ -167,6 +178,7 @@ namespace ICSharpCode.SharpDevelop.Editor public void Select(int selectionStart, int selectionLength) { textEditor.Select(selectionStart, selectionLength); + textEditor.TextArea.Caret.BringCaretToView(); } public void JumpTo(int line, int column) diff --git a/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs b/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs index dc20b1c332..d934909d71 100644 --- a/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs +++ b/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs @@ -226,7 +226,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion } string entityDoc = entity.Documentation; if (!string.IsNullOrEmpty(entityDoc)) { - string documentation = ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor.CodeCompletionData.ConvertDocumentation(entityDoc); + string documentation = ConvertDocumentation(entityDoc); if (!string.IsNullOrEmpty(documentation)) { description += Environment.NewLine + documentation; } @@ -236,6 +236,11 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion } } } + + public static string ConvertDocumentation(string xmlDocumentation) + { + return ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor.CodeCompletionData.ConvertDocumentation(xmlDocumentation); + } #endregion } } diff --git a/src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionItemList.cs b/src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionItemList.cs index a862947136..6b2cd464fb 100644 --- a/src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionItemList.cs +++ b/src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionItemList.cs @@ -110,8 +110,16 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion /// public virtual void Complete(CompletionContext context, ICompletionItem item) { + if (context == null) + throw new ArgumentNullException("context"); if (item == null) throw new ArgumentNullException("item"); + if (InsertSpace) { + InsertSpace = false; + context.Editor.Document.Insert(context.StartOffset, " "); + context.StartOffset++; + context.EndOffset++; + } item.Complete(context); } } diff --git a/src/Main/Base/Project/Src/Editor/IFormattingStrategy.cs b/src/Main/Base/Project/Src/Editor/IFormattingStrategy.cs index 0d11f448f3..94eb32e8f4 100644 --- a/src/Main/Base/Project/Src/Editor/IFormattingStrategy.cs +++ b/src/Main/Base/Project/Src/Editor/IFormattingStrategy.cs @@ -32,6 +32,8 @@ namespace ICSharpCode.SharpDevelop.Editor public class DefaultFormattingStrategy : IFormattingStrategy { + internal static readonly DefaultFormattingStrategy DefaultInstance = new DefaultFormattingStrategy(); + public virtual void FormatLine(ITextEditor editor, char charTyped) { } diff --git a/src/Main/Base/Project/Src/Editor/ITextEditor.cs b/src/Main/Base/Project/Src/Editor/ITextEditor.cs index 876b8fd97d..aa1ce55b9a 100644 --- a/src/Main/Base/Project/Src/Editor/ITextEditor.cs +++ b/src/Main/Base/Project/Src/Editor/ITextEditor.cs @@ -5,10 +5,12 @@ // $Revision$ // -using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using System; using System.Collections.Generic; +using System.Windows.Input; + using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; namespace ICSharpCode.SharpDevelop.Editor { @@ -28,6 +30,8 @@ namespace ICSharpCode.SharpDevelop.Editor ITextEditorCaret Caret { get; } ITextEditorOptions Options { get; } + IFormattingStrategy FormattingStrategy { get; } + /// /// Gets the start offset of the selection. /// @@ -55,6 +59,11 @@ namespace ICSharpCode.SharpDevelop.Editor /// event EventHandler SelectionChanged; + /// + /// Is raised before a key is pressed. + /// + event KeyEventHandler KeyPress; + /// /// Sets the caret to the specified line/column and brings the caret into view. /// diff --git a/src/Main/Base/Project/Src/TextEditor/Gui/TextEditorAdapter.cs b/src/Main/Base/Project/Src/TextEditor/Gui/TextEditorAdapter.cs index 171a740f4f..a266d4fdd7 100644 --- a/src/Main/Base/Project/Src/TextEditor/Gui/TextEditorAdapter.cs +++ b/src/Main/Base/Project/Src/TextEditor/Gui/TextEditorAdapter.cs @@ -118,6 +118,10 @@ namespace ICSharpCode.SharpDevelop public ITextEditorCaret Caret { get; private set; } public ITextEditorOptions Options { get; private set; } + public virtual Editor.IFormattingStrategy FormattingStrategy { + get { return Editor.DefaultFormattingStrategy.DefaultInstance; } + } + public string FileName { get { return editor.FileName; } } @@ -188,6 +192,11 @@ namespace ICSharpCode.SharpDevelop } } + public event System.Windows.Input.KeyEventHandler KeyPress { + add { throw new NotImplementedException(); } + remove { throw new NotImplementedException(); } + } + public event EventHandler SelectionChanged { add { sdtac.ActiveTextAreaControl.SelectionManager.SelectionChanged += value; } remove { sdtac.ActiveTextAreaControl.SelectionManager.SelectionChanged -= value; }