Browse Source

Implemented switch snippet which inserts switch body dependent on switch condition. For enum condition prefills are enum cases. For non-enum condition inserts generic switch body with one case, like the old snippet.

TODO: make the code generation work for VB (using CodeGenerator), now C# switch is always inserted.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5710 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Martin Koníček 16 years ago
parent
commit
de1fefa9a9
  1. 5
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs
  2. 1
      src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin
  3. 2
      src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
  4. 218
      src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchBodySnippetElement.cs
  5. 30
      src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchSnippetProvider.cs

5
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/SnippetManager.cs

@ -82,7 +82,10 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets @@ -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",

1
src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin

@ -48,6 +48,7 @@ @@ -48,6 +48,7 @@
<Path name="/SharpDevelop/ViewContent/AvalonEdit/SnippetElementProviders">
<Class id="ctor" class="SharpRefactoring.InsertCtorSnippetRefactoring" />
<Class id="ctor" class="SharpRefactoring.SwitchSnippetProvider" />
</Path>
<Path name="/SharpDevelop/ViewContent/TextEditor/OverrideCompletionHandler">

2
src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj

@ -107,6 +107,8 @@ @@ -107,6 +107,8 @@
<Compile Include="Src\Options.cs" />
<Compile Include="Src\ResolveAttribute.cs" />
<Compile Include="Src\ResolveExtensionMethod.cs" />
<Compile Include="Src\SwitchBodySnippetElement.cs" />
<Compile Include="Src\SwitchSnippetProvider.cs" />
<Compile Include="Src\Visitors\FindJumpInstructionsVisitor.cs" />
<Compile Include="Src\Visitors\FindMemberVisitor.cs" />
<Compile Include="Src\Visitors\FindReferenceVisitor.cs" />

218
src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchBodySnippetElement.cs

@ -0,0 +1,218 @@ @@ -0,0 +1,218 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Konicek" email="martin.konicek@gmail.com"/>
// <version>$Revision: $</version>
// </file>
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
{
/// <summary>
/// The snippet element inserted inside the body curly braces.
/// When snippet interactive mode ends, resolves type of switch condition and generates switch cases.
/// </summary>
public class SwitchBodySnippetElement : SnippetElement//, IActiveElement would add this.Segment to know where to insert
{
InsertionContext context;
/// <summary>
/// The ITextEditor in which this switch body element has been inserted.
/// </summary>
public ITextEditor Editor {
get { return context.TextArea.GetService(typeof(ITextEditor)) as ITextEditor; }
}
/// <summary>
/// Called when this switch body element is inserted to the editor.
/// </summary>
public override void Insert(InsertionContext context)
{
this.context = context;
this.context.Deactivated += new EventHandler<SnippetEventArgs>(InteractiveModeCompleted);
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// Inserts switch body for enum to the given offset.
/// </summary>
/// <param name="indent">Whole text will be indented by this value.</param>
void GenerateEnumSwitchBodyCode(int offset, IReturnType enumType, string indent)
{
string switchBody = GetSwitchBodyCode(enumType, indent, GetCodeGeneratorForCurrentFile());
this.Editor.Document.Insert(offset, switchBody);
}
/// <summary>
/// Inserts generic switch body to the given offset.
/// </summary>
/// <param name="indent">Whole text will be indented by this value.</param>
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";
}
/// <summary>
/// Gets enum values out of enum type.
/// </summary>
IEnumerable<DefaultField> 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;
}
}
/// <summary>
/// Assuming that interactive mode of 'switch' snippet has currently finished in context,
/// returns the switch condition that user entered.
/// </summary>
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;
}
/// <summary>
/// Resolves the Dom.IReturnType of expression ending at offset, ie. the switch condition expression.
/// </summary>
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;*/
}
/// <summary>
/// Returns CodeGenerator for C# or VB depending on the current file where snippet is being inserted.
/// </summary>
CodeGenerator GetCodeGeneratorForCurrentFile()
{
ParseInformation parseInfo = ParserService.GetParseInformation(this.Editor.FileName);
if (parseInfo != null) {
return parseInfo.CompilationUnit.Language.CodeGenerator;
}
return null;
}
/// <summary>
/// Get start of the next line (that is, from offset, go one line down and to its start).
/// </summary>
int GetNextLineStart(IDocument document, int offset)
{
int nextLineNuber = document.GetLineForOffset(offset).LineNumber + 1;
return document.GetLine(nextLineNuber).Offset;
}
}
}

30
src/AddIns/Misc/SharpRefactoring/Project/Src/SwitchSnippetProvider.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Konicek" email="martin.konicek@gmail.com"/>
// <version>$Revision: $</version>
// </file>
using System;
using ICSharpCode.AvalonEdit.Snippets;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
namespace SharpRefactoring
{
/// <summary>
/// Description of SwitchSnippetProvider.
/// </summary>
public class SwitchSnippetProvider : ISnippetElementProvider
{
public SwitchSnippetProvider()
{
}
public SnippetElement GetElement(string tag)
{
if (tag.Equals("refactoring:switchbody", StringComparison.InvariantCultureIgnoreCase))
return new SwitchBodySnippetElement();
return null;
}
}
}
Loading…
Cancel
Save