Trying to create a Region/Group with a file layout pattern

I am really new to ReSharper, but what I see so far, I really like.  That said I am trying to figure out how to write a specific code cleanup rule. I have classes where I want to Generate code for Equality members (which works great), then I want to group those methods and operators together in a region and stick it at the bottom of the cs file as it needed, but not really part of my business logic.

I created a new rule/pattern (not sure of the correct term) that looks like this...

When I run "Cleanup Code", the only thing that gets put into the region is the overridden Equals method and the GetHashCode method. The implemented strongly typed Equals method and the == and != operators never get stuffed into the region. See sample class below.

What am I doing wrong? 

Sample Class:

Snippet

using System;
 
namespace ReSharperTest
{
    public class ModelA : IEquatable<ModelA>
    {
        public ModelA()
        {
            Counter = 0;
            Name = string.Empty;
        }
 
        public ModelA(int counter, string name)
        {
            Counter = counter;
            Name = name;
        }
 
        public int Counter { get; set; }
        public string Name { get; set; }
 
        public bool Equals(ModelA other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Counter == other.Counter && string.Equals(Name, other.Name);
        }
 
        public static bool operator ==(ModelA left, ModelA right)
        {
            return Equals(left, right);
        }
 
        public static bool operator !=(ModelA left, ModelA right)
        {
            return !Equals(left, right);
        }
 
        #region IEquatable Support
 
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((ModelA) obj);
        }
 
        public override int GetHashCode()
        {
            unchecked
            {
                return (Counter * 397) ^ (Name != null ? Name.GetHashCode() : 0);
            }
        }
 
        #endregion
    }
}

 

0
10 comments

Hello Jim,

Are you able to share your "File Layout" code (XAML link at the right top corner of File Layout page in Options)?

Thanks!  

0

Sure! Mind you, since posting this I have experimented many times trying to get this to work assuming that I am not understanding what I am doing, so what I am posting will not match the screenshot in the OP.

 

<?xml version="1.0" encoding="utf-16"?>
<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
<TypePattern DisplayName="Non-reorderable types">
<TypePattern.Match>
<Or>
<And>
<Kind Is="Interface" />
<Or>
<HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
<HasAttribute Name="System.Runtime.InteropServices.ComImport" />
</Or>
</And>
<Kind Is="Struct" />
<HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" />
<HasAttribute Name="JetBrains.Annotations.NoReorder" />
</Or>
</TypePattern.Match>
</TypePattern>
<TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All">
<TypePattern.Match>
<And>
<Kind Is="Class" />
<HasMember>
<And>
<Kind Is="Method" />
<HasAttribute Name="Xunit.FactAttribute" Inherited="True" />
</And>
</HasMember>
</And>
</TypePattern.Match>
<Entry DisplayName="Setup/Teardown Methods">
<Entry.Match>
<Or>
<Kind Is="Constructor" />
<And>
<Kind Is="Method" />
<ImplementsInterface Name="System.IDisposable" />
</And>
</Or>
</Entry.Match>
<Entry.SortBy>
<Kind Order="Constructor" />
</Entry.SortBy>
</Entry>
<Entry DisplayName="All other members" />
<Entry DisplayName="Test Methods" Priority="100">
<Entry.Match>
<And>
<Kind Is="Method" />
<HasAttribute Name="Xunit.FactAttribute" />
</And>
</Entry.Match>
<Entry.SortBy>
<Name />
</Entry.SortBy>
</Entry>
</TypePattern>
<TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
<TypePattern.Match>
<And>
<Kind Is="Class" />
<HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" />
</And>
</TypePattern.Match>
<Entry DisplayName="Setup/Teardown Methods">
<Entry.Match>
<And>
<Kind Is="Method" />
<Or>
<HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" />
<HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" />
<HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" />
<HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" />
</Or>
</And>
</Entry.Match>
</Entry>
<Entry DisplayName="All other members" />
<Entry DisplayName="Test Methods" Priority="100">
<Entry.Match>
<And>
<Kind Is="Method" />
<HasAttribute Name="NUnit.Framework.TestAttribute" />
</And>
</Entry.Match>
<Entry.SortBy>
<Name />
</Entry.SortBy>
</Entry>
</TypePattern>
<TypePattern DisplayName="Default Pattern">
<Entry DisplayName="Public Delegates" Priority="100">
<Entry.Match>
<And>
<Access Is="Public" />
<Kind Is="Delegate" />
</And>
</Entry.Match>
<Entry.SortBy>
<Name />
</Entry.SortBy>
</Entry>
<Entry DisplayName="Public Enums" Priority="100">
<Entry.Match>
<And>
<Access Is="Public" />
<Kind Is="Enum" />
</And>
</Entry.Match>
<Entry.SortBy>
<Name />
</Entry.SortBy>
</Entry>
<Entry DisplayName="Static Fields and Constants">
<Entry.Match>
<Or>
<Kind Is="Constant" />
<And>
<Kind Is="Field" />
<Static />
</And>
</Or>
</Entry.Match>
<Entry.SortBy>
<Kind Order="Constant Field" />
</Entry.SortBy>
</Entry>
<Entry DisplayName="Fields">
<Entry.Match>
<And>
<Kind Is="Field" />
<Not>
<Static />
</Not>
</And>
</Entry.Match>
<Entry.SortBy>
<Readonly />
<Name />
</Entry.SortBy>
</Entry>
<Entry DisplayName="Constructors">
<Entry.Match>
<Kind Is="Constructor" />
</Entry.Match>
<Entry.SortBy>
<Static />
</Entry.SortBy>
</Entry>
<Entry DisplayName="Properties, Indexers">
<Entry.Match>
<Or>
<Kind Is="Property" />
<Kind Is="Indexer" />
</Or>
</Entry.Match>
</Entry>
<Entry DisplayName="Interface Implementations" Priority="100">
<Entry.Match>
<And>
<Kind Is="Member" />
<ImplementsInterface />
</And>
</Entry.Match>
<Entry.SortBy>
<ImplementsInterface Immediate="True" />
</Entry.SortBy>
</Entry>
<Entry DisplayName="All other members" />
<Region Name="IEquatable Support" Priority="100">
<Entry DisplayName="IEquatable" Priority="150">
<Entry.Match>
<Or>
<And>
<Access Is="Public" />
<Kind Is="Method" />
<Name Is="Equals" />
</And>
<And>
<Access Is="Public" />
<Override />
<Kind Is="Method" />
<Name Is="GetHashCode" />
</And>
</Or>
</Entry.Match>
<Entry.SortBy>
<Name />
</Entry.SortBy>
</Entry>
<Entry DisplayName="Operators" Priority="150">
<Entry.Match>
<And>
<Access Is="Public" />
<Static />
<Kind Is="Operator" />
<Name Is="!=" />
</And>
</Entry.Match>
</Entry>
</Region>
<Entry DisplayName="Nested Types">
<Entry.Match>
<Kind Is="Type" />
</Entry.Match>
</Entry>
</TypePattern>
</Patterns>

0

Thanks for the File Layout.

As a fix, you might remove "<Name Is="!=" />" from "Operator" and when all operators will be placed into "IEquatable Support" region. I don't think "Name" property supports something like "!=" or "==" as a valid name.  

Also you might opt for "All" item in "Remove Regions" combobox

Thanks! 

0

OK, yes, that does move the != and == operators now, but

  1. That doesn't help me as I commonly create Explicit Cast operators, and now they get moved too! Unfortunately, Operators will have to support Name (or some other way of distinction) for this to be of any use to me. See an example below.
  2. That still does not get the strongly typed Equals moved though. (public bool Equals(ModelA other)...) is still out of the new region.

This is an explicit cast that I have contrived for this example. This code should not get moved as part of the new code arrangement as it doesn't have anything to do with IEquatable support.

public static explicit operator ModelA(int value)
{
return new ModelA {Counter = value};
}

Thanks for your suggestions though. Hopefully we can get this worked out!

0

Hello again! 

After talking with an author of such feature in ReSharper, we found a solution to match != and == operators. You need to use compiler names for these operators:

  • "!=" - "op_Inequality";
  • "==" - "op_Equality";

The XAML view will be:

 <Entry DisplayName="Operators" Priority="150">
<Entry.Match>
<And>
<Access Is="Public" />
<Static />
<Kind Is="Operator" />
<Or>
<Name Is="op_Equality" />
<Name Is="op_Inequality" />
</Or>
</And>
</Entry.Match>
</Entry>
0

Hello Alexander!

Yup, that worked! Not exactly intuitive, but it worked!

Now, does your developer have any suggestions on getting the strongly typed Equals to be included? In the example above, it would be this one:

        public bool Equals(ModelA other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Counter == other.Counter && string.Equals(Name, other.Name);
        }

But, of course, the rule can't hard code "ModelA" as that is a generic method from "SnippetIEquatable<T>". That said, I'm not sure why that isn't just included under the the following snip (from my reply above)...

    <Access Is="Public" />
    <Kind Is="Method" />
    <Name Is="Equals" />

Thanks!

Jim

 

 

0

Hello Jim,

Please try this 

<Region Name="IEquatable Support" Priority="100">
<Entry DisplayName="IEquatable" Priority="150">
<Entry.Match>
<And>
<Access Is="Public" />
<Kind Is="Method" />
<Or>
<Name Is="Equals" />
<And>
<Override />
<Name Is="GetHashCode" />
</And>
</Or>
</And>
</Entry.Match>
<Entry.SortBy>
<Name />
</Entry.SortBy>
</Entry>

<Entry DisplayName="Operators" Priority="150">
<Entry.Match>
<And>
<Access Is="Public" />
<Static />
<Kind Is="Operator" />
<Or>
<Name Is="op_Equality" />
<Name Is="op_Inequality" />
</Or>
</And>
</Entry.Match>
</Entry>
</Region>
0

Hello Alexander,

Sorry, no difference for me. Is this working for you with my sample above? Or is this working for you with a different class?

Thanks,
Jim

0

Jim, 

My apologies, forgot to mention one thing. Since strongly typed Equals method right now is covered by "Interface Implementations" pattern, so you need to add a rule to "Interface Implementations" not to include "Equals" name:

<Entry DisplayName="Interface Implementations" Priority="100">
<Entry.Match>
<And>
<Kind Is="Member" />
<ImplementsInterface />
<Not>
<Name Is="Equals" />
</Not>
</And>
</Entry.Match>
<Entry.SortBy>
<ImplementsInterface Immediate="True" />
</Entry.SortBy>

 Then Equals won't be mapped by "Interface Implementations" pattern and will go to "IEquatable Support" region.

Thanks! 

0

Fantastic! I also see that "Interface Implementations" is set to a Priority of 100/High and if I set mine to 150/Highest, it overrides "Interface Implementations" and I don't have to tweak "Interface Implementations". 

I had set the line "<Entry DisplayName="IEquatable" Priority="150">" to 150/Highest, but that was inside "<Region Name="IEquatable Support" Priority="100">", so now I know that inner priorities don't override items in an outer scope.

One last question. One option with <ImplementsInterface /> is to say <ImplementsInterface Immediate="True" />. I can't seem to find any documentation to tell me what "Immediate" means or does.

Thanks for all your help with this,
Jim

0

Please sign in to leave a comment.