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….

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

… flesh out a few of their details…

…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
    ' Determine Insert Point
    Dim InsertPoint = GetJumpLocation(HeaderType)
    ' Expand Template
    Call ExpandTemplateAtSourcepoint(FullTemplateNameAndPath, InsertPoint)
    ' Collect Marker
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, _

    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?