Showing posts with label HowTo. Show all posts
Showing posts with label HowTo. Show all posts

Thursday, August 06, 2009

New CodeRush Plugin: CR_MethodPreview

The other night I saw a tweet by Neal Culiner (@NCSoftware) regarding a plugin he’d like to see developed. 

Shortly there was a response was a tweet by Matthew MacSuga (@csharpbydesign). He mentioned that he’s asked for something like this a while back.

What they’re after, is a way to preview the code of a method, without having to visit that method.

I feel a little guilty because I remember Matthew mentioning this a while back. I think I started a debate at the time on "what should trigger the functionality” and we (well probably I) got bogged down in the details. Sorry Matthew :(

This time I just thought .. “Sod This” - (Wasting time that is, not talking to Matthew and Neal) - “Let’s just get something out there, and we’ll see where it leads afterward”.

So after a quick vote on the name of said plugin (Where Neal suggested the name and we all agreed :D) I was off to build a new plugin.

Where to start?

So I created a basic plugin and dropped an action in place. (For details please see a previous post)

From here I needed 2 things:

  • A function to determine which method to preview.
  • A function to actually do the preview.

There are some complexities in the original brief that I didn’t feel comfortable with initially.

  • “Select method based on method call under cursor”.
    • I have elected to use caret position in this first version.
  • “Hold a key down to see Preview”
    • I have elected to use a toggleable action in this first version.

Which Method?

So the first thing to do is to work out which method to display a preview for.

Some Terminology:
Given the sample…

    Public Sub Method1() 
Call Method2()
End Sub


  • “Method2” is known as the MethodReferenceExpression


  • “Call Method2()” is the MethodCall.



I would like this plugin to function from either of these 2 locations, so I need to return the declaration of the method to which either refers.



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

    Private Function GetMethod(ByVal Element As LanguageElement) As Method


        Dim Method As Method = Nothing


        Select Case Element.ElementType


            Case LanguageElementType.MethodCall


                Method = TryCast(Element, MethodCall).GetDeclaration


            Case LanguageElementType.MethodReferenceExpression


                Method = TryCast(Element, MethodReferenceExpression).GetDeclaration


        End Select


        Return Method


    End Function


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



To this method, I can pass CodeRush.Source.GetNodeAt(CodeRush.Caret.SourcePoint)



Later I can replace this with something which passes the element under the cursor, and I won’t have to change much at all.



Next we need to determine the code behind the Method in question.

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


Dim TheCode as String = CodeRush.CodeMod.GenerateCode(Method)


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


And now we have the code we’d like to render in our preview window. So lets crack on and create the preview itself.




The Preview Window



Building a preview window is easy… (Isn’t everything with the DXCore? :P)



The following function accepts the code and a location for the preview and builds the preview accordingly. It also places the preview on screen.



(The code to hide the preview has been omitted here but is even simpler.)



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

Private mPreviewWindow As CodePreviewWindow





Private Sub ShowCodePreview(ByVal Code As String, ByVal insertionPoint As SourcePoint)


    mPreviewWindow = New CodePreviewWindow(CodeRush.Documents.ActiveTextView, insertionPoint)


    mPreviewWindow.AddCode(Code)


    mPreviewWindow.ShowPreview()


End Sub


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





And so things come together in the main ToggleMethodPreview_Execute() as…





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


Private mShowingPreview As Boolean = False





Private Sub ToggleMethodPreview_Execute(ByVal ea As DevExpress.CodeRush.Core.ExecuteEventArgs) Handles ToggleMethodPreview.Execute


    If mShowingPreview Then


        Call HideCodePreview()


    Else


        Dim Method As Method = GetMethod(CodeRush.Source.GetNodeAt(CodeRush.Caret.SourcePoint))


        If Not Method Is Nothing Then


            Call ShowMethodPreview(Method, CodeRush.Caret.SourcePoint)


            mShowingPreview = True


        End If


    End If


End Sub


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



Summary



That’s pretty much all there is to it.



To use this plugin, simply bind a key to the “ToggleMethodPreview” action and activate it when your caret is in a suitable location.



For this plugin I wrote all of 42 lines of code including signatures.



Proof once again, as if you needed it, that the DXCore makes plugin writing ridiculously easy.



So now we have a first draft of “CR_MethodPreview”… go play… see what you think… And we’ll see where it goes from there.



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

For more DevExpress news, check out their Facebook page


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


How to: Bind a key in CodeRush.

If you would like to use CodeRush to bind a key, you're going to need to access the Options dialog.

This is available from the DevExpress Menu if you have the full version of CodeRush.
Alternatively, if you have CodeRush Xpress, you can press CTRL+Alt+Shift+O.

The left hand side of the options screen should look like this:

This screenshot was taken from the full version of CodeRush. Your version may be missing some items.

This might be because:

  • The 'user level' setting in the lower left of the options screen may be set to something other than expert.
  • You may be running CodeRush Xpress which has less features than the full version of CodeRush.

Select the 'IDE\Shortcuts' node as indicated in the screenshot.

The list of CodeRush bindings will appear:

As you can see, I typically create my shortcuts beneath a 'Custom' folder. this helps when it comes time to backup my customizations.

A new folder or shortcut can be created either from the Toolbar at the top of the shortcuts tree, or from a context menu triggered by right-clicking within the tree itself.

When you create a key binding, the right hand side of the screen will light up thus:

This section allows you to enter:

  • A keystroke which triggers your shortcut.
  • A command to execute once triggered.
  • Any params you wish to pass the command. (Optional)
  • A Context to limit your Shortcut’s use. (Optional)

Once you’ve filled out these elements, you are free to either create more bindings or save via the Ok or Apply button and start making use of your new binding.

----------------------------------------------------------------------------------------
Just a thought: Why not swing by the DevExpress Facebook page. You know you want to :)
----------------------------------------------------------------------------------------


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.