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.


Thursday, June 26, 2008

'CType' is Slow to Type

Ok It's not slow but the way my mind works, I tend not to think of casting until after everything else which leaves me wanting to change...

-------------------------------------------------------------
Dim X as XElement = SomeFunctionThatGetsAnXNode()
-------------------------------------------------------------

...into...

-------------------------------------------------------------
Dim X as XElement = CType(SomeFunctionThatGetsAnXNode(), XElement)
-------------------------------------------------------------

...and the number of keystrokes necessary to do this is in my opinion excessive.

So a couple of days ago, rather than knock up some kind of refactoring (which may have been extreme overkill), I thought about how I might do this with a "Selection Embedding".

Here is what I did.

1.> Create a selection embedding using the right-most embedding type.

-------------------------------------------------------------
Follow these steps to get to the Embedding options page:

1. From the DevExpress menu, select "Options...".
2. In the tree view on the left, navigate to this folder:

    Editor\Selections

3. Select the "Embedding" options page.
-------------------------------------------------------------

Then create a new embedding as shown

SelectionEmbeddingCType

2.> Create a shortcut for it.

-------------------------------------------------------------
Follow these steps to get to the Shortcuts options page:

1. From the DevExpress menu, select "Options...".
2. In the tree view on the left, navigate to this folder:

    IDE

3. Select the "Shortcuts" options page.
-------------------------------------------------------------

Duplicate a shortcut from amongst those in 'Selection\Embedding'.

Then alter its details to give it a suitable keystroke (I chose 'c') and change the parameter to match the name of your selection embedding.

Finally change the context of the shortcut under Selection on the right so that a "line fragment" is required (ticked) and that "Whole line" and "Multi line" is excluded(Crossed)

SelectionEmbeddingKeybinding

After doing this I can now highlight a line fragment and hit C.

This results in the selected text now being wrapped in a CType cast with the caret in the correct location for me to type the class to cast to.

Note you could also have added a marker to the end of the embedding to allow "Esc" to take  you to it but I considered that hitting "End" to be simpler in this case.

Any way.... Job Done.

Questions?  :)


Ask me a question - I'll try to find the answer.

This morning I reached an old Hanselminutes (Number 115) sub-titled "Finding Passion for Software". Now I've not finished listening to this yet, but something about the early topics in the conversation made me want to post.

I hang in the DevExpress "IDE Tools" Forums (way more that my wife would like) helping anyone who wanders in with a question.

But it occurs to me that when learning a new Tech of any kind, sometimes there'll be a question or 2 that seem like the answer should be really obvious and so people decide not to ask for fear of looking foolish.

This is a great shame.

I want to make one thing abundantly clear...

...There is no such thing as a stupid question, only stupid answers (which btw you should tell me about, it's the only way I'll learn :P)
...I'm happy to help - Do you have a question regarding CodeRush, RefactorPro or the Black Magic that is the DXCore?

Seriously.. ask me....

Do you want to know...
...How to make a template to do X?
...How to add a "Selection Embedding" to CodeRush?
...What the F*** is a "Selection Embedding"? :P
...How to write a plugin to do X? (<- I really like this question. The last few times it was asked, I replied with fully functional example. I usually throw them up on The Community Plugin Site)
...Why doesn't X work?
...Is X a bug or am I just not understanding the idea behind Y?
...How do I write a Refactoring of my own?
...What is a Refactoring anyway?
...What is a "Code" Item?
...Can I create those too?
...How does "Code" Items differ from "Refactor" Items on that clever SmartTag menu thingy?
...What SmartTag Menu thingy? What the F** are you talking about?
...Are you doing drugs you sound a little insane? (Only Alcohol and Caffeine btw )
...What is a StringProvider?
...What is a TextCommand?
...What is a Field? (In the CodeRush sense)
...How many Experimental plugins are you playing with? (I have projects started for at least 8)

There are many questions you can ask. Like I said *none* of them are stupid.

Ok perhaps there is.... if you say "How do I write a plugin to do X?" without replacing *X*, then yeah, that's stupid. :P
But it takes that level of question to reach "Stupid"

The best place to ask me a question by far, is in one of the DevExpress "IDE Tools" Forums, but I will accept questions in the comments here or you can email me at RoryBecker[at]Gmail[dot]com.

I work 9-5 GMT(BST at the moment) and so can be a little slow to answer during this time but I'll try anyway.

Don't think that because you're a CodeRush/RefactorPro/DXCore newbie, that this is a bad thing.

Newbies are cool! They challange me to re-explore the stuff I thought I already knew. It's always worth doing that as more often than not I discover something I didn't know before. After all the IDE Tools are a moving target because DevExpress keep making them better :)

So go ahead..... Ask me a question. :)


Wednesday, June 25, 2008

Seth Rowe Blogs

Yup, Seth Rowe.  MVP who frequents the "microsoft.public.dotnet.languages.vb" newsgroup amongst others has started a blog.

You can find it here. I for one have already added his feed to my reader.


Tools

Some tools I use:

Dev Tools:

CodeRush - If you haven't tried this then you should $249 (when I last checked) for mere mortals, this wonderful tool is also available for FREE if you're an MVP and although not widely known, it includes RefactorPro in the price.

RefactorPro - By far the easiest way to refactor your code. Simple interface, quick to operate. A joy to use. $99 if you don't want CodeRush (you mad fool you :P)

DXCore Community Plugins (GoogleCode Binaries) Some open source plugins for DXCore/Refactor/CodeRush which myself and some other community members have been playing about with.

Reflector - For anyone who's curious how stuff works under the hood

Expresso - To say this is a WYSIWYG Regular expression tool does it a great disservice.

SketchPath - As above but for XPath expressions

MyGeneration - Free Code Generation Tool.

 

Other Tools:

Windows Live Writer - I'm writing this post using this wonderful tool. So easy.

OmeaPro - My nntp/ rss reader of choice.

Twhirl - My Twitter Client of choice. Follow me at twitter.com/RoryBecker

AutoHotKey - Windows automation on steroids.

NiftyWindows - AutoHotKey derivative program with many gestures and hotkeys prearranged.

Unlocker - Don't you just hate it when the file you need to delete/move/alter is locked


Thursday, June 05, 2008

Testing Refactorings - Part 2

In this post I will endeavour to explain how to test that your newly crafted refactoring affects your code in manner which you intend it to.

This is going to be a extension of the code supplied in a previous post: How to Test a Refactoring

We are going to add an additional test to our CreateStubForhandler tester to determine if the refactoring operates correctly for a given use case.

For this we have to supply the testing plugin with samples of code which indicate the desired 'before' and 'after' states of the refactoring.

Execute_1.vb looks like this
-------------------------------------------------------------
Namespace Test
    Public Class TestClass
        Public Sub TestMethod()
            Dim X As New System.Windows.Forms.Button
            AddHandler X.Click, AddressOf X_Click<<:caret:>>
        End Sub
    End Class
End Namespace

Namespace Result
    Public Class TestClass
        Public Sub TestMethod()
            Dim X As New System.Windows.Forms.Button
            AddHandler X.Click, AddressOf X_Click
        End Sub

        Private Sub X_Click(ByVal sender As Object, ByVal e As System.EventArgs)
            Throw New System.NotImplementedException()
        End Sub
    End Class
End Namespace
-------------------------------------------------------------
The Build action of this file should be set to Embedded Resource rather than Compile.

image

Then some calling code is necessary.
Initially, I went with the following:
-------------------------------------------------------------
    <Test("Test Execute 1", RESOURCE_PATH, "Execute_1.vb")> _
    Public Sub TestExecute_1(ByVal sender As Object, ByVal ea As TestEventArgs)
        AssertExecute()
    End Sub
-------------------------------------------------------------
But I found that my test would always fail, despite actually producing the correct results in the field (so to speak).

So what was wrong?

I traced into the code as it was running and quickly surmised what was happening.

The original refactoring uses a TargetPicker to allow the user to select a new location for the generated code to appear. The Test had no way of using this UI component to pick a location for the code to go and so effectively the test comparison was done immediately after launching the TargetPicker but before any changes had been made. In fact no changes are made during the course of this test because as things stand, the TargetPicker is never accepted.

So what to do.. Well I figured, let's ask the experts....

So one quick mail to 'da man' (that would be AlexZ) later, and everything became much clearer.

It seems that the big secret to coping with the TargetPicker is quite simple... Turn it off :)

Seriously...The given example was "Extract Method", wherein the user is given a configurable option to either use the picker or have Refactor Insert the extracted Code above or below the source method.

Well unfortunately, CreateStubForHandler has no such option.... So I created one. :) The latest code (revision 81 or better) include just such an option page for this setting.

So now that we have an option that can be set.... We need a way for our Test to set it.

This it turns out is the duty of a new class. DevExpress provide a class 'RefactoringHelper' which we inherit from to create 'CreateStubForHandlerLocalHelper'. It is an instance of this class that we pass to AssertExecute in the following manner.
-------------------------------------------------------------
    <Test("Test Execute 1", RESOURCE_PATH, "Execute_1.vb")> _
    Public Sub TestExecute_1(ByVal sender As Object, ByVal ea As TestEventArgs)
        AssertExecute(New CreateStubForHandlerLocalHelper())
    End Sub
-------------------------------------------------------------

So what goes into 'CreateStubForHandlerLocalHelper' then?

Well given the assumption of a few fields not mentioned, and the following utility code:
-------------------------------------------------------------
    Private Const Section As String = "OptionsCreateStubForHandler"
    Private Const POS_AFTER As String = "After"
    Private Const POS_BEFORE As String = "Before"
    Private Const KEY_DefaultPosition As String = "DefaultPosition"
    Private Const KEY_UseTargetPicker As String = "AllowTargetPicker"

    Private Shared Sub SaveSettings(ByVal Storage As DecoupledStorage, ByVal PositionSetting As String, ByVal AllowUserModifications As Boolean)
        Call Storage.WriteString(Section, KEY_DefaultPosition, PositionSetting)
        Call Storage.WriteBoolean(Section, KEY_UseTargetPicker, AllowUserModifications)
    End Sub

    Private Shared Sub LoadSettings(ByVal Storage As DecoupledStorage, ByRef DefaultPosition As String, ByRef UseTargetPicker As Boolean)
        DefaultPosition = Storage.ReadString(Section, KEY_DefaultPosition, POS_AFTER)
        UseTargetPicker = Storage.ReadBoolean(Section, KEY_UseTargetPicker, False)
    End Sub
-------------------------------------------------------------

All that this class really does, is override the 'UpdateOptions' method and use it to set the options correctly.
-------------------------------------------------------------
    Protected Overrides Sub UpdateOptions()
        MyBase.UpdateOptions()
        Using Storage As DecoupledStorage = GetStorage(PAGE)
            ' Load Existing setting into memory for later.
            Call LoadSettings(Storage, mDefaultPosition, mUseTargetPicker)
            ' Change setting for our own purposes.
            Call SaveSettings(Storage, "After", False)
        End Using
    End Sub
-------------------------------------------------------------
This method is called by the framework prior to the execution of the test, so that the use of the TargetPicker is abandoned and the new code is created "After" the original method just as required.

Et Voila... The test passes.

One final note.... As all good programmers, we really should clean up after ourselves. Shouldn't we?

What do we do about the fact that we just ransacked the main setting of this refactoring just so that we can run the test?

Where/How do we set the setting back to it's original value?

I searched high and I searched low but could not find a RestoreSetting method anywhere on the RefactoringHelper class.

It turns out though that the method we're looking for is called 'AfterExecute'. This method is called after execution of the refactoring but before the comparison.

The main purpose of this method, as I was introduced to it, it to allow you a suitable method in which to "SendKeys" characters into the IDE for testing things like "Rename" where more complex user input is required (see code below).
-------------------------------------------------------------
Protected Overrides Sub AfterExecute()
    CodeRush.Test.SendString(ActiveView.Handle, "SomeCharacters")
End Sub
-------------------------------------------------------------

But it's position within the Test lifecycle makes it a perfect place to restore those settings thus:

-------------------------------------------------------------
    Protected Overrides Sub AfterExecute()
        MyBase.AfterExecute()
        Using Storage As DecoupledStorage = GetStorage(PAGE)
            ' Restore Settings
            Call SaveSettings(Storage, mDefaultPosition, mUseTargetPicker)
        End Using
    End Sub
-------------------------------------------------------------

I am sorry this post was such a long time in coming. I can only claim that things at work are rather busy leaving less than an ideal amount of time for works such as this.

However I will keep going one way or the other.

Happy (plugin) coding.

Please feel free to email any questions to me (RoryBecker [at] Gmail [dot] com) or in the comments to this post.