Don't like ads? PRO users don't see any ads ;-)
Guest

[devtalk.net] R# SDK Adventures Part 2

By: a guest on May 10th, 2012  |  syntax: C#  |  size: 7.99 KB  |  hits: 47  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using ActiveMesa.R2P.Settings;
  6. using JetBrains.Application;
  7. using JetBrains.Application.DataContext;
  8. using JetBrains.Application.Progress;
  9. using JetBrains.Application.Settings;
  10. using JetBrains.DataFlow;
  11. using JetBrains.DocumentModel;
  12. using JetBrains.ProjectModel;
  13. using JetBrains.ReSharper.Daemon;
  14. using JetBrains.ReSharper.Daemon.Stages;
  15. using JetBrains.ReSharper.Daemon.Stages.Dispatcher;
  16. using JetBrains.ReSharper.Feature.Services.Bulbs;
  17. using JetBrains.ReSharper.Feature.Services.CodeCleanup;
  18. using JetBrains.ReSharper.Psi;
  19. using JetBrains.ReSharper.Psi.CSharp;
  20. using JetBrains.ReSharper.Psi.CSharp.Tree;
  21. using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
  22. using JetBrains.ReSharper.Psi.Tree;
  23. using JetBrains.TextControl;
  24. using JetBrains.Util;
  25.  
  26. namespace Stuff
  27. {
  28.   [ElementProblemAnalyzer(new[] { typeof(IInvocationExpression) },
  29.     HighlightingTypes = new[] { typeof(IntPowerHighlighting) })]
  30.   public class IntPowerProblemAnalyzer : ElementProblemAnalyzer<IInvocationExpression>
  31.   {
  32.     public static bool InvocationExpressionIsMathPowCall(IInvocationExpression element, out int power)
  33.     {
  34.       bool isOnMathPow = false;
  35.       var r = element.InvocationExpressionReference.Resolve();
  36.       var m = r.DeclaredElement as IMethod;
  37.       if (m != null)
  38.       {
  39.         var parent = m.GetContainingType();
  40.         if (parent != null)
  41.         {
  42.           isOnMathPow = parent.GetClrName().FullName.Equals("System.Math")
  43.                         && m.ShortName.Equals("Pow");
  44.         }
  45.       }
  46.  
  47.       bool firstArgIsIdentifier = false;
  48.       bool secondArgIsInteger = false;
  49.       power = -1;
  50.       if (element.Arguments.Count == 2)
  51.       {
  52.         firstArgIsIdentifier = element.Arguments[0].Value is IReferenceExpression;
  53.         var secondArg = element.Arguments[1].Value as ICSharpLiteralExpression;
  54.         if (secondArg != null && (secondArg.IsConstantValue()))
  55.         {
  56.           double value = -1.0;
  57.           var cv = secondArg.ConstantValue;
  58.           if (cv.IsDouble())
  59.             value = (double)cv.Value;
  60.           else if (cv.IsInteger())
  61.             value = (int)cv.Value;
  62.           power = (int)value;
  63.           secondArgIsInteger = (value > 0.0) && value == Math.Floor(value);
  64.         }
  65.       }
  66.  
  67.       return isOnMathPow && firstArgIsIdentifier && secondArgIsInteger;
  68.     }
  69.  
  70.     protected override void Run(IInvocationExpression element,
  71.       ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
  72.     {
  73.       int power;
  74.       if (InvocationExpressionIsMathPowCall(element, out power))
  75.         consumer.AddHighlighting(new IntPowerHighlighting(element, power),
  76.           element.GetDocumentRange(), element.GetContainingFile());
  77.     }
  78.   }
  79.  
  80.   [StaticSeverityHighlighting(Severity.WARNING, CSharpLanguage.Name)]
  81.   public class IntPowerHighlighting : IHighlighting
  82.   {
  83.     public IInvocationExpression Expression { get; private set; }
  84.     public int Power { get; private set; }
  85.  
  86.     public IntPowerHighlighting(IInvocationExpression expression, int power)
  87.     {
  88.       Expression = expression;
  89.       Power = power;
  90.     }
  91.  
  92.     public bool IsValid()
  93.     {
  94.       return Expression != null && Expression.IsValid();
  95.     }
  96.  
  97.     public string ToolTip { get { return "Inefficient use of integer-based power"; } }
  98.     public string ErrorStripeToolTip { get { return ToolTip; } }
  99.     public int NavigationOffsetPatch { get { return 0; } }
  100.   }
  101.  
  102.   [QuickFix]
  103.   public class IntPowerInliningFix : BulbItemImpl, IQuickFix
  104.   {
  105.     private readonly IntPowerHighlighting highlighting;
  106.     private Pair<bool, string> customNameSettings;
  107.  
  108.     public IntPowerInliningFix(IntPowerHighlighting highlighting)
  109.     {
  110.       this.highlighting = highlighting;
  111.       customNameSettings = GetCustomFunctionSettings(highlighting.Expression.ToDataContext());
  112.     }
  113.  
  114.     public static void PerformChange(IInvocationExpression expr, Pair<bool,string> customNameSettings, int power)
  115.     {
  116.       var arg = expr.Arguments[0];
  117.       var factory = CSharpElementFactory.GetInstance(expr.GetPsiModule());
  118.  
  119.       ICSharpExpression replacement;
  120.       if (customNameSettings.First && power > 3)
  121.       {
  122.         var template = "$0($1, " + power + ")";
  123.         replacement = factory.CreateExpression(template,
  124.           customNameSettings.Second, expr.Arguments[0]);
  125.       }
  126.       else
  127.       {
  128.         replacement = factory.CreateExpression(
  129.           Enumerable.Range(0, power).Select(i => "$0").Join("*"), arg.Value);
  130.       }
  131.       ModificationUtil.ReplaceChild(expr, replacement);
  132.     }
  133.  
  134.     protected override Action<ITextControl> ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
  135.     {
  136.       PerformChange(highlighting.Expression, customNameSettings, highlighting.Power);
  137.       return null;
  138.     }
  139.  
  140.     public static Pair<bool, string> GetCustomFunctionSettings(Func<Lifetime, DataContexts, IDataContext> ctx)
  141.     {
  142.       var ss = Shell.Instance.GetComponent<ISettingsStore>();
  143.       var bs = ss.BindToContextTransient(ContextRange.Smart(ctx));
  144.       var s = bs.GetKey<GeneralSettings>(SettingsOptimization.DoMeSlowly);
  145.       return new Pair<bool, string>(s.UseCustomPowerFunction, s.CustomPowerFunctionName);
  146.     }
  147.  
  148.     public override string Text
  149.     {
  150.       get
  151.       {
  152.         return customNameSettings.First
  153.           ? "Use '{0}' instead".ƒ(customNameSettings.Second)
  154.           : "Inline integer power";
  155.       }
  156.     }
  157.  
  158.     public bool IsAvailable(IUserDataHolder cache)
  159.     {
  160.       return highlighting.Power >= 2;
  161.     }
  162.   }
  163.  
  164.   [CodeCleanupModule]
  165.   public class IntPowerInliningCleanup : ICodeCleanupModule
  166.   {
  167.     private static readonly Descriptor descriptor = new Descriptor();
  168.     private IShellLocks shellLocks;
  169.  
  170.     public IntPowerInliningCleanup(IShellLocks shellLocks)
  171.     {
  172.       this.shellLocks = shellLocks;
  173.     }
  174.  
  175.     public void SetDefaultSetting(CodeCleanupProfile profile, CodeCleanup.DefaultProfileType profileType)
  176.     {
  177.       switch (profileType)
  178.       {
  179.         case CodeCleanup.DefaultProfileType.FULL:
  180.           profile.SetSetting(descriptor, true);
  181.           break;
  182.         default:
  183.           profile.SetSetting(descriptor, false);
  184.           break;
  185.       }
  186.     }
  187.  
  188.     public bool IsAvailable(IPsiSourceFile sourceFile)
  189.     {
  190.       return sourceFile.GetPsiFile<CSharpLanguage>() != null;
  191.     }
  192.  
  193.     public void Process(IPsiSourceFile sourceFile, IRangeMarker rangeMarkerMarker,
  194.       CodeCleanupProfile profile, IProgressIndicator progressIndicator)
  195.     {
  196.       var file = sourceFile.GetPsiFile<CSharpLanguage>();
  197.       if (file == null)
  198.         return;
  199.  
  200.       if (!profile.GetSetting(descriptor))
  201.         return;
  202.  
  203.       var settings = IntPowerInliningFix.GetCustomFunctionSettings(sourceFile.ToDataContext());
  204.       file.GetPsiServices().PsiManager.DoTransaction(() =>
  205.       {
  206.         using (shellLocks.UsingWriteLock())
  207.         {
  208.           var itemsToChange = new List<Pair<IInvocationExpression,int>>();
  209.           file.ProcessChildren<IInvocationExpression>(e =>
  210.           {
  211.             int power;
  212.             if (IntPowerProblemAnalyzer.InvocationExpressionIsMathPowCall(e, out power))
  213.               itemsToChange.Add(new Pair<IInvocationExpression, int>(e, power));
  214.           });
  215.           foreach (var e in itemsToChange)
  216.             IntPowerInliningFix.PerformChange(e.First, settings, e.Second);
  217.         }
  218.       }, "Code cleanup");
  219.     }
  220.  
  221.     public PsiLanguageType LanguageType
  222.     {
  223.       get { return CSharpLanguage.Instance; }
  224.     }
  225.  
  226.     public ICollection<CodeCleanupOptionDescriptor> Descriptors
  227.     {
  228.       get { return new[] {descriptor}; }
  229.     }
  230.  
  231.     public bool IsAvailableOnSelection
  232.     {
  233.       get { return false; }
  234.     }
  235.  
  236.     [DefaultValue(false)]
  237.     [DisplayName("Replace Math.Pow() integer calls")]
  238.     [Category(CSharpCategory)]
  239.     private class Descriptor : CodeCleanupBoolOptionDescriptor
  240.     {
  241.       public Descriptor() : base("ReplaceMathPowIntegerCalls") {}
  242.     }
  243.   }
  244. }