Thursday, July 09, 2009

HowTo: Write a header plugin for Coderush

I was asked recently if I knew of any plugins for Coderush which would allow one to hit a key and inject a header into the current file.

The idea was that the header would be injected in a location within the existing file but not necessarily where the caret was currently residing.

My initial idea of “use a template” scuppered by this idea, as templates typically expand where your caret is.

But perhaps not.

I quickly rerouted my thinking an  came up with the following idea.

What about a new Action (what is an action?) which would take a parameter of which template to expand. It could drop a marker at the current location… Jump to the top of the current file, Expand the template there, and then collect it’s marker… Perfect right ? :D

So how does one do that then…

See my previous Tutorials for how to create a simple action plugin.

The Action Properties

In this case we will simply set the properties differently….
CR_ExpandHeaderProperties

…add some Parameters (via the Parameters property)….
CR_CreateHeaderParams1

… flesh out a few of their details…
CR_CreateHeaderParams2CR_CreateHeaderParams3  

…and write some different code in the execute routine of the Action.

As usual, the code is really what it’s all about:

The Code

What we need to do here is to drop a marker, Go inject the template text and then collect the marker.

The Main event looks like this:

-------------------------------------------------------------
Private Sub actExpandHeader_Execute(ByVal ea As DevExpress.CodeRush.Core.ExecuteEventArgs) Handles actExpandHeader.Execute
' Get Params Dim HeaderType As String = ea.Action.Parameters.Item("HeaderType").ValueAsStr
    Dim FullTemplateNameAndPath As String = ea.Action.Parameters.Item("Template").ValueAsStr
    ' Drop Marker
    CodeRush.Markers.Drop()
    ' Determine Insert Point
    Dim InsertPoint = GetJumpLocation(HeaderType)
    ' Expand Template
    Call ExpandTemplateAtSourcepoint(FullTemplateNameAndPath, InsertPoint)
    ' Collect Marker
    CodeRush.Markers.Collect()
End Sub
-------------------------------------------------------------

Then we’ll need to determine the location at which to insert our header. This is based on a passed parameter.

-------------------------------------------------------------
Private Function GetJumpLocation(ByVal HeaderType As String) As SourcePoint 
    Dim ElementRange As SourceRange
    Select Case HeaderType.ToLower 
        Case "file" ElementRange = CodeRush.Documents.ActiveTextDocument.Range 
        Case "type" ElementRange = CodeRush.Source.ActiveClassInterfaceStructOrModule.Range 
        Case "member", "method" ElementRange = CodeRush.Source.ActiveMember.Range 
        Case Else ElementRange = CodeRush.Documents.ActiveTextDocument.Range 
    End Select Return ElementRange.Start
End Function
-------------------------------------------------------------

…and finally we’ll need to actually inject the Header from the nominated Template…

-------------------------------------------------------------
Private Sub ExpandTemplateAtSourcepoint(ByVal FullTemplateNameAndPath As String, ByVal InsertPoint As SourcePoint)
    Dim TemplateName = FullTemplateNameAndPath.Split("\"c).Last
    Dim TemplateCategory = FullTemplateNameAndPath.Substring(0, FullTemplateNameAndPath.Length - TemplateName.Length - 1)

    Dim Template = CodeRush.Templates.FindTemplate(TemplateName, TemplateCategory, _
                                                   CodeRush.Documents.ActiveLanguage)

    Dim FinalText = CodeRush.Strings.Expand(Template.FirstItemInContext.Expansion)

    ' Again this isn't the best as we loose links etc... But it was a prety quick turnaround right ? :P
    CodeRush.Documents.ActiveTextDocument.InsertText(InsertPoint, FinalText)
End Sub

-------------------------------------------------------------

This last part expands the template into an in memory string which is then injected into the ActiveTextDocument at the previously calculated location. ...and we’re done.


1 comment:

Nick Kirkes said...

Hey Rory, thanks for the posts on creating CR plug-ins. Trying my hand using your examples...

That said, I was wondering if you'd expand a bit on the header plug-in idea and specifically how/where you envision the header template to be located. Is that something that should just be set as part of the "Template" Action Param? Or is there a way to let the user specify this in an option file?

Thanks