diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs
index a99852e7cf..77030adb9c 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs
@@ -82,7 +82,10 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets
new CodeSnippet {
Name = "switch",
Description = "Switch statement",
- Text = "switch (${condition}) {\n\tcase ${firstcase=0}:\n\t\t${Caret}\n\t\tbreak;\n\tdefault:\n\t\t${Selection}\n\t\tbreak;\n}"
+ // dynamic switch snippet (inserts switch body dependent on condition)
+ Text = "switch (${condition}) {\n${refactoring:switchbody}}"
+ // static switch snippet (always inserts the same, independent of condition)
+ //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/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin
index 796c6ebcaf..7c2e86ccf0 100644
--- a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin
+++ b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin
@@ -48,6 +48,7 @@
+
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
index 6e543107f9..e1c5959aad 100644
--- a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
+++ b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
@@ -107,6 +107,8 @@
+
+
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchBodySnippetElement.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchBodySnippetElement.cs
new file mode 100644
index 0000000000..dcde451ae4
--- /dev/null
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchBodySnippetElement.cs
@@ -0,0 +1,218 @@
+//
+//
+//
+//
+// $Revision: $
+//
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using ICSharpCode.AvalonEdit.Snippets;
+using ICSharpCode.Core;
+using ICSharpCode.NRefactory;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Dom;
+using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;
+using ICSharpCode.SharpDevelop.Dom.Refactoring;
+using ICSharpCode.SharpDevelop.Editor;
+
+namespace SharpRefactoring
+{
+ ///
+ /// The snippet element inserted inside the body curly braces.
+ /// When snippet interactive mode ends, resolves type of switch condition and generates switch cases.
+ ///
+ public class SwitchBodySnippetElement : SnippetElement//, IActiveElement would add this.Segment to know where to insert
+ {
+ InsertionContext context;
+
+ ///
+ /// The ITextEditor in which this switch body element has been inserted.
+ ///
+ public ITextEditor Editor {
+ get { return context.TextArea.GetService(typeof(ITextEditor)) as ITextEditor; }
+ }
+
+ ///
+ /// Called when this switch body element is inserted to the editor.
+ ///
+ public override void Insert(InsertionContext context)
+ {
+ this.context = context;
+ this.context.Deactivated += new EventHandler(InteractiveModeCompleted);
+ }
+
+ ///
+ /// Main logic of switch spinnet. Called when user ends the snippet interactive mode.
+ /// Inserts switch body depending on the type of switch condition:
+ /// Inserts all cases if condition is enum, generic switch body with one case otherwise.
+ ///
+ void InteractiveModeCompleted(object sender, SnippetEventArgs e)
+ {
+ if (e.Reason != DeactivateReason.ReturnPressed)
+ return;
+
+ int offset;
+ // conditionText is currently not needed,
+ // could be useful if NRefactory had a public method for resolving string expressions
+ // (NRefactoryResolver.ParseExpression is private)
+ string conditionText = GetSwitchConditionText(this.context, out offset);
+ IReturnType conditionType = ResolveConditionType(offset);
+
+ int lineStart = this.Editor.Document.GetLineForOffset(offset).Offset;
+ // indent the whole switch body appropriately
+ string indent = DocumentUtilitites.GetWhitespaceAfter(this.Editor.Document, lineStart);
+ indent += '\t';
+ // switch body starts at next line
+ int switchBodyOffset = GetNextLineStart(this.Editor.Document, offset);
+
+ if (conditionType != null && IsEnum(conditionType)) {
+ GenerateEnumSwitchBodyCode(switchBodyOffset, conditionType, indent);
+ } else {
+ GenerateGenericSwitchBodyCode(switchBodyOffset, indent);
+ }
+ }
+
+ ///
+ /// Inserts switch body for enum to the given offset.
+ ///
+ /// Whole text will be indented by this value.
+ void GenerateEnumSwitchBodyCode(int offset, IReturnType enumType, string indent)
+ {
+ string switchBody = GetSwitchBodyCode(enumType, indent, GetCodeGeneratorForCurrentFile());
+ this.Editor.Document.Insert(offset, switchBody);
+ }
+
+ ///
+ /// Inserts generic switch body to the given offset.
+ ///
+ /// Whole text will be indented by this value.
+ void GenerateGenericSwitchBodyCode(int offset, string indent)
+ {
+ string switchBody = GetGenericBodyCode(indent, GetCodeGeneratorForCurrentFile());
+ this.Editor.Document.Insert(offset, switchBody);
+ }
+
+ // TODO this should use CodeGenerator and build a BlockStatement to work for both C# and VB
+ string GetSwitchBodyCode(IReturnType enumType, string indent, CodeGenerator generator)
+ {
+ if (generator == null)
+ return string.Empty;
+ StringBuilder sb = new StringBuilder();
+ foreach (var enumCase in GetEnumCases(enumType)) {
+ sb.AppendLine(string.Format(indent + "case {0}:", enumCase.Name));
+ sb.AppendLine(indent + '\t');
+ sb.AppendLine(indent + "\tbreak;");
+ }
+ sb.AppendLine(indent + "default:");
+ sb.AppendLine(string.Format(indent + "\tthrow new Exception(\"Invalid value for {0}\");", enumType.Name));
+ return sb.ToString();
+ }
+
+ // TODO this should use CodeGenerator and build a BlockStatement to work for both C# and VB
+ string GetGenericBodyCode(string indent, CodeGenerator generator)
+ {
+ if (generator == null)
+ return string.Empty;
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine(indent + "case :");
+ sb.AppendLine(indent + '\t');
+ sb.AppendLine(indent + "\tbreak;");
+ sb.AppendLine(indent + "default:");
+ sb.AppendLine(indent + '\t');
+ sb.AppendLine(indent + "\tbreak;");
+ return sb.ToString();
+ }
+
+ bool IsEnum(IReturnType type)
+ {
+ var typeClass = type.GetUnderlyingClass();
+ if (typeClass == null)
+ // eg. MethodGroup type has no UnderlyingClass
+ return false;
+ return typeClass.BaseClass.DotNetName == "System.Enum";
+ }
+
+ ///
+ /// Gets enum values out of enum type.
+ ///
+ IEnumerable GetEnumCases(IReturnType enumType)
+ {
+ var typeClass = enumType.GetUnderlyingClass();
+ if (typeClass == null)
+ // eg. MethodGroup type has no UnderlyingClass
+ yield break;
+ foreach (var enumValue in typeClass.AllMembers) {
+ yield return enumValue as DefaultField;
+ }
+ }
+
+ ///
+ /// Assuming that interactive mode of 'switch' snippet has currently finished in context,
+ /// returns the switch condition that user entered.
+ ///
+ string GetSwitchConditionText(InsertionContext context, out int startOffset)
+ {
+ var snippetActiveElements = context.ActiveElements.ToList();
+ if (snippetActiveElements.Count == 0)
+ throw new InvalidOperationException("Switch snippet should have at least one active element");
+ var switchConditionElement = snippetActiveElements[0] as IReplaceableActiveElement;
+ if (switchConditionElement == null)
+ throw new InvalidOperationException("Switch snippet condition should be " + typeof(IReplaceableActiveElement).Name);
+ if (switchConditionElement.Segment == null)
+ throw new InvalidOperationException("Swith condition should have a start offset");
+ startOffset = switchConditionElement.Segment.EndOffset - 1;
+ return switchConditionElement.Text;
+ }
+
+ ///
+ /// Resolves the Dom.IReturnType of expression ending at offset, ie. the switch condition expression.
+ ///
+ IReturnType ResolveConditionType(int offset)
+ {
+ // this could be just solved by making NRefactoryResolver.ParseExpression public and passing the string
+ // - no need for offset and expressionFinder / ExpressionResult
+ ITextEditor textEditor = this.Editor;
+ if (textEditor == null)
+ return null;
+ IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(textEditor.FileName);
+ if (expressionFinder == null)
+ return null;
+ ExpressionResult expressionResult = expressionFinder.FindFullExpression(textEditor.Document.Text, offset);
+
+ Location location = textEditor.Document.OffsetToPosition(offset);
+ var result = ParserService.Resolve(expressionResult, location.Line, location.Column, textEditor.FileName, textEditor.Document.Text);
+ return result.ResolvedType;
+
+ /*
+ // alternate way to ParserService.Resolve
+ var resolver = new NRefactoryResolver(textEditor.Language.Properties);
+ ResolveResult rr = resolver.Resolve(expressionResult, ParserService.GetParseInformation(textEditor.FileName), textEditor.Document.Text);
+
+ return rr.ResolvedType;*/
+ }
+
+ ///
+ /// Returns CodeGenerator for C# or VB depending on the current file where snippet is being inserted.
+ ///
+ CodeGenerator GetCodeGeneratorForCurrentFile()
+ {
+ ParseInformation parseInfo = ParserService.GetParseInformation(this.Editor.FileName);
+ if (parseInfo != null) {
+ return parseInfo.CompilationUnit.Language.CodeGenerator;
+ }
+ return null;
+ }
+
+ ///
+ /// Get start of the next line (that is, from offset, go one line down and to its start).
+ ///
+ int GetNextLineStart(IDocument document, int offset)
+ {
+ int nextLineNuber = document.GetLineForOffset(offset).LineNumber + 1;
+ return document.GetLine(nextLineNuber).Offset;
+ }
+ }
+}
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchSnippetProvider.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchSnippetProvider.cs
new file mode 100644
index 0000000000..5e9cbcc3d5
--- /dev/null
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchSnippetProvider.cs
@@ -0,0 +1,30 @@
+//
+//
+//
+//
+// $Revision: $
+//
+using System;
+using ICSharpCode.AvalonEdit.Snippets;
+using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
+
+namespace SharpRefactoring
+{
+ ///
+ /// Description of SwitchSnippetProvider.
+ ///
+ public class SwitchSnippetProvider : ISnippetElementProvider
+ {
+ public SwitchSnippetProvider()
+ {
+ }
+
+ public SnippetElement GetElement(string tag)
+ {
+ if (tag.Equals("refactoring:switchbody", StringComparison.InvariantCultureIgnoreCase))
+ return new SwitchBodySnippetElement();
+
+ return null;
+ }
+ }
+}