Showing posts with label Plugin. Show all posts
Showing posts with label Plugin. 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


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


Monday, April 27, 2009

CR_HelloWorld – Your first DXCore plugin

So for your first DXCore Plugin, we’ll show you the traditional “Hello World” program…plugin style.

We’ll show you how you can execute any managed code from a plugin – Fun stuff

First up you’ll need to Create a Vanilla plugin. From here you’ll be able to add all the relevant pieces to create CR_HelloWorld.

 

Ok first Step… ensure you’re looking at the designer view of your plugin. If you’ve not renamed it this will be the “Plugin1” (cs or vb) file.

DXCoreDesignSurfaceSmall

Next locate the ‘Action’ component in your toolbox and place one on the designer surface.

DXCoreControls

The set an appropriate name and a few properties:

actSayHelloWorld

Finally handle the “Execute” Action and do something within it.

This seems appropriate:

-------------------------------------------------------------
Private Sub actSayHelloWorld_Execute(ByVal ea As ExecuteEventArgs) Handles actSayHelloWorld.Execute
    MessageBox.Show("Hello World")
End Sub
-------------------------------------------------------------

Ok well that’s the majority of the work done.

However you’re going to need some way to activate this action…

So hit F5 to run up a new instance of studio to debug your new plugin.

Once this new copy of studio is running, be sure to ensure your plugin is actually running in memory.

Next we will attach your new action to a shortcut….

Open up the DXCore options screen (DevExpress\Options) Note – You may need to use the previously mentioned 'registry hack' to get the DevExpress menu to show up, if you’re running one of the Free DXCore based products.

Next select the IDE\Shortcuts page from the left hand side and you should see something like the following.

DXCoreOptionsShortcuts

In the shot you can see (Click to view full image) I have created a new ‘keyboard shortcut’ ( which I did via a context menu within the shortcut tree) and then I have filled out the Key1 and command options.

Now you can have your copy of Studio say “Hello World” whenever you choose :)

Obviously this is a something of a contrived example but, as you can see, it would be easy to have studio execute any managed code you care to dream up in a similar way.


How do I test my DXCore Plugin?

A simple enough question but it deserves a bit of thought.

You’ll recall from my previous post on creating a plugin, I suggested indicating that your plugin was a “Community plugin”

The reason for this was to prevent multiple copies of studio from loading and thus locking the plugin dll that you were working on.

So this leads you to a small problem… How to get the DXCore to load your plugin if you have explicitly asked it not to?

Well here’s how…

Simply open up the plugin manager from the DevExpress\Tool Windows\Plug-in Manager.

 

Side note: If you are using one of the free DXCore products, then you may not have such a menu. In order to acquire access to this most useful of facilities you’ll need to do a bit of registry hackery.
The following solution was provided in the AlexS (Another very helpful DevExpress employee)  in the DevExpress IDE Tools Forums
-------------------------------------------------------------
Please invoke the Registry editor, add the "HideMenu" DWORD value to the following Registry key, and set its Value to 0:

HKEY_LOCAL_MACHINE\SOFTWARE\Developer Express\CodeRush for VS\9.1
This should make the "DevExpress" menu visible.
-------------------------------------------------------------

DevExpressPluginManagerMenuOption

…to reveal this…

PluginManagerHelloWorld

Simply double click the entry you would like to load and, after confirmation your action, the DXCore will load your plugin.

All should now be good and you can commence testing your plugin..


What is a DXCore Action?

An action is one of the components you can drop on your plug-in design surface.

When you do so it looks like this ....

DXCoreAction

It can be triggered via either a mouse or keyboard shortcut (found in the IDE shortcuts page of the options screen) or placed on a menu (for example the context menu of the Code Editor)

It is a derivative of ‘system.component’ adding a few properties…

 DXCoreActionProperties

…and events…

DXCoreActionEvents

Some of these are worth drawing special attention to.

Properties
ActionName: The name of the action as displayed to UI like the commands dropdown in the IDE\Shortcuts options page.
ButtonText: What text would you like to represent this action when it is placed on a menu?
CommonMenu: A list of some of the more common menus found around VS.
ParentMenu: An alternative to CommonMenu. Use the name of any menu you know of, to place the action on that rather than one of the ‘common’ ones.

Note: The placing of Actions on menus is entirely optional and CommonMenu and ParentMenu are mutually exclusive options.

Events
Execute: The main event (so to speak :)). Fired whenever the assigned shortcut is triggered or the representative menu item is chosen. This is how the Action does it’s stuff.

In my next post I will walk you through your first actual plugin … Yes we will be starting slow, but we’ll ramp up fairly quickly as the posts flow… so you’ll be knocking out your own custom refactorings and the like pretty soon.


Monday, April 13, 2009

Creating a new DXCore Plugin

[Updated: This post has been updated to account for some small changes in the 9.1.3 release]

There are certain steps you must follow in order to create a DXCore plugin of any kind. This blog post will outline and explain those steps.

So the first step is to create the right sort of project.

If all is installed correctly you should be able to find the 2 available projects (‘Standard Plug-in’ and ‘Tool Window Plug-in’) in the DXCore section under either the Visual Basic or Visual C# sections in the New Project dialog.

Initially I’ll suggest that you pick ’Standard Plug-in’ as you can always add the Tool window later if you need to.

FileNewDXCorePlugin

Once you’ve given your plugin a Name, and hit Ok, you’ll be presented with the Plug-in Project settings dialog.

 InitialPluginSettings913_thumb5

Plug-in Type: System Plug-in: will cause your plugin to be deemed a ‘System plugin’ Duh! But what does this mean? Simply put, system plugins are loaded ahead of non system plugins. 90% of all plugins have no need to do this… I have yet to use this facility in any plugin I have written.

Plug-in Type: Plugin: Will cause your plugin to be built to “IDETools\System\DXCore\Bin\PlugIns\”. I’m guessing that this is something that DevExpress do themselves. Although I see no reason not to use the Community option.

Plug-in Type: Community Plug-in: is a new option with version 9.1.3. This is due, from what I can see, to the new Community Plugin Path option. This option sets the compile path of your new plugin, to your current community folder (Setup when you first loaded CodeRush or RefactorPro or DXCore after installing 9.1.3.) This means that, if you accepted the default, your plugin will exist under your ‘My Docs’ folder and as such will not require you to Launch Visual Studio with Admin rights in order to compile your plugin. – You should pretty much always pick this option.

Checking ‘Load manually’ will cause an entry to be added to the ‘plugin manager’ (another plugin), which in turn will prevent the DXCore from automatically loading your plugin.

Why would you do this?

Well, suppose that whilst developing your plugin, you are suddenly required to do something else. (Heaven forbid that anything should come ahead of this most important work). What you do is you go and immediately fire up another copy of VS and you load what ever you need into that session and off you go.

The trouble is that (assuming that you’ve compiled your plugin at least once) DXCore has now loaded a copy of your unfinished/unpolished plugin into memory. This might not be a problem but there are 2 ways I can think of that might mean it is…

1.> You’re writing the most killer refactoring known to man, and you’ve just completed the ‘DeleteAllThisUselessCrap’ routine but haven’t quite gotten around to writing the ‘InsertGloriouslyShineyCode’ and if activated, without taking suitable precautions then you could be up the swanny.

2.> You might want to tab back to to some more development on your plugin whilst you’re waiting for the results of your latest check-in on your ‘Important project’, but unfortunately the copy of VS hosting your ‘Important Project’ is maintaining a lock on your plugin’s dll and as such you’ll be unable to compile it, much less test it.

So in order to avoid these scenarios, I always tick the ‘Load Manually’ tick box which then ensures that the DXCore never *automatically* loads my plugin. Then when I launch a copy of VS for testing it, I locate it in the plugin manager and double click it in there. At this point DXCore wakes up, notices the plugin and loads it. In doing this you have only loaded your plugin into the address space of a single instance of VS, which exists entirely for the purpose of testing your plugin. This also means that when you shut down this single copy of VS, you’ll be able once again to resume development of your plugin. Great huh?

Note: This setting is local to your development installation only and will not affect any users of your plugin.

Default Load Type

I asked AlexZ (one of the great brains behind the DevExpress IDE Tools) about the ‘Default load type’ setting (and most everything else as well :) – Alex is a great source of info) and he had this to say:

On Demand - plugin is loaded when some event happens for which plugin should work. e.g. if you plugin contains some action - then it will be loaded only when action is being executed. Or it may define context or string provider or any other provider - and plugin will be loaded when provider is really accessed.
On Idle - Plugin will be loaded on editor idle, IOW when VS editor is being idle.
At start-up - Plugin will be loaded on start-up - however it will add time to overall loading time.

So which one should you select.. well the answer is it depends.. Personally I typically leave the default of ‘On demand’ in place as this means your plugin should not affect the start-up time of VS. This might mean that you have a minor slow down as you activate your plugin for the first time, but it will not contribute to slowing down the start up time of VS.

So to summarize… I recommend that you tick the ‘Load manually’ option and leave everything else (except perhaps the name) exactly as default … then hit Ok

At this point VS churns away and provides you with a nice clean plugin project ready for you to create the greatest plugin known to man… after all .. that’s what you were going to do right :)

I am now contractually obligated to make you wait until the next instalment before I reveal anything else :)


DXCore Plugin Types

A single DXCore plugin dll can perform a single simple task or many different functions. These functions are added to a plugin by adding various components to it’s design surface. These components will need various properties set and events handled, but the process is typically quite simple.

These components include:

  • Action – A simple object which represents any arbitrary piece of Code you like. Placable on many of the VS menus and assignable to a keyboard or mouse shortcut (optionally in combination with a context) using the DXCore IDE\Shortcuts option page.
  • RefactoringProvider – Used to add items to the ‘Refactor’ Menu in RefactorPro
  • CodeProvider – Used to add items to the ‘Code’ menu in CodeRush. It differs from the RefactoringProvider in that by convention items placed on this menu are allowed to alter the functionality of the code they affect
  • IssueProvider – Used to add ‘Code smells’, ‘Warnings’ and the like to the CodeRush ‘Code Issues’ feature.
  • CodeMetricProvider – Used to provide additional metrics for both inline metrics and the Metrics tool window.
  • ContextProvider – Used to add items to the Context Tree. This in turn is used to indicate suitability of Templates, Shortcuts and other Coderush facilities.
  • NavigationProvider – Used to add extra Items to the Navigate SmartMenu
  • SmartTagProvider – Used to add a new section to the DXCore SmartTag menu. Items like the existing ‘Refactor’ and ‘Code’ menu sections
    StringProvider and TextCommand – used to enhance existing features like templates, selection embeddings, Selection Inversions and others. Both perform some function, but the StringProvider returns a value to it’s calling mechanism where the TextCommand does not.

Depending on the type of functionality you are after, you may not need to place any additional component on your plugin. The plugin object itself has several properties and events that, when properly used, can provide some very useful effects.

Over the course of the next few blog posts I am hoping to provide some insight into the general process of creating a plugin, as well as the facilities provided by each of these components.


Friday, June 27, 2008

New Plugin "MSDN BCL Help"

So I just released another new plugin.

CR_MsdnBclHelp available from the Community Plugin Site

This one provides a single action "MSDN BCL Help" which causes your browser to launch a url pointing at the help page for the Type that your caret is situated on.

For example: When your caret is positioned on the word string within the phrase “Dim X as String”

I have bound this to the 'F1'  key combo in my copy of CodeRush.

To do this… visit the options screen via “DevExpress\Options” and locate IDE\Shortcuts

Create new “Keyboard shortcut” via the Toolbar button at the top and fill out “F1” (Or Alt+F1) as the key, and “MSDN BCL Help” as the command name.

Click Ok and wonder at how pressing F1 now launches the correct MSDN page for any Type your caret is situated on :)

For the ultimate “Lowband” (read quick) experience I highly recommend you follow the steps outlined on Craig Andera's wonderful post regarding the low bandwidth version of MSDN.

The primary download site for this DXCore plugin is http://www.rorybecker.me.uk/DevExpress/Plugins/Community/CR_MsdnBclHelp/

[Note: If you link here, please link to the folder not the file, as future versions will be named according to their version and old versions may be moved elsewhere.]

[Update: Recently (in build 322)this plugin has been enhanced to take direct advantage of MSDN’s low bandwidth facility. A new parameter has been added to allow you to specify ‘loband’ or ‘robot’ or any of a number of other options (detailed in Scott Hanselman's recent post. Simply pass the desired keyword in the parameter box as shown in the image below and click ok to take direct advantage of this new facility]

MSDNBCLHelp

Never has it been so easy to use Visual studio and MSDN together.