ReSharper.QuickFix . I am writing additional options for this menu. Why? Because it sometimes saves time. Let's take a look at how to write context action for Resharper.Plugins folder in the resharper bin folder. For debugging, there is no need to copy them there - you can simply specify the name and the path to the plugin as the arguments of the studio itself ( devenv.exe ). The syntax is something like this:devenv.exe /ReSharper.Plugin c: \ path \ to \ your.dll
ReSharper appears, because I have no idea what might be needed.CSharpContextActionBase (in the case of C #), as well as implementing several other interfaces. Fortunately, part of the "plumbing" implemented the developers of other plug-ins. For context ContextActionBase , I add the ContextActionBase class to my project, which was written by the authors of the Agent Johnson plugin. Actually the file itself can be found here .ContextActionBaseGetText() method. This method returns in the string what will be written for your command in the CA drop-down menu.IsAvailable(IElement) method. Determines whether your CA is applicable at a given code point or not. IElement is your link to the code point where the cursor is. From this point of the code, you can bypass even the entire tree of the file.Execute(IElement) method. If the user has clicked on your SA, then you can apply it. We again have a link to IElement , i.e. we can walk along the code and choose what to change and where.
Let's take a simple example. Imagine that you need to implement a functional that quickly Math.Pow() calls with integer values. This is necessary becauseMath.Pow(x, 2.0) ← it is bad and slowx*x ← much fastertext field has been added so that we can directly prompt the user in the CA menu what will happen to his code after refactoring.
[ContextAction(Group = "C#" , Name = "Inline a power function" ,
Description = "Inlines a power statement; eg, changes Math.Pow(x, 3) to x*x*x." ,
Priority = 15)]
internal class InlinePowerAction : ContextActionBase
{
private string text;
public InlinePowerAction(ICSharpContextActionDataProvider provider) : base (provider)
{
//
}
⋮
}
Math.Pow() and this body has an integer degree - for example, 3.0. How to do it? First we find the place where the user has the cursor. Then, we get those nodes of the syntax tree, which stand in the same place as the cursor, and try to bring them to the expected types. Since Math.Pow() is a function call, we expect to see a IInvocationExpression with IInvocationExpression in its body. And so on, in a chain, and we always use the as operator in case that the expression is not what we expect.true . In all other cases, return false .
protected override bool IsAvailable(JetBrains.ReSharper.Psi.Tree.IElement element)
{
using (ReadLockCookie.Create())
{
IInvocationExpression invEx = GetSelectedElement<IInvocationExpression>( false );
if (invEx != null && invEx.InvokedExpression.GetText() == "Math.Pow" )
{
IArgumentListNode node = invEx.ToTreeNode().ArgumentList;
if (node != null && node.Arguments.Count == 2)
{
ILiteralExpression value = node.Arguments[1].Value as ILiteralExpression;
if ( value != null )
{
float n;
if ( float .TryParse( value .GetText().Replace( "f" , string .Empty), out n) &&
(n - Math.Floor(n) == 0 && n >= 1 && n <= 10))
{
text = "Replace with " + (n-1) + " multiplications" ;
return true ;
}
}
}
}
}
return false ;
}
text variable before returning true ? This is to make the CA better read by the user. Yes, and as for ReadLockCookie in which the code is wrapped - this is an element of Resharper's internal semantics. I have no idea what he is doing - just copying him from the examples so, just in case. After all, there is no detailed, updated documentation on writing plugins for Resharper.true from IsAvailable() , Resharper will want to know which text to draw in the menu. In this case, we already know what to return - the content of the text variable.
protected override string GetText()
{
return text;
}
Execute() method is called. And here our replacement algorithm is just starting to work. Remember - we want to change, say, Math.Pow(x, 3.0) to x*x*x . How to do it?Math.Pow() . We pull out both parameters (in the example above, x and 3), gently converting the values even if it is written, for example, not 3.0 but 3.0f. Next, we determine how long the expression is to the left - because if we raise to the power of x , then we can write x*x*x , but if x+y we will have to write with brackets (x+y)*(x+y)*(x+y) To do this, we interrupt the type, and if it is ILiteralExpression or IReferenceExpression then hooray is the expression “short”.StringBuilder to make a string for replacement. But then interesting things happen.ICSharpExpression , which allows us to create a node from our string that can replace the Math.Pow node. The following expression does just that - with LowLevelModificationUtil we replace one node with another.
protected override void Execute(JetBrains.ReSharper.Psi.Tree.IElement element)
{
IInvocationExpression expression = GetSelectedElement<IInvocationExpression>( false );
if (expression != null )
{
IInvocationExpressionNode node = expression.ToTreeNode();
if (node != null )
{
IArgumentListNode args = node.ArgumentList;
int count = ( int ) double .Parse(args.Arguments[1].Value.GetText().Replace( "f" , string .Empty));
bool isShort = node.Arguments[0].Value is ILiteralExpression ||
node.Arguments[0].Value is IReferenceExpression;
var sb = new StringBuilder();
sb.Append( "(" );
for ( int i = 0; i < count; ++i)
{
if (!isShort) sb.Append( "(" );
sb.Append(args.Arguments[0].GetText());
if (!isShort) sb.Append( ")" );
if (i + 1 != count)
sb.Append( "*" );
}
sb.Append( ")" );
// now replace everything
ICSharpExpression newExp = Provider.ElementFactory.CreateExpression(
sb.ToString(), new object [] { });
if (newExp != null )
{
LowLevelModificationUtil.ReplaceChildRange(
expression.ToTreeNode(),
expression.ToTreeNode(),
new [] { newExp.ToTreeNode() });
}
}
}
}
ContextActionBase is here . This example was tested on version 4.5 of Resharper, I don’t know anything about version 5 :)null checks and so on. Good luck! ■ Source: https://habr.com/ru/post/74026/
All Articles