Browse Source

Improvements to AvalonEdit CompletionWindow. Implemented TextAreaInputHandler.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3889 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
5380d99724
  1. 15
      src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/CompletionBinding.cs
  2. 47
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs
  3. 6
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs
  4. 17
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditTextEditorAdapter.cs
  5. 26
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  6. 9
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs
  7. 6
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs
  8. 8
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs
  9. 73
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs
  10. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml
  11. 13
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs
  12. 77
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs
  13. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICodeCompletionData.cs
  14. 17
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs
  15. 23
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs
  16. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs
  17. 82
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs
  18. 84
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaDefaultInputHandlers.cs
  19. 184
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaInputHandler.cs
  20. 88
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs
  21. 3
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  22. 64
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ObserveAddRemoveCollection.cs
  23. 4
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  24. 12
      src/Main/Base/Project/Src/TextEditor/CodeCompletionItemProvider.cs
  25. 66
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/CodeCompletionBinding.cs
  26. 5
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs
  27. 1
      src/Main/Base/Project/Src/TextEditor/ITextEditor.cs

15
src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/CompletionBinding.cs

@ -19,7 +19,7 @@ namespace Grunwald.BooBinding.CodeCompletion
this.EnableXmlCommentCompletion = false; this.EnableXmlCommentCompletion = false;
} }
public override bool HandleKeyPress(ITextEditor editor, char ch) public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
{ {
if (ch == '[') { if (ch == '[') {
int cursor = editor.Caret.Offset; int cursor = editor.Caret.Offset;
@ -28,7 +28,7 @@ namespace Grunwald.BooBinding.CodeCompletion
if (c == '\n' || c == '(' || c == ',') { if (c == '\n' || c == '(' || c == ',') {
// -> Attribute completion // -> Attribute completion
editor.ShowCompletionWindow(new AttributesDataProvider(ExpressionFinder.BooAttributeContext.Instance), ch); editor.ShowCompletionWindow(new AttributesDataProvider(ExpressionFinder.BooAttributeContext.Instance), ch);
return true; return CodeCompletionKeyPressResult.Completed;
} }
if (!char.IsWhiteSpace(c)) if (!char.IsWhiteSpace(c))
break; break;
@ -52,12 +52,13 @@ namespace Grunwald.BooBinding.CodeCompletion
return true; return true;
case "as": case "as":
case "isa": case "isa":
if (IsInComment(editor)) return false; if (!IsInComment(editor)) {
editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(ExpressionContext.Type), ' '); editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(ExpressionContext.Type), ' ');
return true; return true;
default: }
return base.HandleKeyword(editor, word); break;
} }
return base.HandleKeyword(editor, word);
} }
} }
} }

47
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs

@ -33,21 +33,21 @@ namespace CSharpBinding
return new CSharpExpressionFinder(ParserService.GetParseInformation(fileName)); 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); CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName);
int cursor = editor.Caret.Offset; int cursor = editor.Caret.Offset;
ExpressionContext context = null; ExpressionContext context = null;
if (ch == '(') { if (ch == '(') {
if (context != null) { if (context != null) {
if (IsInComment(editor)) return false; if (IsInComment(editor)) return CodeCompletionKeyPressResult.None;
editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(context), ch); editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(context), ch);
return true; return CodeCompletionKeyPressResult.Completed;
} else if (EnableMethodInsight && CodeCompletionOptions.InsightEnabled) { } else if (EnableMethodInsight && CodeCompletionOptions.InsightEnabled) {
editor.ShowInsightWindow(new MethodInsightDataProvider()); editor.ShowInsightWindow(new MethodInsightDataProvider());
return true; return CodeCompletionKeyPressResult.Completed;
} }
return false; return CodeCompletionKeyPressResult.None;
} else if (ch == '[') { } else if (ch == '[') {
var line = editor.Document.GetLineForOffset(cursor); var line = editor.Document.GetLineForOffset(cursor);
/* TODO: AVALONEDIT Reimplement this /* TODO: AVALONEDIT Reimplement this
@ -59,7 +59,7 @@ namespace CSharpBinding
}*/ }*/
} else if (ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) { } else if (ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) {
if (InsightRefreshOnComma(editor, ch)) if (InsightRefreshOnComma(editor, ch))
return true; return CodeCompletionKeyPressResult.Completed;
} else if(ch == '=') { } else if(ch == '=') {
var curLine = editor.Document.GetLineForOffset(cursor); var curLine = editor.Document.GetLineForOffset(cursor);
string documentText = editor.Document.Text; string documentText = editor.Document.Text;
@ -76,6 +76,7 @@ namespace CSharpBinding
EventHandlerCompletitionDataProvider eventHandlerProvider = new EventHandlerCompletitionDataProvider(result.Expression, resolveResult); EventHandlerCompletitionDataProvider eventHandlerProvider = new EventHandlerCompletitionDataProvider(result.Expression, resolveResult);
eventHandlerProvider.InsertSpace = true; eventHandlerProvider.InsertSpace = true;
editor.ShowCompletionWindow(eventHandlerProvider, ch); 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); ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText);
if (resolveResult != null && resolveResult.ResolvedType != null) { if (resolveResult != null && resolveResult.ResolvedType != null) {
if (ProvideContextCompletion(editor, resolveResult.ResolvedType, ch)) { if (ProvideContextCompletion(editor, resolveResult.ResolvedType, ch)) {
return true; return CodeCompletionKeyPressResult.Completed;
} }
} }
} }
} }
} else if (ch == '.') { } else if (ch == '.') {
new CSharpCodeCompletionDataProvider().ShowCompletion(editor); new CSharpCodeCompletionDataProvider().ShowCompletion(editor);
return true; return CodeCompletionKeyPressResult.Completed;
} else if (ch == '>') { } else if (ch == '>') {
if (IsInComment(editor)) return false; if (IsInComment(editor)) return CodeCompletionKeyPressResult.None;
char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' ';
if (prevChar == '-') { if (prevChar == '-') {
new PointerArrowCompletionDataProvider().ShowCompletion(editor); new PointerArrowCompletionDataProvider().ShowCompletion(editor);
return true; return CodeCompletionKeyPressResult.Completed;
} }
} }
@ -111,7 +112,7 @@ namespace CSharpBinding
int endOffset = cursor + editor.SelectionLength; int endOffset = cursor + editor.SelectionLength;
// but block code completion when overwriting only part of an identifier // but block code completion when overwriting only part of an identifier
if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset))) if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset)))
return false; return CodeCompletionKeyPressResult.None;
editor.Document.Remove(editor.SelectionStart, editor.SelectionLength); editor.Document.Remove(editor.SelectionStart, editor.SelectionLength);
editor.Caret.Offset = cursor; editor.Caret.Offset = cursor;
} }
@ -129,6 +130,7 @@ namespace CSharpBinding
ShowTemplates = true, ShowTemplates = true,
AllowCompleteExistingExpression = afterUnderscore AllowCompleteExistingExpression = afterUnderscore
}, '\0'); }, '\0');
return CodeCompletionKeyPressResult.Completed;
} }
} }
} }
@ -136,7 +138,7 @@ namespace CSharpBinding
return base.HandleKeyPress(editor, ch); return base.HandleKeyPress(editor, ch);
} }
class CSharpCodeCompletionDataProvider : CodeCompletionItemProvider class CSharpCodeCompletionDataProvider : DotCodeCompletionItemProvider
{ {
public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult) 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) public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult)
{ {
@ -163,13 +165,8 @@ namespace CSharpBinding
public override ExpressionResult GetExpression(ITextEditor editor) public override ExpressionResult GetExpression(ITextEditor editor)
{ {
var document = editor.Document; // - 1 because the "-" is already inserted (the ">" is about to be inserted)
IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(editor.FileName); return GetExpressionFromOffset(editor, editor.Caret.Offset - 1);
if (expressionFinder == null) {
return ExpressionResult.Empty;
} else {
return expressionFinder.FindExpression(document.GetText(0, editor.Caret.Offset - 1), editor.Caret.Offset - 1);
}
} }
} }
@ -191,10 +188,10 @@ namespace CSharpBinding
IClass innerMostClass = parseInfo.MostRecentCompilationUnit.GetInnermostClass(editor.Caret.Line, editor.Caret.Column); IClass innerMostClass = parseInfo.MostRecentCompilationUnit.GetInnermostClass(editor.Caret.Line, editor.Caret.Column);
if (innerMostClass == null) { if (innerMostClass == null) {
editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(ExpressionContext.Namespace), ' '); editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(ExpressionContext.Namespace), ' ');
return true;
} }
return true;
} }
return false; break;
case "as": case "as":
case "is": case "is":
if (IsInComment(editor)) return false; if (IsInComment(editor)) return false;
@ -214,12 +211,10 @@ namespace CSharpBinding
IMember m = GetCurrentMember(editor); IMember m = GetCurrentMember(editor);
if (m != null) { if (m != null) {
return ProvideContextCompletion(editor, m.ReturnType, ' '); return ProvideContextCompletion(editor, m.ReturnType, ' ');
} else {
goto default;
} }
default: break;
return base.HandleKeyword(editor, word);
} }
return base.HandleKeyword(editor, word);
} }
bool ShowNewCompletion(ITextEditor editor) bool ShowNewCompletion(ITextEditor editor)

6
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs

@ -32,14 +32,14 @@ namespace VBNetBinding
this.EnableIndexerInsight = false; this.EnableIndexerInsight = false;
} }
public override bool HandleKeyPress(ITextEditor editor, char ch) public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
{ {
if(ch == '(' && EnableMethodInsight && CodeCompletionOptions.InsightEnabled) { if(ch == '(' && EnableMethodInsight && CodeCompletionOptions.InsightEnabled) {
editor.ShowInsightWindow(new MethodInsightDataProvider()); editor.ShowInsightWindow(new MethodInsightDataProvider());
return true; return CodeCompletionKeyPressResult.Completed;
} else if(ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) { } else if(ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) {
if (InsightRefreshOnComma(editor, ch)) if (InsightRefreshOnComma(editor, ch))
return true; return CodeCompletionKeyPressResult.Completed;
} else if (ch == '\n') { } else if (ch == '\n') {
TryDeclarationTypeInference(editor, editor.Document.GetLineForOffset(editor.Caret.Offset)); TryDeclarationTypeInference(editor, editor.Document.GetLineForOffset(editor.Caret.Offset));
} }

17
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditTextEditorAdapter.cs

@ -5,14 +5,16 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.AvalonEdit.CodeCompletion;
using System; using System;
using System.Linq;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Windows; using System.Windows;
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Gui; using ICSharpCode.AvalonEdit.Gui;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom.Refactoring; using ICSharpCode.SharpDevelop.Dom.Refactoring;
using System.Windows.Media;
namespace ICSharpCode.AvalonEdit.AddIn namespace ICSharpCode.AvalonEdit.AddIn
{ {
@ -107,11 +109,16 @@ namespace ICSharpCode.AvalonEdit.AddIn
{ {
if (data == null || !data.Items.Any()) if (data == null || !data.Items.Any())
return; return;
CompletionWindow window = textEditor.CreateCompletionWindow(data.Items.Select(i => (ICompletionData)new CodeCompletionDataAdapter(i))); CompletionWindow window = CreateCompletionWindow(data);
if (window != null) if (window != null)
window.Show(); window.Show();
} }
protected virtual CompletionWindow CreateCompletionWindow(ICompletionItemList data)
{
return textEditor.CreateCompletionWindow(data.Items.Select(i => (ICompletionData)new CodeCompletionDataAdapter(i)));;
}
public string GetWordBeforeCaret() public string GetWordBeforeCaret()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -162,5 +169,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
public object Description { public object Description {
get { return item.Description; } get { return item.Description; }
} }
public ImageSource Image {
get { return null; }
}
} }
} }

26
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -5,11 +5,11 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.AvalonEdit.CodeCompletion;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
@ -47,14 +47,30 @@ namespace ICSharpCode.AvalonEdit.AddIn
base.OnPreviewTextInput(e); base.OnPreviewTextInput(e);
if (!e.Handled && e.Text.Length == 1) { if (!e.Handled && e.Text.Length == 1) {
foreach (ICodeCompletionBinding cc in CodeCompletionBindings) { foreach (ICodeCompletionBinding cc in CodeCompletionBindings) {
if (cc.HandleKeyPress(textEditorAdapter, e.Text[0])) { CompletionWindow oldCompletionWindow = lastCompletionWindow;
// Don't set e.Handled - we still want to insert the typed text. CodeCompletionKeyPressResult result = cc.HandleKeyPress(textEditorAdapter, e.Text[0]);
// This isn't optimal, but that's how the ICodeCompletionBinding interface if (result == CodeCompletionKeyPressResult.Completed) {
// has always worked; and I don't want to change too much at once. 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; return;
} }
} }
} }
} }
CompletionWindow lastCompletionWindow;
internal void NotifyCompletionWindowOpened(CompletionWindow window)
{
lastCompletionWindow = window;
window.Closed += delegate { lastCompletionWindow = null; };
}
} }
} }

9
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs

@ -5,7 +5,9 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.SharpDevelop;
using System; using System;
using ICSharpCode.AvalonEdit.CodeCompletion;
namespace ICSharpCode.AvalonEdit.AddIn namespace ICSharpCode.AvalonEdit.AddIn
{ {
@ -26,5 +28,12 @@ namespace ICSharpCode.AvalonEdit.AddIn
public override string FileName { public override string FileName {
get { return codeEditor.FileName; } get { return codeEditor.FileName; }
} }
protected override CompletionWindow CreateCompletionWindow(ICompletionItemList data)
{
CompletionWindow window = base.CreateCompletionWindow(data);
codeEditor.NotifyCompletionWindowOpened(window);
return window;
}
} }
} }

6
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs

@ -20,7 +20,7 @@ namespace Hornung.ResourceToolkit.CodeCompletion
public abstract class AbstractNRefactoryResourceCodeCompletionBinding : DefaultCodeCompletionBinding 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)) { 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); 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;
} }
// ******************************************************************************************************************************** // ********************************************************************************************************************************

8
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs

@ -21,7 +21,7 @@ namespace Hornung.ResourceToolkit.CodeCompletion
public class ICSharpCodeCoreResourceCodeCompletionBinding : DefaultCodeCompletionBinding public class ICSharpCodeCoreResourceCodeCompletionBinding : DefaultCodeCompletionBinding
{ {
public override bool HandleKeyPress(ITextEditor editor, char ch) public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
{ {
if (ch == ':') { if (ch == ':') {
@ -49,7 +49,7 @@ namespace Hornung.ResourceToolkit.CodeCompletion
if (content != null) { if (content != null) {
editor.ShowCompletionWindow(new ResourceCodeCompletionDataProvider(content, null, null), ch); 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) { ICSharpCodeCoreResourceResolver.GetICSharpCodeCoreLocalResourceSet(editor.FileName).ResourceFileContent != null) {
editor.ShowCompletionWindow(new ICSharpCodeCoreTagCompletionDataProvider(), ch); editor.ShowCompletionWindow(new ICSharpCodeCoreTagCompletionDataProvider(), ch);
return true; return CodeCompletionKeyPressResult.Completed;
} }
} }
return false; return CodeCompletionKeyPressResult.None;
} }
} }

73
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs

@ -111,6 +111,74 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
} }
} }
/// <summary>
/// Selects the item that starts with the specified text.
/// </summary>
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) void SelectIndex(int offset)
{ {
if (offset >= listBox.Items.Count) if (offset >= listBox.Items.Count)
@ -120,5 +188,10 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
listBox.SelectedIndex = offset; listBox.SelectedIndex = offset;
listBox.ScrollIntoView(listBox.SelectedItem); listBox.ScrollIntoView(listBox.SelectedItem);
} }
void CenterViewOn(int offset)
{
// TODO: implement me
}
} }
} }

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml

@ -41,7 +41,10 @@
<cc:CompletionListBox x:Name="PART_ListBox" ItemContainerStyle="{StaticResource CompletionListBoxItem}"> <cc:CompletionListBox x:Name="PART_ListBox" ItemContainerStyle="{StaticResource CompletionListBoxItem}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<ContentControl Content="{Binding Content}"/> <StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}"/>
<ContentControl Content="{Binding Content}"/>
</StackPanel>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</cc:CompletionListBox> </cc:CompletionListBox>

13
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs

@ -32,6 +32,19 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
} }
} }
/// <summary>
/// Gets the number of the first visible item.
/// </summary>
public int FirstVisibleItem {
get {
if (scrollViewer == null) {
return 0;
} else {
return (int)(this.Items.Count * scrollViewer.VerticalOffset / scrollViewer.ExtentHeight);
}
}
}
/// <summary> /// <summary>
/// Gets the number of visible items. /// Gets the number of visible items.
/// </summary> /// </summary>

77
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
@ -13,6 +14,7 @@ using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Gui;
namespace ICSharpCode.AvalonEdit.CodeCompletion namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {
@ -36,21 +38,36 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
this.Width = 200; this.Width = 200;
this.Content = completionList; this.Content = completionList;
completionList.SizeChanged += completionList_SizeChanged; startOffset = endOffset = this.TextArea.Caret.Offset;
document = textArea.TextView.Document; document = textArea.TextView.Document;
startOffset = endOffset = textArea.Caret.Offset;
} }
void completionList_SizeChanged(object sender, SizeChangedEventArgs e) /// <summary>
/// Gets/Sets the start offset of the edited text portion.
/// </summary>
public int StartOffset {
get { return startOffset; }
set { startOffset = value; }
}
/// <summary>
/// Gets/Sets the end offset of the edited text portion.
/// </summary>
public int EndOffset {
get { return endOffset; }
set { endOffset = value; }
}
/// <inheritdoc/>
protected override void OnSourceInitialized(EventArgs e)
{ {
if (completionList.ActualHeight < 200) { // prevent CompletionWindow from growing too large
this.SizeToContent = SizeToContent.Height; if (this.ActualHeight > 300) {
this.Height = double.NaN;
} else if (completionList.ActualHeight > 300) {
this.SizeToContent = SizeToContent.Manual; this.SizeToContent = SizeToContent.Manual;
this.Height = 300; this.Height = 300;
} }
base.OnSourceInitialized(e);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -60,6 +77,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
document.Changing += textArea_Document_Changing; document.Changing += textArea_Document_Changing;
this.TextArea.Caret.PositionChanged += CaretPositionChanged; this.TextArea.Caret.PositionChanged += CaretPositionChanged;
this.TextArea.MouseWheel += textArea_MouseWheel; this.TextArea.MouseWheel += textArea_MouseWheel;
this.TextArea.ActiveInputHandler = new InputHandler(this);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -69,8 +87,42 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
this.TextArea.Caret.PositionChanged -= CaretPositionChanged; this.TextArea.Caret.PositionChanged -= CaretPositionChanged;
this.TextArea.MouseWheel -= textArea_MouseWheel; this.TextArea.MouseWheel -= textArea_MouseWheel;
base.DetachEvents(); base.DetachEvents();
this.TextArea.ActiveInputHandler = this.TextArea.DefaultInputHandler;
} }
#region InputHandler
/// <summary>
/// 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.
/// </summary>
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
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnKeyDown(KeyEventArgs e) protected override void OnKeyDown(KeyEventArgs e)
{ {
@ -104,12 +156,19 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList; return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList;
} }
bool expectInsertionBeforeStart = true;
void textArea_Document_Changing(object sender, DocumentChangeEventArgs e) void textArea_Document_Changing(object sender, DocumentChangeEventArgs e)
{ {
// => startOffset test required so that this startOffset/endOffset are not incremented again // => startOffset test required so that this startOffset/endOffset are not incremented again
// for BeforeStartKey characters // for BeforeStartKey characters
if (e.Offset >= startOffset && e.Offset <= endOffset) { if (e.Offset >= startOffset && e.Offset <= endOffset) {
endOffset += e.InsertionLength - e.RemovalLength; 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 { } else {
Close(); Close();
} }
@ -133,7 +192,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
if (offset < startOffset || offset > endOffset) { if (offset < startOffset || offset > endOffset) {
Close(); Close();
} else { } else {
//codeCompletionListView.SelectItemWithStart(document.GetText(startOffset, offset - startOffset)); completionList.SelectItemWithStart(document.GetText(startOffset, offset - startOffset));
} }
} }

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICodeCompletionData.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Windows.Media;
namespace ICSharpCode.AvalonEdit.CodeCompletion namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {
@ -14,6 +15,11 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
/// </summary> /// </summary>
public interface ICompletionData public interface ICompletionData
{ {
/// <summary>
/// Gets the image.
/// </summary>
ImageSource Image { get; }
/// <summary> /// <summary>
/// Gets the text. This property is used to filter the list of visible elements. /// Gets the text. This property is used to filter the list of visible elements.
/// </summary> /// </summary>

17
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs

@ -6,11 +6,13 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Gui namespace ICSharpCode.AvalonEdit.Gui
{ {
@ -35,8 +37,19 @@ namespace ICSharpCode.AvalonEdit.Gui
static class CaretNavigationCommandHandler static class CaretNavigationCommandHandler
{ {
public static readonly CommandBindingCollection CommandBindings = new CommandBindingCollection(); /// <summary>
public static readonly InputBindingCollection InputBindings = new InputBindingCollection(); /// Creates a new <see cref="TextAreaInputHandler"/> for the text area.
/// </summary>
public static TextAreaInputHandler Create(TextArea textArea)
{
TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
handler.CommandBindings.AddRange(CommandBindings);
handler.InputBindings.AddRange(InputBindings);
return handler;
}
static readonly List<CommandBinding> CommandBindings = new List<CommandBinding>();
static readonly List<InputBinding> InputBindings = new List<InputBinding>();
static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler) static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
{ {

23
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs

@ -6,24 +6,37 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Gui namespace ICSharpCode.AvalonEdit.Gui
{ {
/// <summary>
/// We re-use the CommandBinding and InputBinding instances between multiple text areas,
/// so this class is static.
/// </summary>
static class EditingCommandHandler static class EditingCommandHandler
{ {
public static readonly CommandBindingCollection CommandBindings = new CommandBindingCollection(); /// <summary>
public static readonly InputBindingCollection InputBindings = new InputBindingCollection(); /// Creates a new <see cref="TextAreaInputHandler"/> for the text area.
/// </summary>
public static TextAreaInputHandler Create(TextArea textArea)
{
TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
handler.CommandBindings.AddRange(CommandBindings);
handler.InputBindings.AddRange(InputBindings);
return handler;
}
static readonly List<CommandBinding> CommandBindings = new List<CommandBinding>();
static readonly List<InputBinding> InputBindings = new List<InputBinding>();
static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler) static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
{ {

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs

@ -21,7 +21,7 @@ namespace ICSharpCode.AvalonEdit.Gui
/// <summary> /// <summary>
/// Handles selection of text using the mouse. /// Handles selection of text using the mouse.
/// </summary> /// </summary>
sealed class SelectionMouseHandler sealed class SelectionMouseHandler : ITextAreaInputHandler
{ {
#region enum SelectionMode #region enum SelectionMode
enum SelectionMode enum SelectionMode
@ -62,9 +62,15 @@ namespace ICSharpCode.AvalonEdit.Gui
#region Constructor + Attach + Detach #region Constructor + Attach + Detach
public SelectionMouseHandler(TextArea textArea) public SelectionMouseHandler(TextArea textArea)
{ {
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea; this.textArea = textArea;
} }
public TextArea TextArea {
get { return textArea; }
}
public void Attach() public void Attach()
{ {
textArea.MouseLeftButtonDown += textArea_MouseLeftButtonDown; textArea.MouseLeftButtonDown += textArea_MouseLeftButtonDown;

82
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs

@ -77,19 +77,46 @@ namespace ICSharpCode.AvalonEdit
caret = new Caret(this); caret = new Caret(this);
caret.PositionChanged += (sender, e) => RequestSelectionValidation(); 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.DefaultInputHandler = new TextAreaDefaultInputHandler(this);
this.InputBindings.AddRange(CaretNavigationCommandHandler.InputBindings); this.ActiveInputHandler = this.DefaultInputHandler;
this.CommandBindings.AddRange(EditingCommandHandler.CommandBindings);
this.InputBindings.AddRange(EditingCommandHandler.InputBindings);
new SelectionMouseHandler(this).Attach();
} }
#endregion #endregion
#region InputHandler management
/// <summary>
/// Gets the default input handler.
/// </summary>
public TextAreaDefaultInputHandler DefaultInputHandler { get; private set; }
ITextAreaInputHandler activeInputHandler;
/// <summary>
/// Gets/Sets the active input handler.
/// </summary>
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);
}
}
}
/// <summary>
/// Occurs when the ActiveInputHandler property changes.
/// </summary>
public event EventHandler ActiveInputHandlerChanged;
#endregion
#region Document property #region Document property
/// <summary> /// <summary>
/// Document property. /// Document property.
@ -330,43 +357,6 @@ namespace ICSharpCode.AvalonEdit
} }
#endregion #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 #region IScrollInfo implementation
ScrollViewer scrollOwner; ScrollViewer scrollOwner;
bool canVerticallyScroll, canHorizontallyScroll; bool canVerticallyScroll, canHorizontallyScroll;

84
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaDefaultInputHandlers.cs

@ -0,0 +1,84 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Gui
{
/// <summary>
/// Contains the predefined input handlers.
/// </summary>
public class TextAreaDefaultInputHandler : TextAreaInputHandler
{
/// <summary>
/// Gets the caret navigation input handler.
/// </summary>
public TextAreaInputHandler CaretNavigation { get; private set; }
/// <summary>
/// Gets the editing input handler.
/// </summary>
public TextAreaInputHandler Editing { get; private set; }
/// <summary>
/// Gets the mouse selection input handler.
/// </summary>
public ITextAreaInputHandler MouseSelection { get; private set; }
/// <summary>
/// Creates a new TextAreaDefaultInputHandler instance.
/// </summary>
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
}
}

184
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextAreaInputHandler.cs

@ -0,0 +1,184 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using ICSharpCode.AvalonEdit.Utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace ICSharpCode.AvalonEdit.Gui
{
/// <summary>
/// A set of input bindings and event handlers for the text area.
/// </summary>
public interface ITextAreaInputHandler
{
/// <summary>
/// Gets the text area that the input handler belongs to.
/// </summary>
TextArea TextArea {
get;
}
/// <summary>
/// Attaches an input handler to the text area.
/// </summary>
void Attach();
/// <summary>
/// Detaches the input handler from the text area.
/// </summary>
void Detach();
}
/// <summary>
/// Default-implementation of <see cref="ITextAreaInputHandler"/>.
/// </summary>
public class TextAreaInputHandler : ITextAreaInputHandler
{
readonly ObserveAddRemoveCollection<CommandBinding> commandBindings;
readonly ObserveAddRemoveCollection<InputBinding> inputBindings;
readonly ObserveAddRemoveCollection<ITextAreaInputHandler> nestedInputHandlers;
readonly TextArea textArea;
bool isAttached;
/// <summary>
/// Creates a new TextAreaInputHandler.
/// </summary>
public TextAreaInputHandler(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
commandBindings = new ObserveAddRemoveCollection<CommandBinding>(CommandBinding_Added, CommandBinding_Removed);
inputBindings = new ObserveAddRemoveCollection<InputBinding>(InputBinding_Added, InputBinding_Removed);
nestedInputHandlers = new ObserveAddRemoveCollection<ITextAreaInputHandler>(NestedInputHandler_Added, NestedInputHandler_Removed);
}
/// <inheritdoc/>
public TextArea TextArea {
get { return textArea; }
}
/// <summary>
/// Gets whether the input handler is currently attached to the text area.
/// </summary>
public bool IsAttached {
get { return isAttached; }
}
#region CommandBindings / InputBindings
/// <summary>
/// Gets the command bindings of this input handler.
/// </summary>
public ICollection<CommandBinding> 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);
}
/// <summary>
/// Gets the input bindings of this input handler.
/// </summary>
public ICollection<InputBinding> 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);
}
/// <summary>
/// Adds a command and input binding.
/// </summary>
/// <param name="command">The command ID.</param>
/// <param name="modifiers">The modifiers of the keyboard shortcut.</param>
/// <param name="key">The key of the keyboard shortcut.</param>
/// <param name="handler">The event handler to run when the command is executed.</param>
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
/// <summary>
/// Gets the collection of nested input handlers. NestedInputHandlers are activated and deactivated
/// together with this input handler.
/// </summary>
public ICollection<ITextAreaInputHandler> 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
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
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
}
}

88
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs

@ -50,9 +50,9 @@ namespace ICSharpCode.AvalonEdit.Gui
public TextView() public TextView()
{ {
textLayer = new TextLayer(this); textLayer = new TextLayer(this);
elementGenerators.CollectionChanged += elementGenerators_CollectionChanged; elementGenerators = new ObserveAddRemoveCollection<VisualLineElementGenerator>(ElementGenerator_Added, ElementGenerator_Removed);
lineTransformers.CollectionChanged += lineTransformers_CollectionChanged; lineTransformers = new ObserveAddRemoveCollection<IVisualLineTransformer>(LineTransformer_Added, LineTransformer_Removed);
backgroundRenderer.CollectionChanged += backgroundRenderer_CollectionChanged; backgroundRenderers = new ObserveAddRemoveCollection<IBackgroundRenderer>(BackgroundRenderer_Added, BackgroundRenderer_Removed);
layers = new UIElementCollection(this, this); layers = new UIElementCollection(this, this);
InsertLayer(textLayer, KnownLayer.Text, LayerInsertionPosition.Replace); InsertLayer(textLayer, KnownLayer.Text, LayerInsertionPosition.Replace);
} }
@ -128,33 +128,45 @@ namespace ICSharpCode.AvalonEdit.Gui
#endregion #endregion
#region ElementGenerators+LineTransformers Properties #region ElementGenerators+LineTransformers Properties
readonly ObservableCollection<VisualLineElementGenerator> elementGenerators = new ObservableCollection<VisualLineElementGenerator>(); readonly ObserveAddRemoveCollection<VisualLineElementGenerator> elementGenerators;
/// <summary> /// <summary>
/// Gets a collection where element generators can be registered. /// Gets a collection where element generators can be registered.
/// </summary> /// </summary>
public ObservableCollection<VisualLineElementGenerator> ElementGenerators { public IList<VisualLineElementGenerator> ElementGenerators {
get { return elementGenerators; } get { return elementGenerators; }
} }
void elementGenerators_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) void ElementGenerator_Added(VisualLineElementGenerator generator)
{ {
HandleTextViewConnect(e); ConnectToTextView(generator);
Redraw(); Redraw();
} }
readonly ObservableCollection<IVisualLineTransformer> lineTransformers = new ObservableCollection<IVisualLineTransformer>(); void ElementGenerator_Removed(VisualLineElementGenerator generator)
{
DisconnectFromTextView(generator);
Redraw();
}
readonly ObserveAddRemoveCollection<IVisualLineTransformer> lineTransformers;
/// <summary> /// <summary>
/// Gets a collection where line transformers can be registered. /// Gets a collection where line transformers can be registered.
/// </summary> /// </summary>
public ObservableCollection<IVisualLineTransformer> LineTransformers { public IList<IVisualLineTransformer> LineTransformers {
get { return 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(); Redraw();
} }
#endregion #endregion
@ -700,18 +712,29 @@ namespace ICSharpCode.AvalonEdit.Gui
#endregion #endregion
#region Render #region Render
readonly ObservableCollection<IBackgroundRenderer> backgroundRenderer = new ObservableCollection<IBackgroundRenderer>(); readonly ObserveAddRemoveCollection<IBackgroundRenderer> backgroundRenderers;
/// <summary> /// <summary>
/// Gets the list of background renderers. /// Gets the list of background renderers.
/// </summary> /// </summary>
public ObservableCollection<IBackgroundRenderer> BackgroundRenderer { public IList<IBackgroundRenderer> BackgroundRenderers {
get { return backgroundRenderer; } 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(); InvalidateVisual();
foreach (UIElement layer in this.Layers) { foreach (UIElement layer in this.Layers) {
// invalidate known layers // invalidate known layers
@ -728,7 +751,7 @@ namespace ICSharpCode.AvalonEdit.Gui
internal void RenderBackground(DrawingContext drawingContext, KnownLayer layer) internal void RenderBackground(DrawingContext drawingContext, KnownLayer layer)
{ {
foreach (IBackgroundRenderer bg in backgroundRenderer) { foreach (IBackgroundRenderer bg in backgroundRenderers) {
if (bg.Layer == layer) { if (bg.Layer == layer) {
bg.Draw(drawingContext); bg.Draw(drawingContext);
} }
@ -1155,27 +1178,18 @@ namespace ICSharpCode.AvalonEdit.Gui
return services.GetService(serviceType); return services.GetService(serviceType);
} }
void HandleTextViewConnect(NotifyCollectionChangedEventArgs e) void ConnectToTextView(object obj)
{ {
switch (e.Action) { ITextViewConnect c = obj as ITextViewConnect;
case NotifyCollectionChangedAction.Add: if (c != null)
case NotifyCollectionChangedAction.Remove: c.AddToTextView(this);
case NotifyCollectionChangedAction.Replace: }
if (e.OldItems != null) {
foreach (ITextViewConnect c in e.OldItems.OfType<ITextViewConnect>()) void DisconnectFromTextView(object obj)
c.RemoveFromTextView(this); {
} ITextViewConnect c = obj as ITextViewConnect;
if (e.NewItems != null) { if (c != null)
foreach (ITextViewConnect c in e.NewItems.OfType<ITextViewConnect>()) c.RemoveFromTextView(this);
c.AddToTextView(this);
}
break;
case NotifyCollectionChangedAction.Move:
// ignore Move
break;
default:
throw new NotSupportedException(e.Action.ToString());
}
} }
#endregion #endregion

3
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -134,6 +134,8 @@
<DependentUpon>FoldingManager.cs</DependentUpon> <DependentUpon>FoldingManager.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Gui\IBackgroundRenderer.cs" /> <Compile Include="Gui\IBackgroundRenderer.cs" />
<Compile Include="Gui\TextAreaDefaultInputHandlers.cs" />
<Compile Include="Gui\TextAreaInputHandler.cs" />
<Compile Include="Gui\IReadOnlySectionProvider.cs" /> <Compile Include="Gui\IReadOnlySectionProvider.cs" />
<Compile Include="Gui\ITextViewConnect.cs"> <Compile Include="Gui\ITextViewConnect.cs">
<DependentUpon>TextView.cs</DependentUpon> <DependentUpon>TextView.cs</DependentUpon>
@ -278,6 +280,7 @@
<Compile Include="Utils\HtmlClipboard.cs" /> <Compile Include="Utils\HtmlClipboard.cs" />
<Compile Include="Utils\ImmutableStack.cs" /> <Compile Include="Utils\ImmutableStack.cs" />
<Compile Include="Utils\NullSafeCollection.cs" /> <Compile Include="Utils\NullSafeCollection.cs" />
<Compile Include="Utils\ObserveAddRemoveCollection.cs" />
<Compile Include="Utils\TextUtilities.cs" /> <Compile Include="Utils\TextUtilities.cs" />
<Compile Include="Utils\WeakEventManagerBase.cs" /> <Compile Include="Utils\WeakEventManagerBase.cs" />
<Compile Include="Utils\PixelSnapHelpers.cs" /> <Compile Include="Utils\PixelSnapHelpers.cs" />

64
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ObserveAddRemoveCollection.cs

@ -0,0 +1,64 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.ObjectModel;
namespace ICSharpCode.AvalonEdit.Utils
{
/// <summary>
/// A collection where adding and removing items causes a callback.
/// </summary>
sealed class ObserveAddRemoveCollection<T> : Collection<T>
{
readonly Action<T> onAdd, onRemove;
public ObserveAddRemoveCollection(Action<T> onAdd, Action<T> 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);
}
}
}

4
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -750,10 +750,6 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj">
<Project>{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}</Project>
<Name>ICSharpCode.AvalonEdit</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\ICSharpCode.TextEditor\Project\ICSharpCode.TextEditor.csproj"> <ProjectReference Include="..\..\..\Libraries\ICSharpCode.TextEditor\Project\ICSharpCode.TextEditor.csproj">
<Project>{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}</Project> <Project>{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}</Project>
<Name>ICSharpCode.TextEditor</Name> <Name>ICSharpCode.TextEditor</Name>

12
src/Main/Base/Project/Src/TextEditor/CodeCompletionItemProvider.cs

@ -37,6 +37,11 @@ namespace ICSharpCode.SharpDevelop
} }
public virtual ExpressionResult GetExpression(ITextEditor editor) public virtual ExpressionResult GetExpression(ITextEditor editor)
{
return GetExpressionFromOffset(editor, editor.Caret.Offset);
}
protected ExpressionResult GetExpressionFromOffset(ITextEditor editor, int offset)
{ {
if (editor == null) if (editor == null)
throw new ArgumentNullException("editor"); throw new ArgumentNullException("editor");
@ -45,7 +50,7 @@ namespace ICSharpCode.SharpDevelop
if (expressionFinder == null) { if (expressionFinder == null) {
return ExpressionResult.Empty; return ExpressionResult.Empty;
} else { } 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 public class CodeCompletionItem : ICompletionItem
{ {
readonly IEntity entity; readonly IEntity entity;

66
src/Main/Base/Project/Src/TextEditor/Gui/Editor/CodeCompletionBinding.cs

@ -18,10 +18,29 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
/// </summary> /// </summary>
public interface ICodeCompletionBinding public interface ICodeCompletionBinding
{ {
bool HandleKeyPress(ITextEditor editor, char ch); CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch);
bool CtrlSpace(ITextEditor editor); bool CtrlSpace(ITextEditor editor);
} }
/// <summary>
/// The result of <see cref="ICodeCompletionBinding.HandleKeyPress"/>.
/// </summary>
public enum CodeCompletionKeyPressResult
{
/// <summary>
/// The binding did not run code completion. The pressed key will be handled normally.
/// </summary>
None,
/// <summary>
/// The binding handled code completion, the pressed key will be handled normally.
/// </summary>
Completed,
/// <summary>
/// The binding handled code completion, and the key will not be handled by the text editor.
/// </summary>
EatKey
}
/// <summary> /// <summary>
/// Creates code completion bindings that manage code completion for one language. /// Creates code completion bindings that manage code completion for one language.
/// </summary> /// </summary>
@ -68,7 +87,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
this.extensions = extensions; this.extensions = extensions;
} }
public bool HandleKeyPress(ITextEditor editor, char ch) public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
{ {
string ext = Path.GetExtension(editor.FileName); string ext = Path.GetExtension(editor.FileName);
foreach (string extension in extensions) { foreach (string extension in extensions) {
@ -79,7 +98,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
return binding.HandleKeyPress(editor, ch); return binding.HandleKeyPress(editor, ch);
} }
} }
return false; return CodeCompletionKeyPressResult.None;
} }
public bool CtrlSpace(ITextEditor editor) 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) { switch (ch) {
case '(': case '(':
if (enableMethodInsight && CodeCompletionOptions.InsightEnabled) { if (enableMethodInsight && CodeCompletionOptions.InsightEnabled) {
editor.ShowInsightWindow(new MethodInsightDataProvider()); editor.ShowInsightWindow(new MethodInsightDataProvider());
return true; return CodeCompletionKeyPressResult.Completed;
} else {
return false;
} }
break;
case '[': case '[':
if (enableIndexerInsight && CodeCompletionOptions.InsightEnabled) { if (enableIndexerInsight && CodeCompletionOptions.InsightEnabled) {
editor.ShowInsightWindow(new IndexerInsightDataProvider()); editor.ShowInsightWindow(new IndexerInsightDataProvider());
return true; return CodeCompletionKeyPressResult.Completed;
} else {
return false;
} }
break;
case '<': case '<':
if (enableXmlCommentCompletion) { if (enableXmlCommentCompletion) {
editor.ShowCompletionWindow(new CommentCompletionDataProvider(), ch); editor.ShowCompletionWindow(new CommentCompletionDataProvider(), ch);
return true; return CodeCompletionKeyPressResult.Completed;
} else {
return false;
} }
break;
case '.': case '.':
if (enableDotCompletion) { if (enableDotCompletion) {
new CodeCompletionItemProvider().ShowCompletion(editor); new DotCodeCompletionItemProvider().ShowCompletion(editor);
return true; return CodeCompletionKeyPressResult.Completed;
} else {
return false;
} }
break;
case ' ': case ' ':
if (!CodeCompletionOptions.KeywordCompletionEnabled) if (CodeCompletionOptions.KeywordCompletionEnabled) {
return false; string word = editor.GetWordBeforeCaret();
string word = editor.GetWordBeforeCaret(); if (word != null) {
if (word != null) return HandleKeyword(editor, word) ? CodeCompletionKeyPressResult.Completed : CodeCompletionKeyPressResult.None;
return HandleKeyword(editor, word); }
else }
return false; break;
default:
return false;
} }
return CodeCompletionKeyPressResult.None;
} }
public virtual bool HandleKeyword(ITextEditor editor, string word) public virtual bool HandleKeyword(ITextEditor editor, string word)

5
src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs

@ -328,8 +328,11 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
if (CodeCompletionOptions.EnableCodeCompletion) { if (CodeCompletionOptions.EnableCodeCompletion) {
foreach (ICodeCompletionBinding ccBinding in CodeCompletionBindings) { foreach (ICodeCompletionBinding ccBinding in CodeCompletionBindings) {
if (ccBinding.HandleKeyPress(adapter, ch)) CodeCompletionKeyPressResult result = ccBinding.HandleKeyPress(adapter, ch);
if (result == CodeCompletionKeyPressResult.Completed)
return false; return false;
else if (result == CodeCompletionKeyPressResult.EatKey)
return true;
} }
} }
} catch (Exception ex) { } catch (Exception ex) {

1
src/Main/Base/Project/Src/TextEditor/ITextEditor.cs

@ -38,6 +38,7 @@ namespace ICSharpCode.SharpDevelop
void Select(int selectionStart, int selectionLength); void Select(int selectionStart, int selectionLength);
string FileName { get; } string FileName { get; }
void ShowInsightWindow(ICSharpCode.TextEditor.Gui.InsightWindow.IInsightDataProvider provider); void ShowInsightWindow(ICSharpCode.TextEditor.Gui.InsightWindow.IInsightDataProvider provider);
[Obsolete] [Obsolete]
void ShowCompletionWindow(ICSharpCode.TextEditor.Gui.CompletionWindow.ICompletionDataProvider provider, char ch); void ShowCompletionWindow(ICSharpCode.TextEditor.Gui.CompletionWindow.ICompletionDataProvider provider, char ch);

Loading…
Cancel
Save