Adding and modifying attributes - questions

Hello,
I am writing my first plugin for Resharper and I came across few problems.

How to force adding attributes on a new line (new AttributeSection), instead of being separated by comma ?

My plugin includes a CodeCleanup module, which adds the [DataMember(Name="...")] Attribute to a certain properties.
To add this attribute I am using the following code:

IPropertyDeclaration propertyDeclaration = ...;
Pair<string, AttributeValue>[] propertySetters = ...;
ITypeElement typeElement = psiServices
    .CacheManager
     .GetDeclarationsCache(sourceFile.PsiModule, true, false)
     .GetTypeElementsByCLRName(new ClrTypeName("System.Runtime.Serialization.DataMemberAttribute"))
     .First();

IAttribute dataMemberAttribute = elementFactory.CreateAttribute(typeElement, new AttributeValue[0], propertySetters);
propertyDeclaration.AddAttributeBefore(dataMemberAttribute, propertyDeclaration.Attributes.FirstOrDefault());


Unfortunately, when the property already has other attributes, the DataMember attribute is added to the same line as the existing and separated by comma, e.g.:

// BEFORE:

[XmlIgnore]
public string Address {get;set;}

// AFTER:

[DataMember(Name="Address"), XmlIgnore]
public string Address {get;set;}

// WHAT I WANT:

[DataMember(Name="Address")]
[XmlIgnore]
public string Address {get;set;}


And another question: how to add property setters to existing attributes ? There are no methods on an IAttribute to add property setters. Example of what I want to accomplish:

BEFORE:

[DataMember]
public string Address {get;set;}

WHAT I WANT:

[DataMember(Name="Address")]
public string Address {get;set;}



Regards

3 comments
Comment actions Permalink

The implementation of AddAttributeBefore adds the attribute to the anchor's attribute list, rather than creating a new attribute section. There are no helper methods to create a new attribute section directly. Instead, you can follow the same pattern used by CSharpElementFactory or CSharpSharedImplUtil (which is the implementation of AddAttributeBefore) - they create a class definition that has an attribute, and grab the attribute tree node (see the CreateAttribute method in both classes). You can implement the same logic, but get the IAttributeSection instead of the IAttribute. You can get this from the top of the tree (the class declaration) and calling FindChildByRole(ChildRole.CSHARP_ATTRIBUTES), which returns an IAttributeSectionList, and getting the first section, or walking up from the attribute, using AttributeSectionNavigator.GetByAttribute. You'll most likely need to remove the dummy attribute you created as part of the class declaration - ModificationUtil.DeleteChild.

Alternatively, you can probably shortcut this by using the navigators on the attribute returned from CSharpElementFactory.CreateAttribute - this relies on the implementation detail that CreateAttribute gets an attribute from a class declaration. As ever, relying on implementation details is a bit of a gamble - it might change in the future.

Once you have a new IAttributeSection, you need to add it to your IAttributeSectionList. Use AttributeSectionNavigator and AttributeSectionListNavigator from your property to get your IAttributeSectionList. You then need to add your section into the section list. You can do this with ModificationUtil.AddChildBefore(originalSection, newSection), or you might be able to use CSharpSharedImplUtil.AddListItemBefore(propertyDeclaration, PropertyDeclarationStub.CSHARP_ATTRIBUTES, AttributeSectionList.ATTRIBUTE_SECTION, CSharpTokenType.NEW_LINE, newSection, originalSection)

You can add arguments to the constructor using AddArguemntBefore or AddArgumentAfter, which IAttribute inherits from ICSharpArgumentsOwner. You can create arguments (and named arguments) using CSharpElementFactory.

Hope this helps
Matt

0
Comment actions Permalink

How did you set attribute properties?

I need to initialize my attribute with a value.
Example:

[DataContract(Name = "TestClass")]


I just can figure out how to create the attribute with the name set:
IAttribute dataMemberAttribute = elementFactory.CreateAttribute(typeElement, new AttributeValue[0], propertySetters);

in your example you have
Pair<string, AttributeValue>[] propertySetters = ...;

what is the code you are using to set the "Name" property of the DataContract?
Thanks,
John

0
Comment actions Permalink

Hello John,
Try the following (assuming you have the psiModule and other required variables):

var pairs = new List<Pair<string, AttributeValue>>

    {

        new Pair<string, AttributeValue>("Name", new AttributeValue(new ConstantValue("TestClass", psiModule)))

    };


var nameAttribute = elementFactory.CreateAttribute(typeElement, new AttributeValue[0], pairs.ToArray());

Regards,
Maciej

0

Please sign in to leave a comment.