diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/CompletionBinding.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/CompletionBinding.cs index fbece530dd..11d2c8261c 100644 --- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/CompletionBinding.cs +++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/CompletionBinding.cs @@ -19,7 +19,7 @@ namespace Grunwald.BooBinding.CodeCompletion this.EnableXmlCommentCompletion = false; } - public override bool HandleKeyPress(ITextEditor editor, char ch) + public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { if (ch == '[') { int cursor = editor.Caret.Offset; @@ -28,7 +28,7 @@ namespace Grunwald.BooBinding.CodeCompletion if (c == '\n' || c == '(' || c == ',') { // -> Attribute completion editor.ShowCompletionWindow(new AttributesDataProvider(ExpressionFinder.BooAttributeContext.Instance), ch); - return true; + return CodeCompletionKeyPressResult.Completed; } if (!char.IsWhiteSpace(c)) break; @@ -52,12 +52,13 @@ namespace Grunwald.BooBinding.CodeCompletion return true; case "as": case "isa": - if (IsInComment(editor)) return false; - editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(ExpressionContext.Type), ' '); - return true; - default: - return base.HandleKeyword(editor, word); + if (!IsInComment(editor)) { + editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(ExpressionContext.Type), ' '); + return true; + } + break; } + return base.HandleKeyword(editor, word); } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs index 0df14d7881..cc83f7ad44 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs @@ -33,21 +33,21 @@ namespace CSharpBinding return new CSharpExpressionFinder(ParserService.GetParseInformation(fileName)); } - public override bool HandleKeyPress(ITextEditor editor, char ch) + public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); int cursor = editor.Caret.Offset; ExpressionContext context = null; if (ch == '(') { if (context != null) { - if (IsInComment(editor)) return false; + if (IsInComment(editor)) return CodeCompletionKeyPressResult.None; editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(context), ch); - return true; + return CodeCompletionKeyPressResult.Completed; } else if (EnableMethodInsight && CodeCompletionOptions.InsightEnabled) { editor.ShowInsightWindow(new MethodInsightDataProvider()); - return true; + return CodeCompletionKeyPressResult.Completed; } - return false; + return CodeCompletionKeyPressResult.None; } else if (ch == '[') { var line = editor.Document.GetLineForOffset(cursor); /* TODO: AVALONEDIT Reimplement this @@ -59,7 +59,7 @@ namespace CSharpBinding }*/ } else if (ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) { if (InsightRefreshOnComma(editor, ch)) - return true; + return CodeCompletionKeyPressResult.Completed; } else if(ch == '=') { var curLine = editor.Document.GetLineForOffset(cursor); string documentText = editor.Document.Text; @@ -76,6 +76,7 @@ namespace CSharpBinding EventHandlerCompletitionDataProvider eventHandlerProvider = new EventHandlerCompletitionDataProvider(result.Expression, resolveResult); eventHandlerProvider.InsertSpace = true; editor.ShowCompletionWindow(eventHandlerProvider, ch); + return CodeCompletionKeyPressResult.Completed; } } } @@ -86,21 +87,21 @@ namespace CSharpBinding ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText); if (resolveResult != null && resolveResult.ResolvedType != null) { if (ProvideContextCompletion(editor, resolveResult.ResolvedType, ch)) { - return true; + return CodeCompletionKeyPressResult.Completed; } } } } } else if (ch == '.') { new CSharpCodeCompletionDataProvider().ShowCompletion(editor); - return true; + return CodeCompletionKeyPressResult.Completed; } else if (ch == '>') { - if (IsInComment(editor)) return false; + if (IsInComment(editor)) return CodeCompletionKeyPressResult.None; char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; if (prevChar == '-') { new PointerArrowCompletionDataProvider().ShowCompletion(editor); - return true; + return CodeCompletionKeyPressResult.Completed; } } @@ -111,7 +112,7 @@ namespace CSharpBinding int endOffset = cursor + editor.SelectionLength; // but block code completion when overwriting only part of an identifier if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset))) - return false; + return CodeCompletionKeyPressResult.None; editor.Document.Remove(editor.SelectionStart, editor.SelectionLength); editor.Caret.Offset = cursor; } @@ -129,6 +130,7 @@ namespace CSharpBinding ShowTemplates = true, AllowCompleteExistingExpression = afterUnderscore }, '\0'); + return CodeCompletionKeyPressResult.Completed; } } } @@ -136,7 +138,7 @@ namespace CSharpBinding return base.HandleKeyPress(editor, ch); } - class CSharpCodeCompletionDataProvider : CodeCompletionItemProvider + class CSharpCodeCompletionDataProvider : DotCodeCompletionItemProvider { public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult) { @@ -148,7 +150,7 @@ namespace CSharpBinding } } - class PointerArrowCompletionDataProvider : CodeCompletionItemProvider + class PointerArrowCompletionDataProvider : DotCodeCompletionItemProvider { public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult) { @@ -163,13 +165,8 @@ namespace CSharpBinding public override ExpressionResult GetExpression(ITextEditor editor) { - var document = editor.Document; - IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(editor.FileName); - if (expressionFinder == null) { - return ExpressionResult.Empty; - } else { - return expressionFinder.FindExpression(document.GetText(0, editor.Caret.Offset - 1), editor.Caret.Offset - 1); - } + // - 1 because the "-" is already inserted (the ">" is about to be inserted) + return GetExpressionFromOffset(editor, editor.Caret.Offset - 1); } } @@ -191,10 +188,10 @@ namespace CSharpBinding IClass innerMostClass = parseInfo.MostRecentCompilationUnit.GetInnermostClass(editor.Caret.Line, editor.Caret.Column); if (innerMostClass == null) { editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(ExpressionContext.Namespace), ' '); + return true; } - return true; } - return false; + break; case "as": case "is": if (IsInComment(editor)) return false; @@ -214,12 +211,10 @@ namespace CSharpBinding IMember m = GetCurrentMember(editor); if (m != null) { return ProvideContextCompletion(editor, m.ReturnType, ' '); - } else { - goto default; } - default: - return base.HandleKeyword(editor, word); + break; } + return base.HandleKeyword(editor, word); } bool ShowNewCompletion(ITextEditor editor) diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs index 2cb5953e5e..952eef7ce4 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs @@ -32,14 +32,14 @@ namespace VBNetBinding this.EnableIndexerInsight = false; } - public override bool HandleKeyPress(ITextEditor editor, char ch) + public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { if(ch == '(' && EnableMethodInsight && CodeCompletionOptions.InsightEnabled) { editor.ShowInsightWindow(new MethodInsightDataProvider()); - return true; + return CodeCompletionKeyPressResult.Completed; } else if(ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) { if (InsightRefreshOnComma(editor, ch)) - return true; + return CodeCompletionKeyPressResult.Completed; } else if (ch == '\n') { TryDeclarationTypeInference(editor, editor.Document.GetLineForOffset(editor.Caret.Offset)); } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditTextEditorAdapter.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditTextEditorAdapter.cs index 58c7e1d3f8..1e7b3e4d68 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditTextEditorAdapter.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditTextEditorAdapter.cs @@ -5,14 +5,16 @@ // $Revision$ // -using ICSharpCode.AvalonEdit.CodeCompletion; using System; -using System.Linq; using System.Diagnostics; +using System.Linq; using System.Windows; + +using ICSharpCode.AvalonEdit.CodeCompletion; using ICSharpCode.AvalonEdit.Gui; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Dom.Refactoring; +using System.Windows.Media; namespace ICSharpCode.AvalonEdit.AddIn { @@ -107,11 +109,16 @@ namespace ICSharpCode.AvalonEdit.AddIn { if (data == null || !data.Items.Any()) return; - CompletionWindow window = textEditor.CreateCompletionWindow(data.Items.Select(i => (ICompletionData)new CodeCompletionDataAdapter(i))); + CompletionWindow window = CreateCompletionWindow(data); if (window != null) window.Show(); } + protected virtual CompletionWindow CreateCompletionWindow(ICompletionItemList data) + { + return textEditor.CreateCompletionWindow(data.Items.Select(i => (ICompletionData)new CodeCompletionDataAdapter(i)));; + } + public string GetWordBeforeCaret() { throw new NotImplementedException(); @@ -162,5 +169,9 @@ namespace ICSharpCode.AvalonEdit.AddIn public object Description { get { return item.Description; } } + + public ImageSource Image { + get { return null; } + } } } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index ea3d334b93..8a67677f96 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -5,11 +5,11 @@ // $Revision$ // +using ICSharpCode.AvalonEdit.CodeCompletion; using System; using System.Collections.ObjectModel; using System.Windows.Input; using System.Windows.Media; - using ICSharpCode.Core; using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; @@ -47,14 +47,30 @@ namespace ICSharpCode.AvalonEdit.AddIn base.OnPreviewTextInput(e); if (!e.Handled && e.Text.Length == 1) { foreach (ICodeCompletionBinding cc in CodeCompletionBindings) { - if (cc.HandleKeyPress(textEditorAdapter, e.Text[0])) { - // Don't set e.Handled - we still want to insert the typed text. - // This isn't optimal, but that's how the ICodeCompletionBinding interface - // has always worked; and I don't want to change too much at once. + CompletionWindow oldCompletionWindow = lastCompletionWindow; + CodeCompletionKeyPressResult result = cc.HandleKeyPress(textEditorAdapter, e.Text[0]); + if (result == CodeCompletionKeyPressResult.Completed) { + if (lastCompletionWindow != null && lastCompletionWindow != oldCompletionWindow) { + // a new CompletionWindow was shown, but does not eat the input + // increment the offsets so that they are correct after the text insertion + lastCompletionWindow.StartOffset++; + lastCompletionWindow.EndOffset++; + } + return; + } else if (result == CodeCompletionKeyPressResult.EatKey) { + e.Handled = true; return; } } } } + + CompletionWindow lastCompletionWindow; + + internal void NotifyCompletionWindowOpened(CompletionWindow window) + { + lastCompletionWindow = window; + window.Closed += delegate { lastCompletionWindow = null; }; + } } } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs index e3bf200848..851f592e2c 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs @@ -5,7 +5,9 @@ // $Revision$ // +using ICSharpCode.SharpDevelop; using System; +using ICSharpCode.AvalonEdit.CodeCompletion; namespace ICSharpCode.AvalonEdit.AddIn { @@ -26,5 +28,12 @@ namespace ICSharpCode.AvalonEdit.AddIn public override string FileName { get { return codeEditor.FileName; } } + + protected override CompletionWindow CreateCompletionWindow(ICompletionItemList data) + { + CompletionWindow window = base.CreateCompletionWindow(data); + codeEditor.NotifyCompletionWindowOpened(window); + return window; + } } } diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs index b14cadfad3..57f4fc4a4c 100644 --- a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs +++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs @@ -20,7 +20,7 @@ namespace Hornung.ResourceToolkit.CodeCompletion public abstract class AbstractNRefactoryResourceCodeCompletionBinding : DefaultCodeCompletionBinding { - public override bool HandleKeyPress(ITextEditor editor, char ch) + public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { if (this.CompletionPossible(editor, ch)) { @@ -42,13 +42,13 @@ namespace Hornung.ResourceToolkit.CodeCompletion } editor.ShowCompletionWindow(new ResourceCodeCompletionDataProvider(content, this.OutputVisitor, result.CallingClass != null ? result.CallingClass.Name+"." : null), ch); - return true; + return CodeCompletionKeyPressResult.Completed; } } } - return false; + return CodeCompletionKeyPressResult.None; } // ******************************************************************************************************************************** diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs index ef56c22af7..67ce5d3226 100644 --- a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs +++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs @@ -21,7 +21,7 @@ namespace Hornung.ResourceToolkit.CodeCompletion public class ICSharpCodeCoreResourceCodeCompletionBinding : DefaultCodeCompletionBinding { - public override bool HandleKeyPress(ITextEditor editor, char ch) + public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { if (ch == ':') { @@ -49,7 +49,7 @@ namespace Hornung.ResourceToolkit.CodeCompletion if (content != null) { editor.ShowCompletionWindow(new ResourceCodeCompletionDataProvider(content, null, null), ch); - return true; + return CodeCompletionKeyPressResult.Completed; } } @@ -62,13 +62,13 @@ namespace Hornung.ResourceToolkit.CodeCompletion ICSharpCodeCoreResourceResolver.GetICSharpCodeCoreLocalResourceSet(editor.FileName).ResourceFileContent != null) { editor.ShowCompletionWindow(new ICSharpCodeCoreTagCompletionDataProvider(), ch); - return true; + return CodeCompletionKeyPressResult.Completed; } } - return false; + return CodeCompletionKeyPressResult.None; } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs index b70f7b6e55..d920c808eb 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs @@ -111,6 +111,74 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion } } + /// + /// Selects the item that starts with the specified text. + /// + public void SelectItemWithStart(string startText) + { + if (string.IsNullOrEmpty(startText)) + return; + int selectedItem = listBox.SelectedIndex; + + int bestIndex = -1; + int bestQuality = -1; + // Qualities: 0 = match start + // 1 = match start case sensitive + // 2 = full match + // 3 = full match case sensitive + double bestPriority = 0; + for (int i = 0; i < completionData.Count; ++i) { + string itemText = completionData[i].Text; + if (itemText.StartsWith(startText, StringComparison.InvariantCultureIgnoreCase)) { + double priority = 0; //completionData[i].Priority; + int quality; + if (string.Equals(itemText, startText, StringComparison.InvariantCultureIgnoreCase)) { + if (startText == itemText) + quality = 3; + else + quality = 2; + } else if (itemText.StartsWith(startText, StringComparison.InvariantCulture)) { + quality = 1; + } else { + quality = 0; + } + bool useThisItem; + if (bestQuality < quality) { + useThisItem = true; + } else { + if (bestIndex == selectedItem) { + useThisItem = false; + } else if (i == selectedItem) { + useThisItem = bestQuality == quality; + } else { + useThisItem = bestQuality == quality && bestPriority < priority; + } + } + if (useThisItem) { + bestIndex = i; + bestPriority = priority; + bestQuality = quality; + } + } + } + if (bestIndex < 0) { + ClearSelection(); + } else { + int firstItem = listBox.FirstVisibleItem; + if (bestIndex < firstItem || firstItem + listBox.VisibleItemCount <= bestIndex) { + CenterViewOn(bestIndex); + SelectIndex(bestIndex); + } else { + SelectIndex(bestIndex); + } + } + } + + void ClearSelection() + { + listBox.SelectedIndex = -1; + } + void SelectIndex(int offset) { if (offset >= listBox.Items.Count) @@ -120,5 +188,10 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion listBox.SelectedIndex = offset; listBox.ScrollIntoView(listBox.SelectedItem); } + + void CenterViewOn(int offset) + { + // TODO: implement me + } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml index 4d17e02f22..3eb76e5521 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml @@ -41,7 +41,10 @@ - + + + + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs index 667f8c987b..2966c009ff 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs @@ -32,6 +32,19 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion } } + /// + /// Gets the number of the first visible item. + /// + public int FirstVisibleItem { + get { + if (scrollViewer == null) { + return 0; + } else { + return (int)(this.Items.Count * scrollViewer.VerticalOffset / scrollViewer.ExtentHeight); + } + } + } + /// /// Gets the number of visible items. /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs index 061acf3d2d..a33dea9137 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs @@ -6,6 +6,7 @@ // using System; +using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; @@ -13,6 +14,7 @@ using System.Windows.Input; using System.Windows.Media; using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Gui; namespace ICSharpCode.AvalonEdit.CodeCompletion { @@ -36,21 +38,36 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion this.Width = 200; this.Content = completionList; - completionList.SizeChanged += completionList_SizeChanged; - + startOffset = endOffset = this.TextArea.Caret.Offset; document = textArea.TextView.Document; - startOffset = endOffset = textArea.Caret.Offset; } - void completionList_SizeChanged(object sender, SizeChangedEventArgs e) + /// + /// Gets/Sets the start offset of the edited text portion. + /// + public int StartOffset { + get { return startOffset; } + set { startOffset = value; } + } + + /// + /// Gets/Sets the end offset of the edited text portion. + /// + public int EndOffset { + get { return endOffset; } + set { endOffset = value; } + } + + /// + protected override void OnSourceInitialized(EventArgs e) { - if (completionList.ActualHeight < 200) { - this.SizeToContent = SizeToContent.Height; - this.Height = double.NaN; - } else if (completionList.ActualHeight > 300) { + // prevent CompletionWindow from growing too large + if (this.ActualHeight > 300) { this.SizeToContent = SizeToContent.Manual; this.Height = 300; } + + base.OnSourceInitialized(e); } /// @@ -60,6 +77,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion document.Changing += textArea_Document_Changing; this.TextArea.Caret.PositionChanged += CaretPositionChanged; this.TextArea.MouseWheel += textArea_MouseWheel; + this.TextArea.ActiveInputHandler = new InputHandler(this); } /// @@ -69,8 +87,42 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion this.TextArea.Caret.PositionChanged -= CaretPositionChanged; this.TextArea.MouseWheel -= textArea_MouseWheel; base.DetachEvents(); + this.TextArea.ActiveInputHandler = this.TextArea.DefaultInputHandler; } + #region InputHandler + /// + /// A dummy input handler (that justs invokes the default input handler). + /// This is used to ensure the completion window closes when any other input handler + /// becomes active. + /// + sealed class InputHandler : ITextAreaInputHandler + { + readonly CompletionWindow window; + + public InputHandler(CompletionWindow window) + { + Debug.Assert(window != null); + this.window = window; + } + + public TextArea TextArea { + get { return window.TextArea; } + } + + public void Attach() + { + this.TextArea.DefaultInputHandler.Attach(); + } + + public void Detach() + { + this.TextArea.DefaultInputHandler.Detach(); + window.Close(); + } + } + #endregion + /// protected override void OnKeyDown(KeyEventArgs e) { @@ -104,12 +156,19 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList; } + bool expectInsertionBeforeStart = true; + void textArea_Document_Changing(object sender, DocumentChangeEventArgs e) { // => startOffset test required so that this startOffset/endOffset are not incremented again // for BeforeStartKey characters if (e.Offset >= startOffset && e.Offset <= endOffset) { endOffset += e.InsertionLength - e.RemovalLength; + } else if (expectInsertionBeforeStart && e.Offset == startOffset - 1 && e.InsertionLength == 1 && e.RemovalLength == 0) { + // allow a single one-character insertion in front of StartOffset. + // this is necessary because for dot-completion, the CompletionWindow is shown before + // the dot is actually inserted. + expectInsertionBeforeStart = false; } else { Close(); } @@ -133,7 +192,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion if (offset < startOffset || offset > endOffset) { Close(); } else { - //codeCompletionListView.SelectItemWithStart(document.GetText(startOffset, offset - startOffset)); + completionList.SelectItemWithStart(document.GetText(startOffset, offset - startOffset)); } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICodeCompletionData.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICodeCompletionData.cs index 44b5ed116f..77f9108e7b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICodeCompletionData.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICodeCompletionData.cs @@ -6,6 +6,7 @@ // using System; +using System.Windows.Media; namespace ICSharpCode.AvalonEdit.CodeCompletion { @@ -14,6 +15,11 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion /// public interface ICompletionData { + /// + /// Gets the image. + /// + ImageSource Image { get; } + /// /// Gets the text. This property is used to filter the list of visible elements. /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs index 1e4861d01a..f12e380a8b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs @@ -6,11 +6,13 @@ // using System; +using System.Collections.Generic; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media.TextFormatting; using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Gui { @@ -35,8 +37,19 @@ namespace ICSharpCode.AvalonEdit.Gui static class CaretNavigationCommandHandler { - public static readonly CommandBindingCollection CommandBindings = new CommandBindingCollection(); - public static readonly InputBindingCollection InputBindings = new InputBindingCollection(); + /// + /// Creates a new for the text area. + /// + public static TextAreaInputHandler Create(TextArea textArea) + { + TextAreaInputHandler handler = new TextAreaInputHandler(textArea); + handler.CommandBindings.AddRange(CommandBindings); + handler.InputBindings.AddRange(InputBindings); + return handler; + } + + static readonly List CommandBindings = new List(); + static readonly List InputBindings = new List(); static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler) { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs index 9e588b8194..cdafabbc3b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs @@ -6,24 +6,37 @@ // using System; +using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Text; using System.Windows; using System.Windows.Documents; using System.Windows.Input; using ICSharpCode.AvalonEdit.Document; -using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Gui { + /// + /// We re-use the CommandBinding and InputBinding instances between multiple text areas, + /// so this class is static. + /// static class EditingCommandHandler { - public static readonly CommandBindingCollection CommandBindings = new CommandBindingCollection(); - public static readonly InputBindingCollection InputBindings = new InputBindingCollection(); + /// + /// Creates a new for the text area. + /// + public static TextAreaInputHandler Create(TextArea textArea) + { + TextAreaInputHandler handler = new TextAreaInputHandler(textArea); + handler.CommandBindings.AddRange(CommandBindings); + handler.InputBindings.AddRange(InputBindings); + return handler; + } + + static readonly List CommandBindings = new List(); + static readonly List InputBindings = new List(); static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler) { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs index 9b7484be3f..10c1b32e46 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs @@ -21,7 +21,7 @@ namespace ICSharpCode.AvalonEdit.Gui /// /// Handles selection of text using the mouse. /// - sealed class SelectionMouseHandler + sealed class SelectionMouseHandler : ITextAreaInputHandler { #region enum SelectionMode enum SelectionMode @@ -62,9 +62,15 @@ namespace ICSharpCode.AvalonEdit.Gui #region Constructor + Attach + Detach public SelectionMouseHandler(TextArea textArea) { + if (textArea == null) + throw new ArgumentNullException("textArea"); this.textArea = textArea; } + public TextArea TextArea { + get { return textArea; } + } + public void Attach() { textArea.MouseLeftButtonDown += textArea_MouseLeftButtonDown; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs index fc3a0a6b3d..d4f34e40db 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs @@ -77,19 +77,46 @@ namespace ICSharpCode.AvalonEdit caret = new Caret(this); caret.PositionChanged += (sender, e) => RequestSelectionValidation(); - this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, ExecuteUndo, CanExecuteUndo)); - this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, ExecuteRedo, CanExecuteRedo)); - this.CommandBindings.AddRange(CaretNavigationCommandHandler.CommandBindings); - this.InputBindings.AddRange(CaretNavigationCommandHandler.InputBindings); - - this.CommandBindings.AddRange(EditingCommandHandler.CommandBindings); - this.InputBindings.AddRange(EditingCommandHandler.InputBindings); - - new SelectionMouseHandler(this).Attach(); + this.DefaultInputHandler = new TextAreaDefaultInputHandler(this); + this.ActiveInputHandler = this.DefaultInputHandler; } #endregion + #region InputHandler management + /// + /// Gets the default input handler. + /// + public TextAreaDefaultInputHandler DefaultInputHandler { get; private set; } + + ITextAreaInputHandler activeInputHandler; + + /// + /// Gets/Sets the active input handler. + /// + public ITextAreaInputHandler ActiveInputHandler { + get { return activeInputHandler; } + set { + if (value != null && value.TextArea != this) + throw new ArgumentException("The input handler was created for a different text area than this one."); + if (activeInputHandler != value) { + if (activeInputHandler != null) + activeInputHandler.Detach(); + activeInputHandler = value; + if (value != null) + value.Attach(); + if (ActiveInputHandlerChanged != null) + ActiveInputHandlerChanged(this, EventArgs.Empty); + } + } + } + + /// + /// Occurs when the ActiveInputHandler property changes. + /// + public event EventHandler ActiveInputHandlerChanged; + #endregion + #region Document property /// /// Document property. @@ -330,43 +357,6 @@ namespace ICSharpCode.AvalonEdit } #endregion - #region Undo / Redo - UndoStack GetUndoStack() - { - TextDocument document = this.Document; - if (document != null) - return document.UndoStack; - else - return null; - } - - void ExecuteUndo(object sender, ExecutedRoutedEventArgs e) - { - var undoStack = GetUndoStack(); - if (undoStack != null && undoStack.CanUndo) - undoStack.Undo(); - } - - void CanExecuteUndo(object sender, CanExecuteRoutedEventArgs e) - { - var undoStack = GetUndoStack(); - e.CanExecute = undoStack != null && undoStack.CanUndo; - } - - void ExecuteRedo(object sender, ExecutedRoutedEventArgs e) - { - var undoStack = GetUndoStack(); - if (undoStack != null && undoStack.CanRedo) - undoStack.Redo(); - } - - void CanExecuteRedo(object sender, CanExecuteRoutedEventArgs e) - { - var undoStack = GetUndoStack(); - e.CanExecute = undoStack != null && undoStack.CanRedo; - } - #endregion - #region IScrollInfo implementation ScrollViewer scrollOwner; bool canVerticallyScroll, canHorizontallyScroll; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaDefaultInputHandlers.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaDefaultInputHandlers.cs new file mode 100644 index 0000000000..17c7252df0 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaDefaultInputHandlers.cs @@ -0,0 +1,84 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows.Input; +using ICSharpCode.AvalonEdit.Document; + +namespace ICSharpCode.AvalonEdit.Gui +{ + /// + /// Contains the predefined input handlers. + /// + public class TextAreaDefaultInputHandler : TextAreaInputHandler + { + /// + /// Gets the caret navigation input handler. + /// + public TextAreaInputHandler CaretNavigation { get; private set; } + + /// + /// Gets the editing input handler. + /// + public TextAreaInputHandler Editing { get; private set; } + + /// + /// Gets the mouse selection input handler. + /// + public ITextAreaInputHandler MouseSelection { get; private set; } + + /// + /// Creates a new TextAreaDefaultInputHandler instance. + /// + public TextAreaDefaultInputHandler(TextArea textArea) : base(textArea) + { + this.NestedInputHandlers.Add(CaretNavigation = CaretNavigationCommandHandler.Create(textArea)); + this.NestedInputHandlers.Add(Editing = EditingCommandHandler.Create(textArea)); + this.NestedInputHandlers.Add(MouseSelection = new SelectionMouseHandler(textArea)); + + this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, ExecuteUndo, CanExecuteUndo)); + this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, ExecuteRedo, CanExecuteRedo)); + } + + #region Undo / Redo + UndoStack GetUndoStack() + { + TextDocument document = this.TextArea.Document; + if (document != null) + return document.UndoStack; + else + return null; + } + + void ExecuteUndo(object sender, ExecutedRoutedEventArgs e) + { + var undoStack = GetUndoStack(); + if (undoStack != null && undoStack.CanUndo) + undoStack.Undo(); + } + + void CanExecuteUndo(object sender, CanExecuteRoutedEventArgs e) + { + var undoStack = GetUndoStack(); + e.CanExecute = undoStack != null && undoStack.CanUndo; + } + + void ExecuteRedo(object sender, ExecutedRoutedEventArgs e) + { + var undoStack = GetUndoStack(); + if (undoStack != null && undoStack.CanRedo) + undoStack.Redo(); + } + + void CanExecuteRedo(object sender, CanExecuteRoutedEventArgs e) + { + var undoStack = GetUndoStack(); + e.CanExecute = undoStack != null && undoStack.CanRedo; + } + #endregion + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaInputHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaInputHandler.cs new file mode 100644 index 0000000000..481290fe90 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaInputHandler.cs @@ -0,0 +1,184 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.AvalonEdit.Utils; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Windows.Input; + +namespace ICSharpCode.AvalonEdit.Gui +{ + /// + /// A set of input bindings and event handlers for the text area. + /// + public interface ITextAreaInputHandler + { + /// + /// Gets the text area that the input handler belongs to. + /// + TextArea TextArea { + get; + } + + /// + /// Attaches an input handler to the text area. + /// + void Attach(); + + /// + /// Detaches the input handler from the text area. + /// + void Detach(); + } + + /// + /// Default-implementation of . + /// + public class TextAreaInputHandler : ITextAreaInputHandler + { + readonly ObserveAddRemoveCollection commandBindings; + readonly ObserveAddRemoveCollection inputBindings; + readonly ObserveAddRemoveCollection nestedInputHandlers; + readonly TextArea textArea; + bool isAttached; + + /// + /// Creates a new TextAreaInputHandler. + /// + public TextAreaInputHandler(TextArea textArea) + { + if (textArea == null) + throw new ArgumentNullException("textArea"); + this.textArea = textArea; + commandBindings = new ObserveAddRemoveCollection(CommandBinding_Added, CommandBinding_Removed); + inputBindings = new ObserveAddRemoveCollection(InputBinding_Added, InputBinding_Removed); + nestedInputHandlers = new ObserveAddRemoveCollection(NestedInputHandler_Added, NestedInputHandler_Removed); + } + + /// + public TextArea TextArea { + get { return textArea; } + } + + /// + /// Gets whether the input handler is currently attached to the text area. + /// + public bool IsAttached { + get { return isAttached; } + } + + #region CommandBindings / InputBindings + /// + /// Gets the command bindings of this input handler. + /// + public ICollection CommandBindings { + get { return commandBindings; } + } + + void CommandBinding_Added(CommandBinding commandBinding) + { + if (isAttached) + textArea.CommandBindings.Add(commandBinding); + } + + void CommandBinding_Removed(CommandBinding commandBinding) + { + if (isAttached) + textArea.CommandBindings.Remove(commandBinding); + } + + /// + /// Gets the input bindings of this input handler. + /// + public ICollection InputBindings { + get { return inputBindings; } + } + + void InputBinding_Added(InputBinding inputBinding) + { + if (isAttached) + textArea.InputBindings.Add(inputBinding); + } + + void InputBinding_Removed(InputBinding inputBinding) + { + if (isAttached) + textArea.InputBindings.Remove(inputBinding); + } + + /// + /// Adds a command and input binding. + /// + /// The command ID. + /// The modifiers of the keyboard shortcut. + /// The key of the keyboard shortcut. + /// The event handler to run when the command is executed. + protected void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler) + { + this.CommandBindings.Add(new CommandBinding(command, handler)); + this.InputBindings.Add(new KeyBinding(command, key, modifiers)); + } + #endregion + + #region NestedInputHandlers + /// + /// Gets the collection of nested input handlers. NestedInputHandlers are activated and deactivated + /// together with this input handler. + /// + public ICollection NestedInputHandlers { + get { return nestedInputHandlers; } + } + + void NestedInputHandler_Added(ITextAreaInputHandler handler) + { + if (handler == null) + throw new ArgumentNullException("handler"); + if (handler.TextArea != textArea) + throw new ArgumentException("The nested handler must be working for the same text area!"); + if (isAttached) + handler.Attach(); + } + + void NestedInputHandler_Removed(ITextAreaInputHandler handler) + { + if (isAttached) + handler.Detach(); + } + #endregion + + #region Attach/Detach + /// + public virtual void Attach() + { + if (isAttached) + throw new InvalidOperationException("Input handler is already attached"); + isAttached = true; + + textArea.CommandBindings.AddRange(commandBindings); + textArea.InputBindings.AddRange(inputBindings); + foreach (ITextAreaInputHandler handler in nestedInputHandlers) + handler.Attach(); + } + + /// + public virtual void Detach() + { + if (!isAttached) + throw new InvalidOperationException("Input handler is not attached"); + isAttached = false; + + foreach (CommandBinding b in commandBindings) + textArea.CommandBindings.Remove(b); + foreach (InputBinding b in inputBindings) + textArea.InputBindings.Remove(b); + foreach (ITextAreaInputHandler handler in nestedInputHandlers) + handler.Detach(); + } + #endregion + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs index 5f0cdebf6f..3997b895ce 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs @@ -50,9 +50,9 @@ namespace ICSharpCode.AvalonEdit.Gui public TextView() { textLayer = new TextLayer(this); - elementGenerators.CollectionChanged += elementGenerators_CollectionChanged; - lineTransformers.CollectionChanged += lineTransformers_CollectionChanged; - backgroundRenderer.CollectionChanged += backgroundRenderer_CollectionChanged; + elementGenerators = new ObserveAddRemoveCollection(ElementGenerator_Added, ElementGenerator_Removed); + lineTransformers = new ObserveAddRemoveCollection(LineTransformer_Added, LineTransformer_Removed); + backgroundRenderers = new ObserveAddRemoveCollection(BackgroundRenderer_Added, BackgroundRenderer_Removed); layers = new UIElementCollection(this, this); InsertLayer(textLayer, KnownLayer.Text, LayerInsertionPosition.Replace); } @@ -128,33 +128,45 @@ namespace ICSharpCode.AvalonEdit.Gui #endregion #region ElementGenerators+LineTransformers Properties - readonly ObservableCollection elementGenerators = new ObservableCollection(); + readonly ObserveAddRemoveCollection elementGenerators; /// /// Gets a collection where element generators can be registered. /// - public ObservableCollection ElementGenerators { + public IList ElementGenerators { get { return elementGenerators; } } - void elementGenerators_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + void ElementGenerator_Added(VisualLineElementGenerator generator) { - HandleTextViewConnect(e); + ConnectToTextView(generator); Redraw(); } - readonly ObservableCollection lineTransformers = new ObservableCollection(); + void ElementGenerator_Removed(VisualLineElementGenerator generator) + { + DisconnectFromTextView(generator); + Redraw(); + } + + readonly ObserveAddRemoveCollection lineTransformers; /// /// Gets a collection where line transformers can be registered. /// - public ObservableCollection LineTransformers { + public IList LineTransformers { get { return lineTransformers; } } - void lineTransformers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + void LineTransformer_Added(IVisualLineTransformer lineTransformer) + { + ConnectToTextView(lineTransformer); + Redraw(); + } + + void LineTransformer_Removed(IVisualLineTransformer lineTransformer) { - HandleTextViewConnect(e); + DisconnectFromTextView(lineTransformer); Redraw(); } #endregion @@ -700,18 +712,29 @@ namespace ICSharpCode.AvalonEdit.Gui #endregion #region Render - readonly ObservableCollection backgroundRenderer = new ObservableCollection(); + readonly ObserveAddRemoveCollection backgroundRenderers; /// /// Gets the list of background renderers. /// - public ObservableCollection BackgroundRenderer { - get { return backgroundRenderer; } + public IList BackgroundRenderers { + get { return backgroundRenderers; } + } + + void BackgroundRenderer_Added(IBackgroundRenderer renderer) + { + ConnectToTextView(renderer); + InvalidateVisualAndLayerVisuals(); } - void backgroundRenderer_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + void BackgroundRenderer_Removed(IBackgroundRenderer renderer) + { + DisconnectFromTextView(renderer); + InvalidateVisualAndLayerVisuals(); + } + + void InvalidateVisualAndLayerVisuals() { - HandleTextViewConnect(e); InvalidateVisual(); foreach (UIElement layer in this.Layers) { // invalidate known layers @@ -728,7 +751,7 @@ namespace ICSharpCode.AvalonEdit.Gui internal void RenderBackground(DrawingContext drawingContext, KnownLayer layer) { - foreach (IBackgroundRenderer bg in backgroundRenderer) { + foreach (IBackgroundRenderer bg in backgroundRenderers) { if (bg.Layer == layer) { bg.Draw(drawingContext); } @@ -1155,27 +1178,18 @@ namespace ICSharpCode.AvalonEdit.Gui return services.GetService(serviceType); } - void HandleTextViewConnect(NotifyCollectionChangedEventArgs e) + void ConnectToTextView(object obj) { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - case NotifyCollectionChangedAction.Remove: - case NotifyCollectionChangedAction.Replace: - if (e.OldItems != null) { - foreach (ITextViewConnect c in e.OldItems.OfType()) - c.RemoveFromTextView(this); - } - if (e.NewItems != null) { - foreach (ITextViewConnect c in e.NewItems.OfType()) - c.AddToTextView(this); - } - break; - case NotifyCollectionChangedAction.Move: - // ignore Move - break; - default: - throw new NotSupportedException(e.Action.ToString()); - } + ITextViewConnect c = obj as ITextViewConnect; + if (c != null) + c.AddToTextView(this); + } + + void DisconnectFromTextView(object obj) + { + ITextViewConnect c = obj as ITextViewConnect; + if (c != null) + c.RemoveFromTextView(this); } #endregion diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index 74a7d73248..4b1abe7251 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -134,6 +134,8 @@ FoldingManager.cs + + TextView.cs @@ -278,6 +280,7 @@ + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ObserveAddRemoveCollection.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ObserveAddRemoveCollection.cs new file mode 100644 index 0000000000..2f13a4f972 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ObserveAddRemoveCollection.cs @@ -0,0 +1,64 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.ObjectModel; + +namespace ICSharpCode.AvalonEdit.Utils +{ + /// + /// A collection where adding and removing items causes a callback. + /// + sealed class ObserveAddRemoveCollection : Collection + { + readonly Action onAdd, onRemove; + + public ObserveAddRemoveCollection(Action onAdd, Action onRemove) + { + this.onAdd = onAdd; + this.onRemove = onRemove; + } + + protected override void ClearItems() + { + if (onRemove != null) { + foreach (T val in this) + onRemove(val); + } + base.ClearItems(); + } + + protected override void InsertItem(int index, T item) + { + if (onAdd != null) + onAdd(item); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + if (onRemove != null) + onRemove(this[index]); + base.RemoveItem(index); + } + + protected override void SetItem(int index, T item) + { + if (onRemove != null) + onRemove(this[index]); + try { + if (onAdd != null) + onAdd(item); + } catch { + // when adding the new item fails, just remove the old one + base.RemoveAt(index); + throw; + } + base.SetItem(index, item); + } + } +} diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 71a8f55cbe..b01753ae4f 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -750,10 +750,6 @@ - - {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} - ICSharpCode.AvalonEdit - {2D18BE89-D210-49EB-A9DD-2246FBB3DF6D} ICSharpCode.TextEditor diff --git a/src/Main/Base/Project/Src/TextEditor/CodeCompletionItemProvider.cs b/src/Main/Base/Project/Src/TextEditor/CodeCompletionItemProvider.cs index 4948c977d2..36fce41423 100644 --- a/src/Main/Base/Project/Src/TextEditor/CodeCompletionItemProvider.cs +++ b/src/Main/Base/Project/Src/TextEditor/CodeCompletionItemProvider.cs @@ -37,6 +37,11 @@ namespace ICSharpCode.SharpDevelop } public virtual ExpressionResult GetExpression(ITextEditor editor) + { + return GetExpressionFromOffset(editor, editor.Caret.Offset); + } + + protected ExpressionResult GetExpressionFromOffset(ITextEditor editor, int offset) { if (editor == null) throw new ArgumentNullException("editor"); @@ -45,7 +50,7 @@ namespace ICSharpCode.SharpDevelop if (expressionFinder == null) { return ExpressionResult.Empty; } else { - return expressionFinder.FindExpression(document.GetText(0, editor.Caret.Offset), editor.Caret.Offset); + return expressionFinder.FindExpression(document.GetText(0, offset), offset); } } @@ -92,6 +97,11 @@ namespace ICSharpCode.SharpDevelop } } + public class DotCodeCompletionItemProvider : CodeCompletionItemProvider + { + + } + public class CodeCompletionItem : ICompletionItem { readonly IEntity entity; diff --git a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/CodeCompletionBinding.cs b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/CodeCompletionBinding.cs index 4693a287bf..63d86fe116 100644 --- a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/CodeCompletionBinding.cs +++ b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/CodeCompletionBinding.cs @@ -18,10 +18,29 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor /// public interface ICodeCompletionBinding { - bool HandleKeyPress(ITextEditor editor, char ch); + CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch); bool CtrlSpace(ITextEditor editor); } + /// + /// The result of . + /// + public enum CodeCompletionKeyPressResult + { + /// + /// The binding did not run code completion. The pressed key will be handled normally. + /// + None, + /// + /// The binding handled code completion, the pressed key will be handled normally. + /// + Completed, + /// + /// The binding handled code completion, and the key will not be handled by the text editor. + /// + EatKey + } + /// /// Creates code completion bindings that manage code completion for one language. /// @@ -68,7 +87,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor this.extensions = extensions; } - public bool HandleKeyPress(ITextEditor editor, char ch) + public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { string ext = Path.GetExtension(editor.FileName); foreach (string extension in extensions) { @@ -79,7 +98,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor return binding.HandleKeyPress(editor, ch); } } - return false; + return CodeCompletionKeyPressResult.None; } public bool CtrlSpace(ITextEditor editor) @@ -140,48 +159,43 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor } } - public virtual bool HandleKeyPress(ITextEditor editor, char ch) + public virtual CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { switch (ch) { case '(': if (enableMethodInsight && CodeCompletionOptions.InsightEnabled) { editor.ShowInsightWindow(new MethodInsightDataProvider()); - return true; - } else { - return false; + return CodeCompletionKeyPressResult.Completed; } + break; case '[': if (enableIndexerInsight && CodeCompletionOptions.InsightEnabled) { editor.ShowInsightWindow(new IndexerInsightDataProvider()); - return true; - } else { - return false; + return CodeCompletionKeyPressResult.Completed; } + break; case '<': if (enableXmlCommentCompletion) { editor.ShowCompletionWindow(new CommentCompletionDataProvider(), ch); - return true; - } else { - return false; + return CodeCompletionKeyPressResult.Completed; } + break; case '.': if (enableDotCompletion) { - new CodeCompletionItemProvider().ShowCompletion(editor); - return true; - } else { - return false; + new DotCodeCompletionItemProvider().ShowCompletion(editor); + return CodeCompletionKeyPressResult.Completed; } + break; case ' ': - if (!CodeCompletionOptions.KeywordCompletionEnabled) - return false; - string word = editor.GetWordBeforeCaret(); - if (word != null) - return HandleKeyword(editor, word); - else - return false; - default: - return false; + if (CodeCompletionOptions.KeywordCompletionEnabled) { + string word = editor.GetWordBeforeCaret(); + if (word != null) { + return HandleKeyword(editor, word) ? CodeCompletionKeyPressResult.Completed : CodeCompletionKeyPressResult.None; + } + } + break; } + return CodeCompletionKeyPressResult.None; } public virtual bool HandleKeyword(ITextEditor editor, string word) diff --git a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs index 4171584f04..1d6eb6420a 100644 --- a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs +++ b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs @@ -328,8 +328,11 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor if (CodeCompletionOptions.EnableCodeCompletion) { foreach (ICodeCompletionBinding ccBinding in CodeCompletionBindings) { - if (ccBinding.HandleKeyPress(adapter, ch)) + CodeCompletionKeyPressResult result = ccBinding.HandleKeyPress(adapter, ch); + if (result == CodeCompletionKeyPressResult.Completed) return false; + else if (result == CodeCompletionKeyPressResult.EatKey) + return true; } } } catch (Exception ex) { diff --git a/src/Main/Base/Project/Src/TextEditor/ITextEditor.cs b/src/Main/Base/Project/Src/TextEditor/ITextEditor.cs index f80aa75ab6..28813af114 100644 --- a/src/Main/Base/Project/Src/TextEditor/ITextEditor.cs +++ b/src/Main/Base/Project/Src/TextEditor/ITextEditor.cs @@ -38,6 +38,7 @@ namespace ICSharpCode.SharpDevelop void Select(int selectionStart, int selectionLength); string FileName { get; } + void ShowInsightWindow(ICSharpCode.TextEditor.Gui.InsightWindow.IInsightDataProvider provider); [Obsolete] void ShowCompletionWindow(ICSharpCode.TextEditor.Gui.CompletionWindow.ICompletionDataProvider provider, char ch);