diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs index 1ca93a3bb3..c81726df9c 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs @@ -73,7 +73,7 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets public ICompletionItem CreateCompletionItem(ITextEditor context) { - return new SnippetCompletionItem(context, this); + return new SnippetCompletionItem(context, this) { AlwaysInsertSnippet = context.SelectionLength > 0 }; } readonly static Regex pattern = new Regex(@"\$\{([^\}]*)\}", RegexOptions.CultureInvariant); @@ -104,8 +104,8 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets if (pos < snippetText.Length) { snippet.Elements.Add(new SnippetTextElement { Text = snippetText.Substring(pos) }); } - if (!snippet.Elements.Any(e2 => e2 is SnippetCaretElement)) { - int index = snippet.Elements.IndexOf(e => e is SnippetSelectionElement); + if (!snippet.Elements.Any(e => e is SnippetCaretElement)) { + int index = snippet.Elements.FindIndex(e2 => e2 is SnippetSelectionElement); if (index > -1) snippet.Elements.Insert(index + 1, new SnippetCaretElement()); } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetCompletionItem.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetCompletionItem.cs index d67484b2b5..f181b5f0e4 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetCompletionItem.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetCompletionItem.cs @@ -40,6 +40,8 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets this.codeSnippet = codeSnippet; } + public bool AlwaysInsertSnippet { get; set; } + public string Text { get { return codeSnippet.Name; } } @@ -62,7 +64,7 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets if (context.Editor != this.textEditor) throw new ArgumentException("wrong editor"); using (context.Editor.Document.OpenUndoGroup()) { - if (context.CompletionChar == '\t') { + if (context.CompletionChar == '\t' || AlwaysInsertSnippet) { context.Editor.Document.Remove(context.StartOffset, context.Length); CreateSnippet().Insert(textArea); } else { diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs index 1a2f66e10c..34d5f81629 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs @@ -53,7 +53,7 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets new CodeSnippet { Name = "prop", Description = "Property", - Text = "${type} ${toFieldName(name)};\n\npublic ${type=int} ${name=Property} {\n\tget { return ${toFieldName(name)}; }\n\tset { ${toFieldName(name)} = value; }\n}" + Text = "${type} ${toFieldName(name)};\n\npublic ${type=int} ${name=Property} {\n\tget { return ${toFieldName(name)}; }\n\tset { ${toFieldName(name)} = value; }\n}${Caret}" }, new CodeSnippet { Name = "propdp", @@ -65,17 +65,22 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets + "public ${type=int} ${name=Property} {" + Environment.NewLine + "\tget { return (${type})GetValue(${name}Property); }" + Environment.NewLine + "\tset { SetValue(${name}Property, value); }" - + Environment.NewLine + "}" + + Environment.NewLine + "}${Caret}" + }, + new CodeSnippet { + Name = "props", + Description = "Property", + Text = "public ${Type=object} ${Property=Property} { get; set; }${Caret}" }, new CodeSnippet { Name = "ctor", Description = "Constructor", - Text = "public ${ClassName}()\n{\t\n${Selection}\n}" + Text = "public ${ClassName}(${Caret})\n{\t\n${Selection}\n}" }, new CodeSnippet { Name = "switch", Description = "Switch statement", - Text = "switch (${condition}) {\n\tcase ${firstcase=0}:\n\t\tbreak;\n\tdefault:\n\t\t${Selection}\n\t\tbreak;\n}" + Text = "switch (${condition}) {\n\tcase ${firstcase=0}:\n\t\t${Caret}\n\t\tbreak;\n\tdefault:\n\t\t${Selection}\n\t\tbreak;\n}" }, new CodeSnippet { Name = "try", diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index d67fba149a..607d66718e 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -279,6 +279,7 @@ VisualLine.cs + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/InsertionContext.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/InsertionContext.cs index 3be38b9c1a..1a8b5e6a68 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/InsertionContext.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/InsertionContext.cs @@ -188,7 +188,7 @@ namespace ICSharpCode.AvalonEdit.Snippets currentStatus = Status.Interactive; if (registeredElements.Count == 0) { // deactivate immediately if there are no interactive elements - Deactivate(EventArgs.Empty); + Deactivate(new SnippetEventArgs(DeactivateReason.NoActiveElements)); } else { myInputHandler = new SnippetInputHandler(this); // disable existing snippet input handlers - there can be only 1 active snippet @@ -211,14 +211,14 @@ namespace ICSharpCode.AvalonEdit.Snippets /// Calls the method on all registered active elements. /// /// The EventArgs to use - public void Deactivate(EventArgs e) + public void Deactivate(SnippetEventArgs e) { if (currentStatus == Status.Deactivated || currentStatus == Status.RaisingDeactivated) return; if (currentStatus != Status.Interactive) throw new InvalidOperationException("Cannot call Deactivate() until RaiseInsertionCompleted() has finished."); if (e == null) - e = EventArgs.Empty; + e = new SnippetEventArgs(DeactivateReason.Unknown); TextDocumentWeakEventManager.UpdateFinished.RemoveListener(Document, this); currentStatus = Status.RaisingDeactivated; @@ -234,7 +234,7 @@ namespace ICSharpCode.AvalonEdit.Snippets /// /// Occurs when the interactive mode is deactivated. /// - public event EventHandler Deactivated; + public event EventHandler Deactivated; bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { @@ -248,7 +248,7 @@ namespace ICSharpCode.AvalonEdit.Snippets // Deactivate if snippet is deleted. This is necessary for correctly leaving interactive // mode if Undo is pressed after a snippet insertion. if (wholeSnippetAnchor.Length == 0) - Deactivate(e); + Deactivate(new SnippetEventArgs(DeactivateReason.Deleted)); return true; } return false; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetCaretElement.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetCaretElement.cs index 8fed95701e..b2b97ca67e 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetCaretElement.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetCaretElement.cs @@ -19,12 +19,17 @@ namespace ICSharpCode.AvalonEdit.Snippets { /// public override void Insert(InsertionContext context) + { + if (!string.IsNullOrEmpty(context.SelectedText)) + SetCaret(context); + } + + internal static void SetCaret(InsertionContext context) { TextAnchor pos = context.Document.CreateAnchor(context.InsertionPosition); pos.SurviveDeletion = true; context.Deactivated += (sender, e) => { - KeyEventArgs ke = e as KeyEventArgs; - if (ke != null && ke.Key == Key.Return) { + if (e.Reason == DeactivateReason.ReturnPressed || e.Reason == DeactivateReason.NoActiveElements) { context.TextArea.Caret.Offset = pos.Offset; } }; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetEventArgs.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetEventArgs.cs new file mode 100644 index 0000000000..ea7f362cf8 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetEventArgs.cs @@ -0,0 +1,61 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.AvalonEdit.Snippets +{ + /// + /// Provides information about the event that occured during use of snippets. + /// + public class SnippetEventArgs : EventArgs + { + /// + /// Gets the reason for deactivation. + /// + public DeactivateReason Reason { get; private set; } + + /// + /// Creates a new SnippetEventArgs object, with a DeactivateReason. + /// + public SnippetEventArgs(DeactivateReason reason) + { + this.Reason = reason; + } + } + + /// + /// Describes the reason for deactivation of a . + /// + public enum DeactivateReason + { + /// + /// Unknown reason. + /// + Unknown, + /// + /// Snippet was deleted. + /// + Deleted, + /// + /// There are no active elements in the snippet. + /// + NoActiveElements, + /// + /// The SnippetInputHandler was detached. + /// + InputHandlerDetached, + /// + /// Return was pressed by the user. + /// + ReturnPressed, + /// + /// Escape was pressed by the user. + /// + EscapePressed + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetInputHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetInputHandler.cs index a1273b5587..d32450e2bb 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetInputHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetInputHandler.cs @@ -36,14 +36,17 @@ namespace ICSharpCode.AvalonEdit.Snippets public override void Detach() { base.Detach(); - context.Deactivate(EventArgs.Empty); + context.Deactivate(new SnippetEventArgs(DeactivateReason.InputHandlerDetached)); } public override void OnPreviewKeyDown(KeyEventArgs e) { base.OnPreviewKeyDown(e); - if (e.Key == Key.Escape || e.Key == Key.Return) { - context.Deactivate(e); + if (e.Key == Key.Escape) { + context.Deactivate(new SnippetEventArgs(DeactivateReason.EscapePressed)); + e.Handled = true; + } else if (e.Key == Key.Return) { + context.Deactivate(new SnippetEventArgs(DeactivateReason.ReturnPressed)); e.Handled = true; } else if (e.Key == Key.Tab) { bool backwards = e.KeyboardDevice.Modifiers == ModifierKeys.Shift; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetReplaceableTextElement.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetReplaceableTextElement.cs index 875d0697af..75a42eee8b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetReplaceableTextElement.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetReplaceableTextElement.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.AvalonEdit.Snippets void AnchorDeleted(object sender, EventArgs e) { - context.Deactivate(EventArgs.Empty); + context.Deactivate(new SnippetEventArgs(DeactivateReason.Deleted)); } public void OnInsertionCompleted() diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetSelectionElement.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetSelectionElement.cs index a067ace6db..f8410f6685 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetSelectionElement.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetSelectionElement.cs @@ -21,6 +21,8 @@ namespace ICSharpCode.AvalonEdit.Snippets public override void Insert(InsertionContext context) { context.InsertText(context.SelectedText); + if (string.IsNullOrEmpty(context.SelectedText)) + SnippetCaretElement.SetCaret(context); } } } diff --git a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs index d30c09d565..0a00f553d3 100644 --- a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs +++ b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs @@ -410,7 +410,7 @@ namespace ICSharpCode.SharpDevelop /// Returns the index of the first element for which constraint returns true. /// If none of the items in the list fits the constraint -1 is returned. /// - public static int IndexOf(this IList list, Func constraint) { + public static int FindIndex(this IList list, Func constraint) { for (int i = 0; i < list.Count; i++) { if (constraint(list[i])) return i;