diff --git a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin
index 68907a0a71..75e3e4f3b4 100644
--- a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin
+++ b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin
@@ -40,11 +40,13 @@
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
index 807c8e6638..5e5162181f 100644
--- a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
+++ b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
@@ -71,6 +71,9 @@
+
+
+
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentAction.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentAction.cs
new file mode 100644
index 0000000000..14d973f207
--- /dev/null
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentAction.cs
@@ -0,0 +1,107 @@
+//
+//
+//
+//
+// $Revision: $
+//
+using System;
+using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Dom;
+using ICSharpCode.SharpDevelop.Dom.Refactoring;
+using ICSharpCode.SharpDevelop.Refactoring;
+
+namespace SharpRefactoring.ContextActions
+{
+ ///
+ /// Description of CheckAssignmentAction.
+ ///
+ public abstract class CheckAssignmentAction : ContextAction
+ {
+ protected string VariableName { get; private set; }
+
+ protected CodeGenerator CodeGenerator { get; private set; }
+
+ protected DomRegion ElementRegion { get; private set; }
+
+ protected string GetVariableName(EditorContext context)
+ {
+ // a = Foo() : AssignmentExpression.Left == IdentifierExpression(*identifier*)
+ // var a = Foo() : VariableDeclaration(*name*).Initializer != empty
+
+ var variableName = GetVariableNameFromAssignment(context.GetContainingElement());
+ if (variableName != null)
+ return variableName;
+ variableName = GetVariableNameFromVariableDeclaration(context.GetContainingElement());
+ if (variableName != null)
+ return variableName;
+
+ return null;
+ }
+
+ protected DomRegion GetStatementRegion(EditorContext context)
+ {
+ // a = Foo() : AssignmentExpression.Left == IdentifierExpression(*identifier*)
+ // var a = Foo() : VariableDeclaration(*name*).Initializer != empty
+
+ var assignment = context.GetContainingElement();
+ if (assignment != null)
+ return DomRegion.FromLocation(assignment.StartLocation, assignment.EndLocation);
+ var declaration = context.GetContainingElement();
+ if (declaration != null)
+ return DomRegion.FromLocation(declaration.StartLocation, declaration.EndLocation);
+
+ return DomRegion.Empty;
+ }
+
+ string GetVariableNameFromAssignment(AssignmentExpression assignment)
+ {
+ if (assignment == null)
+ return null;
+ var identifier = assignment.Left as IdentifierExpression;
+ if (identifier == null)
+ return null;
+ if (assignment.Right is ObjectCreateExpression)
+ // // don't offer action for "a = new Foo()"
+ return null;
+ return identifier.Identifier;
+ }
+
+ string GetVariableNameFromVariableDeclaration(LocalVariableDeclaration declaration)
+ {
+ if (declaration == null)
+ return null;
+ if (declaration.Variables.Count != 1)
+ return null;
+ VariableDeclaration varDecl = declaration.Variables[0];
+ if (!varDecl.Initializer.IsNull &&
+ // don't offer action for "var a = new Foo()"
+ !(varDecl.Initializer is ObjectCreateExpression))
+ return varDecl.Name;
+ return null;
+ }
+
+ CodeGenerator GetCodeGenerator(EditorContext context)
+ {
+ var parseInfo = ParserService.GetParseInformation(context.Editor.FileName);
+ if (parseInfo == null)
+ return null;
+ return parseInfo.CompilationUnit.Language.CodeGenerator;
+ }
+
+ public IReturnType GetResolvedType(ResolveResult symbol)
+ {
+ if (symbol != null)
+ return symbol.ResolvedType;
+ return null;
+ }
+
+ public override bool IsEnabled(EditorContext context)
+ {
+ this.VariableName = GetVariableName(context);
+ this.CodeGenerator = GetCodeGenerator(context);
+ this.ElementRegion = GetStatementRegion(context);
+ return !string.IsNullOrEmpty(this.VariableName) && (this.CodeGenerator != null);
+ }
+ }
+}
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentNotNull.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentNotNull.cs
new file mode 100644
index 0000000000..74be619e10
--- /dev/null
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentNotNull.cs
@@ -0,0 +1,50 @@
+//
+//
+//
+//
+// $Revision: $
+//
+using System;
+using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Refactoring;
+
+namespace SharpRefactoring.ContextActions
+{
+ ///
+ /// Description of CheckAssignmentNotNull.
+ ///
+ public class CheckAssignmentNotNull : CheckAssignmentAction
+ {
+ public override string Title {
+ get { return "Check for not null"; }
+ }
+
+ string caretMarker = "<<>>";
+
+ public override void Execute(EditorContext context)
+ {
+ var ifStatement = GenerateAstToInsert(this.VariableName);
+
+ var editor = context.Editor;
+ string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, editor.Document.GetLineStartOffset(this.ElementRegion.GetStart()));
+ string code = this.CodeGenerator.GenerateCode(ifStatement, indent);
+ int insertOffset = editor.Document.GetLineEndOffset(this.ElementRegion.GetEnd());
+ using (var undoGroup = editor.Document.OpenUndoGroup()) {
+ editor.Document.Insert(insertOffset, code);
+ var caretPos = editor.Document.Text.IndexOf(caretMarker, insertOffset);
+ editor.Caret.Offset = caretPos;
+ editor.Document.RemoveRestOfLine(caretPos);
+ }
+ }
+
+ AbstractNode GenerateAstToInsert(string variableName)
+ {
+ var block = new BlockStatement();
+ block.AddChild(new ExpressionStatement(new IdentifierExpression(caretMarker)));
+ return new IfElseStatement(
+ new BinaryOperatorExpression(new IdentifierExpression(variableName), BinaryOperatorType.InEquality, new PrimitiveExpression(null)),
+ block);
+ }
+ }
+}
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentNull.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentNull.cs
new file mode 100644
index 0000000000..a50c1aedd0
--- /dev/null
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/CheckAssignmentNull.cs
@@ -0,0 +1,43 @@
+//
+//
+//
+//
+// $Revision: $
+//
+using System;
+using System.Collections.Generic;
+using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Refactoring;
+
+namespace SharpRefactoring.ContextActions
+{
+ ///
+ /// Offers inserting "if (a == null) return;" after "var a = *expr*"
+ ///
+ public class CheckAssignmentNull : CheckAssignmentAction
+ {
+ public override string Title {
+ get { return "Check for null"; }
+ }
+
+ public override void Execute(EditorContext context)
+ {
+ var ifStatement = GenerateAstToInsert(this.VariableName);
+
+ var editor = context.Editor;
+ string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, editor.Document.GetLineStartOffset(this.ElementRegion.GetStart()));
+ string code = this.CodeGenerator.GenerateCode(ifStatement, indent);
+ int insertOffset = editor.Document.GetLineEndOffset(this.ElementRegion.GetEnd());
+ editor.Document.Insert(insertOffset, code);
+ editor.Caret.Offset = insertOffset + code.Length - 1;
+ }
+
+ AbstractNode GenerateAstToInsert(string variableName)
+ {
+ return new IfElseStatement(
+ new BinaryOperatorExpression(new IdentifierExpression(variableName), BinaryOperatorType.Equality, new PrimitiveExpression(null)),
+ new ReturnStatement(Expression.Null));
+ }
+ }
+}
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/Extensions.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/Extensions.cs
index ef69a01a07..f7e9a026de 100644
--- a/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/Extensions.cs
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/Extensions.cs
@@ -7,10 +7,13 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+
+using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.Refactoring;
+using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Refactoring;
namespace SharpRefactoring.ContextActions
@@ -47,5 +50,41 @@ namespace SharpRefactoring.ContextActions
}
}
}
+
+ public static Location GetStart(this DomRegion region)
+ {
+ return new Location(region.BeginColumn, region.BeginLine);
+ }
+
+ public static Location GetEnd(this DomRegion region)
+ {
+ return new Location(region.EndColumn, region.EndLine);
+ }
+
+ public static int PositionToOffset(this IDocument document, Location location)
+ {
+ return document.PositionToOffset(location.Line, location.Column);
+ }
+
+ ///
+ /// Gets offset for the start of line at which given location is.
+ ///
+ public static int GetLineStartOffset(this IDocument document, Location location)
+ {
+ return document.GetLineForOffset(document.PositionToOffset(location)).Offset;
+ }
+
+ public static int GetLineEndOffset(this IDocument document, Location location)
+ {
+ var line = document.GetLineForOffset(document.PositionToOffset(location));
+ return line.Offset + line.TotalLength;
+ }
+
+ public static void RemoveRestOfLine(this IDocument document, int offset)
+ {
+ var line = document.GetLineForOffset(offset);
+ int lineEndOffset = line.Offset + line.Length;
+ document.Remove(offset, lineEndOffset - offset);
+ }
}
}
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/GenerateMember.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/GenerateMember.cs
index 4a5a594c48..4377b907af 100644
--- a/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/GenerateMember.cs
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/GenerateMember.cs
@@ -18,13 +18,13 @@ namespace SharpRefactoring.ContextActions
///
public class GenerateMemberProvider : IContextActionsProvider
{
- public IEnumerable GetAvailableActions(EditorContext editorContext)
+ public IEnumerable GetAvailableActions(EditorContext context)
{
- if (string.IsNullOrEmpty(editorContext.CurrentExpression.Expression)) {
+ if (string.IsNullOrEmpty(context.CurrentExpression.Expression)) {
yield break;
}
- if (editorContext.CurrentExpression.Region != null &&
- editorContext.CurrentExpression.Region.EndLine > editorContext.CurrentExpression.Region.BeginLine) {
+ if (context.CurrentExpression.Region != null &&
+ context.CurrentExpression.Region.EndLine > context.CurrentExpression.Region.BeginLine) {
// do not yield the action for 2-line expressions like this, which are actually 2 different expressions
// variable.(*caret*)
// CallFooMethod();
@@ -33,7 +33,7 @@ namespace SharpRefactoring.ContextActions
// 123);
yield break;
}
- var generateCodeAction = GenerateCode.GetContextAction(editorContext.CurrentSymbol, editorContext);
+ var generateCodeAction = GenerateCode.GetContextAction(context);
if (generateCodeAction != null)
yield return generateCodeAction;
}
diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/GenerateCode.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/GenerateCode.cs
index 964bbf2954..5f49d27125 100644
--- a/src/AddIns/Misc/SharpRefactoring/Project/Src/GenerateCode.cs
+++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/GenerateCode.cs
@@ -30,10 +30,10 @@ namespace SharpRefactoring
///
/// If given symbol is Unknown ResolveResult, returns action that can generate code for this missing symbol.
///
- public static GenerateCodeContextAction GetContextAction(ResolveResult symbol, EditorContext context)
+ public static GenerateCodeContextAction GetContextAction(EditorContext context)
{
- if (symbol is UnknownMethodResolveResult) {
- UnknownMethodResolveResult unknownMethodCall = (UnknownMethodResolveResult)symbol;
+ if (context.CurrentSymbol is UnknownMethodResolveResult) {
+ UnknownMethodResolveResult unknownMethodCall = (UnknownMethodResolveResult)context.CurrentSymbol;
Ast.Expression expression = context.GetContainingElement();
if (expression == null)
return null;
diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
index 32fcf36896..78f4855a9b 100644
--- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
+++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
@@ -331,6 +331,7 @@
+
ContextActionsBulbControl.xaml
Code
diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextAction.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextAction.cs
new file mode 100644
index 0000000000..760aaa07a3
--- /dev/null
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextAction.cs
@@ -0,0 +1,40 @@
+//
+//
+//
+//
+// $Revision: $
+//
+using System;
+using System.Collections.Generic;
+
+namespace ICSharpCode.SharpDevelop.Refactoring
+{
+ ///
+ /// Base class for implementing custom context actions.
+ ///
+ public abstract class ContextAction : IContextActionsProvider, IContextAction
+ {
+ public abstract string Title { get; }
+
+ public abstract bool IsEnabled(EditorContext context);
+
+ public abstract void Execute(EditorContext context);
+
+ public IEnumerable GetAvailableActions(EditorContext context)
+ {
+ this.context = context;
+ if (this.IsEnabled(context))
+ yield return this;
+ }
+
+ EditorContext context;
+ public void Execute()
+ {
+ Execute(this.context);
+ }
+
+ public virtual string Id {
+ get { return this.GetType().FullName; }
+ }
+ }
+}
diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsService.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsService.cs
index e04b6c0b70..03e29b68c9 100644
--- a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsService.cs
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsService.cs
@@ -6,8 +6,8 @@
//
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
-
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Refactoring;
@@ -31,7 +31,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
private ContextActionsService()
{
- this.providers = AddInTree.BuildItems("/SharpDevelop/ViewContent/AvalonEdit/ContextActionProviders", null, false);
+ this.providers = AddInTree.BuildItems("/SharpDevelop/ViewContent/AvalonEdit/ContextActions", null, false);
}
///
@@ -43,13 +43,19 @@ namespace ICSharpCode.SharpDevelop.Refactoring
yield break;
var parseTask = ParserService.BeginParseCurrentViewContent();
parseTask.Wait();
+
+ var sw = new Stopwatch(); sw.Start();
var editorContext = new EditorContext(editor);
+ long elapsedEditorContextMs = sw.ElapsedMilliseconds;
+
// could run providers in parallel
foreach (var provider in this.providers) {
foreach (var action in provider.GetAvailableActions(editorContext)) {
yield return action;
}
}
+ ICSharpCode.Core.LoggingService.Debug(string.Format("Context actions elapsed {0}ms ({1}ms in EditorContext)",
+ sw.ElapsedMilliseconds, elapsedEditorContextMs));
}
}
}
diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs
index 272779af0a..6583227a12 100644
--- a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs
@@ -66,12 +66,8 @@ namespace ICSharpCode.SharpDevelop.Refactoring
CaretColumn -= 1;
}
- Stopwatch sw = new Stopwatch();
- sw.Start();
-
this.CurrentExpression = GetExpressionAtCaret(editor);
this.CurrentSymbol = ResolveExpression(editor);
- long elapsedResolveMs = sw.ElapsedMilliseconds;
this.CurrentLine = editor.Document.GetLine(CaretLine);
this.CurrentLineAST = GetCurrentLineAst(this.CurrentLine, editor);
@@ -80,21 +76,28 @@ namespace ICSharpCode.SharpDevelop.Refactoring
this.CurrentElement = FindInnermostNodeAtLocation(this.CurrentMemberAST, new Location(CaretColumn, CaretLine));
-// ICSharpCode.Core.LoggingService.Debug(string.Format(
-// @"
-// Context actions (elapsed {5} ms ({6} ms in Resolver)):
-// ExprAtCaret: {0}
-// ----------------------
-// SymbolAtCaret: {1}
-// ----------------------
-// CurrentLineAST: {2}
-// ----------------------
-// AstNodeAtCaret: {3}
-// ----------------------
-// CurrentMemberAST: {4}
-// ----------------------",
-// CurrentExpression, CurrentSymbol, CurrentLineAST, CurrentElement, CurrentMemberAST == null ? "" : CurrentMemberAST.ToString().TakeStartEllipsis(100),
-// sw.ElapsedMilliseconds, elapsedResolveMs));
+ //DebugLog();
+ }
+
+ void DebugLog()
+ {
+ ICSharpCode.Core.LoggingService.Debug(string.Format(
+ @"
+
+ Context actions :
+ ExprAtCaret: {0}
+ ----------------------
+ SymbolAtCaret: {1}
+ ----------------------
+ CurrentLineAST: {2}
+ ----------------------
+ AstNodeAtCaret: {3}
+ ----------------------
+ CurrentMemberAST: {4}
+ ----------------------",
+ CurrentExpression, CurrentSymbol, CurrentLineAST,
+ CurrentElement == null ? "" : CurrentElement.ToString().TakeStartEllipsis(400),
+ CurrentMemberAST == null ? "" : CurrentMemberAST.ToString().TakeStartEllipsis(400)));
}
public TNode GetCurrentElement() where TNode : class, INode
@@ -173,7 +176,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
// class FindOutermostNodeVisitor : NodeTrackingAstVisitor where TNode : class, INode
// {
// public TNode FoundNode { get; private set; }
-//
+//
// protected override void BeginVisit(INode node)
// {
// if (node is TNode && FoundNode == null) {
diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextAction.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextAction.cs
index 721dab7e24..42f53a2fd8 100644
--- a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextAction.cs
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextAction.cs
@@ -15,6 +15,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
public interface IContextAction
{
string Title { get; }
+ //string Id { get; }
void Execute();
}
}
diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextActionsProvider.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextActionsProvider.cs
index 0a3e484fda..0036c55ba7 100644
--- a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextActionsProvider.cs
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/IContextActionsProvider.cs
@@ -19,6 +19,6 @@ namespace ICSharpCode.SharpDevelop.Refactoring
///
/// Gets actions available for current line of the editor.
///
- IEnumerable GetAvailableActions(EditorContext editorAST);
+ IEnumerable GetAvailableActions(EditorContext context);
}
}
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs
index 38b34b7f3a..9a19b5e8b1 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs
@@ -213,6 +213,7 @@ namespace ICSharpCode.SharpDevelop.Dom
public static ResolveResult GetResultFromDeclarationLine(IClass callingClass, IMethodOrProperty callingMember, int caretLine, int caretColumn, ExpressionResult expressionResult)
{
string expression = expressionResult.Expression;
+ if (expression == null) return null;
if (callingClass == null) return null;
int pos = expression.IndexOf('(');
if (pos >= 0) {