public
Created

Readonly interface generator

  • Download Gist
ReadOnlyInterfaceGen.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
using System.Collections.Generic;
using System.Linq;
using JetBrains.ActionManagement;
using JetBrains.Annotations;
using JetBrains.Application.DataContext;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.CSharp.Generate;
using JetBrains.ReSharper.Feature.Services.Generate;
using JetBrains.ReSharper.Feature.Services.Generate.Actions;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.CSharp.Util;
using JetBrains.UI.Icons;
using JetBrains.UI.RichText;
using JetBrains.Util;
using DataConstants = JetBrains.ProjectModel.DataContext.DataConstants;
 
namespace ActiveMesa.R2P.Generators
{
[ActionHandler("Generate.ReadOnlyInterface")]
public class ReadOnlyInterfaceAction : GenerateActionBase<ReadOnlyInterfaceItemProvider>
{
protected override bool ShowMenuWithOneItem
{
get { return true; }
}
 
protected override RichText Caption
{
get { return "Generate Read-Only Interface"; }
}
}
 
[GenerateProvider]
public class ReadOnlyInterfaceItemProvider : IGenerateActionProvider
{
public IEnumerable<IGenerateActionWorkflow> CreateWorkflow(IDataContext dataContext)
{
var solution = dataContext.GetData(DataConstants.SOLUTION);
var iconManager = solution.GetComponent<PsiIconManager>();
var icon = iconManager.GetImage(CLRDeclaredElementType.INTERFACE);
yield return new ReadOnlyInterfaceActionWorkflow(icon);
}
}
 
public class ReadOnlyInterfaceActionWorkflow : StandardGenerateActionWorkflow
{
public ReadOnlyInterfaceActionWorkflow(IconId icon)
: base("ReadOnlyInterface", icon, "Read-only interface", GenerateActionGroup.CLR_LANGUAGE, "Generate read-only interface",
"Generates a read-only interface for this class.", "Generate.ReadOnlyInterface")
{
}
 
public override double Order
{
get { return 1; }
}
 
/// <summary>
/// This method is redefined in order to get rid of the IsKindAllowed() check at the end.
/// </summary>
public override bool IsAvailable(IDataContext dataContext)
{
var solution = dataContext.GetData(DataConstants.SOLUTION);
if (solution == null)
return false;
 
var generatorManager = GeneratorManager.GetInstance(solution);
if (generatorManager == null)
return false;
 
var languageType = generatorManager.GetPsiLanguageFromContext(dataContext);
if (languageType == null)
return false;
 
var generatorContextFactory = LanguageManager.Instance.TryGetService<IGeneratorContextFactory>(languageType);
return generatorContextFactory != null;
}
}
 
[GeneratorBuilder("ReadOnlyInterface", typeof(CSharpLanguage))]
public class ReadOnlyInterfaceBuilder : GeneratorBuilderBase<CSharpGeneratorContext>
{
protected override bool IsAvaliable(CSharpGeneratorContext context)
{
return base.IsAvaliable(context);
// fuckup: R# thinks all classes are static
 
// class cannot be static or abstract
//return context.ClassDeclaration != null &&
// !context.ClassDeclaration.IsAbstract &&
// !context.ClassDeclaration.IsStatic;
}
 
protected override void Process(CSharpGeneratorContext context)
{
var factory = CSharpElementFactory.GetInstance(context.PsiModule);
ReadOnlyIntefaceBuilderWorkflow.Start(context, factory);
}
 
public override double Priority
{
get { return 0; }
}
 
private static class ReadOnlyIntefaceBuilderWorkflow
{
public static WorkflowResult Start([CanBeNull] CSharpGeneratorContext context, CSharpElementFactory factory)
{
if (context.ClassDeclaration == null) return WorkflowResult.Inapplicable;
return CreateNewInterfaceDeclaration(context, factory);
}
 
private static WorkflowResult CreateNewInterfaceDeclaration(CSharpGeneratorContext context, CSharpElementFactory factory)
{
string interfaceName = "IReadOnly" + context.ClassDeclaration.DeclaredName;
var itfDecl = (IInterfaceDeclaration)factory.CreateTypeMemberDeclaration("interface " + interfaceName + " {}");
return AddInterfaceToContainingNamespace(context, itfDecl, factory);
}
 
private static WorkflowResult AddInterfaceToContainingNamespace(CSharpGeneratorContext context, IInterfaceDeclaration itfDecl, CSharpElementFactory factory)
{
var ns = context.ClassDeclaration.GetContainingNamespaceDeclaration();
if (ns == null) return WorkflowResult.Inapplicable;
else
{
var typeDecl = ns.AddTypeDeclarationBefore(itfDecl, context.ClassDeclaration);
return PopulateInterfaceDeclaration(context, factory, (IInterfaceDeclaration)typeDecl);
}
}
 
private static WorkflowResult PopulateInterfaceDeclaration(CSharpGeneratorContext context, CSharpElementFactory factory, IInterfaceDeclaration itfDecl)
{
var props = context.InputElements.OfType<GeneratorDeclaredElement<ITypeOwner>>().ToList();
foreach (var prop in props)
{
var propDecl = (IClassMemberDeclaration)factory.CreateTypeMemberDeclaration("public $0 $1 { get; }", prop.DeclaredElement.Type, prop.DeclaredElement.ShortName);
itfDecl.AddClassMemberDeclaration(propDecl);
}
return EnsureClassImplementsInterface(context, itfDecl);
}
 
private static WorkflowResult EnsureClassImplementsInterface(CSharpGeneratorContext context, IInterfaceDeclaration itfDecl)
{
var interfaceType = TypeFactory.CreateType(itfDecl.DeclaredElement);
context.ClassDeclaration.AddSuperInterface(interfaceType, false);
return WorkflowResult.Success;
}
}
}
 
[GeneratorElementProvider("ReadOnlyInterface", typeof(CSharpLanguage))]
internal class ReadOnlyInterfacePropertyProvider : GeneratorProviderBase<CSharpGeneratorContext>
{
/// <summary>
/// If we have several providers for the same generate kind, this property will set order on them
/// </summary>
public override double Priority
{
get { return 0; }
}
 
/// <summary>
/// Populate context with input elements
/// </summary>
/// <param name="context"></param>
public override void Populate(CSharpGeneratorContext context)
{
var typeElement = context.ClassDeclaration.DeclaredElement;
if (typeElement == null)
return;
 
if (!(typeElement is IStruct) && !(typeElement is IClass))
return;
// provide only readable properies
var stuff =
context.ClassDeclaration.PropertyDeclarations.Select(
member => new { member, memberType = member.Type as IDeclaredType }).Where(
t => !t.member.IsStatic &&
!t.member.IsAbstract &&
t.member.DeclaredElement.IsReadable && // must be readable
!t.member.IsSynthetic() &&
t.memberType != null &&
t.memberType.CanUseExplicitly(context.ClassDeclaration))
.Select(u => new GeneratorDeclaredElement<ITypeOwner>(u.member.DeclaredElement));
context.ProvidedElements.AddRange(stuff);
}
}
 
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.