Showing posts with label CatelR#. Show all posts
Showing posts with label CatelR#. Show all posts

Friday, February 21, 2014

Developing a ReSharper Plugin – The backward compatibility approach

Introduction

We have being developed a ReSharper plugin for Catel framework also known as CatelR# for a while, following an interesting approach in order support the new R# version and also keep the backward compatibility.

If you want to know how we made it, just take a look.

Visual Studio solution setup

1)    Create project per supported R# version, which means that the output of each project is targeting to the specific version of R# and references the specific version of the SDK.



2)    Keep in mind, that could be several breaking changes between R# SDK versions, but nothing that couldn’t be handled with pre-processor directives.

#if R70 || R71 || R80
        protected override void Process(CSharpGeneratorContext context)

#elif R61
        public override void Process(CSharpGeneratorContext context)
#endif
        {
            CSharpElementFactory factory = CSharpElementFactory.GetInstance(context.Root.GetPsiModule());
#if R80
            IDeclaredType viewModelToModelAttributeClrType = TypeFactory.CreateTypeByCLRName(CatelMVVM.ViewModelToModelAttribute, context.PsiModule, UniversalModuleReferenceContext.Instance);
#else
            IDeclaredType viewModelToModelAttributeClrType = TypeFactory.CreateTypeByCLRName(CatelMVVM.ViewModelToModelAttribute, context.PsiModule);
#endif
            /*...*/
#if R80
                        var fixedArguments = new List<AttributeValue> { new AttributeValue(ClrConstantValueFactory.CreateStringValue(model.ShortName, context.PsiModule, UniversalModuleReferenceContext.Instance)) };
#else
                        var fixedArguments = new List<AttributeValue> { new AttributeValue(ClrConstantValueFactory.CreateStringValue(model.ShortName, context.PsiModule)) };
#endif
                        if (propertyName != modelProperty.ShortName)
                        {
#if R80
                            fixedArguments.Add(new AttributeValue(ClrConstantValueFactory.CreateStringValue(modelProperty.ShortName, context.PsiModule,  UniversalModuleReferenceContext.Instance)));
#else
                            fixedArguments.Add(new AttributeValue(ClrConstantValueFactory.CreateStringValue(modelProperty.ShortName, context.PsiModule)));
#endif
                        }
            /*...*/
                    }
                }
            }
        }

3)    Keep all these entire project sources synchronized. Could be very easy thanks to Caitlyn.




4)   Finally redirect the build projects outputs to dealing with ease with the packaging of the deployment units.

Building the deployment units

The R# plugin build process indeed an heterogeneous one as any build process. Even though  this build could be handled via msbuild tasks, we actually recommend the usage of tools with intuitive GUI in order to quickly creating and debugging such build "scripts", such as FinalBuilder or VisualBuild.


1) Since 8.0 R# version  the NuGet based extension manager is available. Therefore one of the build output could be a NuGet package to distribute your plugin through the ReSharper extension gallery.

2) But NuGet based extension manager is not available for all R# versions. Therefore a second build output could also be classic deployment unit built on top of any of the existing installer system.  For instance InnoSetup or NSIS.

The following are the code snippets from the install and uninstall sections of CatelR# setup. Notice how we deal with build output to support all R#  versions.


# ...
# Installer section
# ...

Push "v6.1"
Push "v7.0"
Push "v7.1"
Push "v8.0"
Push "v8.1"
${Do}
  Pop $0
  ReadRegStr $1 HKLM "Software\JetBrains\ReSharper\$0" InstallDir
  ${If} $1 != ''
    DetailPrint "Installing Catel.ReSharper for JetBrains ReSharper $0"
    SetOutPath "$1\Plugins\$(^Name)"
    ${If} $0 == 'v6.1'
      File /r "..\..\output\Debug\v6.1\*.dll"
    ${ElseIf} $0 == 'v7.0'
      File /r "..\..\output\Debug\v7.0\*.dll"
    ${ElseIf} $0 == 'v7.1'
      File /r "..\..\output\Debug\v7.1\*.dll"
    ${ElseIf} $0 == 'v8.0'
      File /r "..\..\output\Debug\v8.0\*.dll"
    ${ElseIf} $0 == 'v8.1'
      File /r "..\..\output\Debug\v8.1\*.dll"
    ${EndIf}
    Push true
    Pop $3
    WriteRegStr HKLM "${REGKEY}" "$0" 1
  ${EndIf}
${LoopUntil} $0 == "v6.1"

# ... 

# ... 
# Uninstaller section
# ... 

Push "v6.1"
Push "v7.0"
Push "v7.1"
Push "v8.0"
Push "v8.1"
${Do}
  Pop $0
  ReadRegStr $1 HKLM "${REGKEY}" "$0"
  ${If} $1 == '1'
    ReadRegStr $2 HKLM "Software\JetBrains\ReSharper\$0" InstallDir
    ${If} $2 != ''
      RMDir /r /REBOOTOK "$2\Plugins\$(^Name)" 
      DeleteRegValue HKLM "${REGKEY}" "$0"
    ${EndIf}
  ${EndIf}
${LoopUntil} $0 == "v6.1"  

# ...


Conclusions

Just a few minutes ago, I read the notification about the release of R#8.2 EAP. It’s a good moment to revalidate this approach. Let’s see,… creating a new project with post-fix 82, …installing the SDK package, ..., ...time out, sorry…slow connection…,…,…,..updating the build script, …, …, … reviewing for breaking changes, good news, there are no breaking changes,… updating setup script…., committing source modifications, … running the build script and it’s done.

Now you can update the extension from the extension gallery or download the full installer of CatelR# with support for R#8.2 EAP ;)

Saturday, September 28, 2013

Keep updated CatelR# with ReSharper 8.0



CatelR# is now compatible with the latest version of ReSharper (R#).

We also keep the compatibility with the most recent R# versions including 6.0, 7.0 and 7.1. The full installer, that also includes the R# 8.0 assemblies, of the latest beta of CatelR# is available to download here.

R# 8.0 includes a NuGet based Extension Manager and the Catel Team started to publish the CatelR# extension in the Extension Gallery.

Yes, CatelR# is alive. We keep working in more features and also are waiting for your ideas. You got one? Let us know!

So, ensure yourself to install the latest version of R# and you will no miss any of the forthcoming CatelR#'s cool features.

Thursday, August 23, 2012

Creating a view model with a model and mappings with CatelR#

There are tons of yarns based on MVVM developer’s experiences behind Catel framework. Some of them are well documented on Catel docs and one of my favorites is the named "Creating a view model with a model and mappings".

When I started to read it I identified myself as one of those developers that map all the view model properties back to the model.

I will remember you how it started:

"During the use of the MVVM pattern, we noticed that lots and lots of developers have a model, and map the values of the model to all properties of the view model. When the UI closes, the developers map all the properties back to the model. All this redundant code is not necessary when using the view models of Catel." More...

This only feature makes that my interest about Catel grown until I became one of the members of the development team. But this is part of the other history.

Let’s go back to the Catel feature again. If you don’t remember how it works, here is a summary.

Basically if you want to create a model the only thing that you have to do is decorate a view model property with the ModelAttribute. So if you want to expose the model property as view model one, and don’t write the mapping back code you must decorate the exposed property with the ViewModelToModelAttribute just like this:

    /// 
    /// The person view model.
    /// 
    public class PersonViewModel : ViewModelBase
    {
        #region Static Fields

        /// Register the FirstName property so it is known in the class.
        public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName", typeof(string));

        /// Register the Person property so it is known in the class.
        public static readonly PropertyData PersonProperty = RegisterProperty("Person", typeof(Person));

        #endregion

        #region Public Properties

        /// 
        /// Gets or sets the first name.
        /// 
        [ViewModelToModel("Person")]
        public string FirstName
        {
            get { return GetValue<string>(FirstNameProperty); }
            set { SetValue(FirstNameProperty, value); }
        }

        /// 
        /// Gets or sets the person.
        /// 
        [Model]
        public Person Person
        {
            get { return GetValue<Person>(PersonProperty); }
            set { SetValue(PersonProperty, value); }
        }

        #endregion
    }
 
This example will automatically create a mapping between Person.FirstName and PersonViewModel.FirstName.

This feature is amazing and Catel distributes some code snippets to accelerate writing such code:
  • vm - declare a view model
  • vmpropmodel - declare a property as model on a view model
  • vmpropviewmodeltomodel - declare a property as a pass-through property on a view model

But could you imagine to yourself writing this code (and more) as fast as is possible (near to the speed of light). Don't you believe me?

Watch the movie, and believe me, it is in slow motion ;)


This is a forthcoming feature of CateR#, now powered by Catel itself.

You got more ideas? Let us know!

X-ray StoneAssemblies.MassAuth with NDepend

Introduction A long time ago, I wrote this post  Why should you start using NDepend?  which I consider as the best post I have ever...