while(!(succeed=try())); RSS Feed Feed your read!


Visual Studio Macros (C# Developers) 

Tags:

How many times have you defined a block of code, such as an if statement?  You begin the statement, followed by the conditions "if ([condition(s)])".  You then follow a pattern similar to the following: 
  • Press Enter to advance a line
  • Start the block with a left brace ({)
  • Press Enter to advance a line
  • End the block with a right brace (})
  • Press Up so you're behind the left brace
  • Press Enter to add a line between the braces

I quickly grew tired of repeating that bulleted list every time I wanted to define a block of code (classes, properties, methods, if statements, switch statements, using statements, etc.).  I found a fast, and relatively easy way to automate this task for me: Visual Studio Macros.  (Note: the reason for the "relatively" part is due to the VB.NET language requirement for macros)

This is the code I wrote to define a block for me:

Sub AutoBrace()

    Dim sel As EnvDTE.TextSelection

 

    sel = DTE.ActiveWindow.Selection

 

    DTE.UndoContext.Open("Auto Brace")

 

    Try

        sel.EndOfLine()

        sel.NewLine()

        sel.Unindent()

        sel.Text = "{"

        sel.EndOfLine()

        sel.NewLine()

        sel.Unindent()

        sel.Text = "}"

        sel.LineUp()

        sel.EndOfLine()

        sel.NewLine()

    Finally

        DTE.UndoContext.Close()

    End Try

End Sub

Since my attempt to automate block definition turned out so well, I decided to take a stab at automating another repetative task I routinely performed: region directives.

In general, I like to use region directives around methods, properties, etc. for outlining.  Again, I figured that there had to be a way to automate this.  This macro proved to be quite a bit tougher than the last one, but I was able to automate the task.

#region SomeMethod

public void SomeMethod()

{

    ...

}

#endregion

This is the macro that I wrote to handle generation of region directives around the code element that the cursor was within.  Basically, it finds the inner-most code element that the cursor is in, locates the start and end points, and wraps them in a #region directive.

Sub AutoRegion()

    Dim sel As EnvDTE.TextSelection

    Dim selPoint As EnvDTE.EditPoint

    Dim editPoint As EnvDTE.EditPoint

    Dim elem As EnvDTE.CodeElement

 

    sel = DTE.ActiveWindow.Selection

    selPoint = sel.ActivePoint.CreateEditPoint

    editPoint = sel.ActivePoint.CreateEditPoint

    elem = GetCurrentCodeElement(editPoint)

 

    DTE.UndoContext.Open("Auto Region")

 

    Try

        editPoint.MoveToPoint(elem.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes))

        editPoint.LineUp()

 

        Do While IsCommentLine(editPoint, "///")

            editPoint.LineUp()

        Loop

 

        editPoint.EndOfLine()

        sel.MoveToPoint(editPoint, False)

        sel.NewLine()

        sel.Text = "#region " + elem.Name

        editPoint.MoveToPoint(selPoint)

        editPoint.MoveToPoint(elem.GetEndPoint(vsCMPart.vsCMPartWholeWithAttributes))

        sel.MoveToPoint(editPoint, False)

        sel.NewLine()

        sel.Text = "#endregion"

        sel.MoveToPoint(selPoint)

    Finally

        DTE.UndoContext.Close()

    End Try

End Sub

The first issue I ran into was how to find the inner-most element at the cursor location.  The best method of doing this, at the time, was to step through a list of possible elements, returning the first that didn't throw an exception (dirty, I know, but I already felt a little dirty working in VB.NET anyway).

Private Function GetCurrentCodeElement(ByVal editPoint As EnvDTE.EditPoint) As EnvDTE.CodeElement

    Dim elem As EnvDTE.CodeElement

    Dim types As vsCMElement() = _

        {vsCMElement.vsCMElementProperty, _

        vsCMElement.vsCMElementFunction, _

        vsCMElement.vsCMElementEnum, _

        vsCMElement.vsCMElementInterface, _

        vsCMElement.vsCMElementStruct, _

        vsCMElement.vsCMElementClass}

    Dim type As vsCMElement

 

    For Each type In types

        elem = GetCodeElement(editPoint, type)

        If Not elem Is Nothing Then

            Exit For

        End If

    Next

 

    GetCurrentCodeElement = elem

End Function

 

Private Function GetCodeElement(ByVal editPoint As EnvDTE.EditPoint, ByVal type As vsCMElement) As EnvDTE.CodeElement

    Dim elem As EnvDTE.CodeElement

 

    Try

        elem = editPoint.CodeElement(type)

    Catch ex As Exception

        elem = Nothing

    End Try

 

    GetCodeElement = elem

End Function

I also wanted the macro to be able to handle XML comment tags for the target code element as well.

Private Function IsCommentLine(ByVal editPoint As EnvDTE.EditPoint, ByVal linePrefix As String) As Boolean

    Dim lineText As String

    Dim prefixText As String

 

    EditPoint.StartOfLine()

    lineText = EditPoint.GetText(EditPoint.LineLength).Trim

    prefixText = linePrefix.Trim

 

    If lineText.Equals(prefixText) Or lineText.StartsWith(prefixText) Then

        IsCommentLine = True

    Else

        IsCommentLine = False

    End If

End Function

I have attached a zip containing the source file here.  Happy automating!

* edited to correct spelling mistake (consicions -> conditions)

 
Posted by Jim Baltzell-Gauthier on 17-Jul-07
7 Comments  |  Trackback Url  |  Link to this post | Bookmark this post with:        
 

Comments


Something more... commented on Tuesday, 17-Jul-2007
Hi. Great stuff. How do you use this macros? Do you setup a shortcut? Isn't any way they can be called automatically by the IDE?


Re: Visual Studio Macros (C# Developers) commented on Tuesday, 17-Jul-2007
I have these two macros mapped to keyboard shortcuts. This can be done using Visual Studio's keyboard mapping interface (Tools -> Options -> Environment -> Keyboard). I find it easiest if I filter the commands by entering "macro" in the box labeled "Show commands containing". I believe user created macros end up in Macros.MyMacros. Once you select the command (macro) that you want, place your cursor in the box labeled "Press shortcut keys". Then just press whatever shortcut you want to be assigned (I use Ctrl + [ for AutoBrace and Ctrl + r for AutoRegion).


Don't need the macro commented on Tuesday, 17-Jul-2007
* Press Enter to advance a line * Start the block with a left brace ({) * Press Enter to advance a line * End the block with a right brace (}) * Press Ctrl+Enter to push the right brace line down and leave the cursor on the new line between the braces. - Jason http://www.codeassassin.com/


Don't need the macro commented on Tuesday, 29-Apr-2008
Jason's approach would work but the whole point of macros is to avoid repetitive work ( 5 keystrokes per his approach).


Alexzander commented on Thursday, 24-Jul-2008
6918a5be439d5096e8054e591d20dc08 http://njdokj.info/dc6c76c1559a9c4a33095fa45e1c4deb/6918a5be439d5096e8054e591d20dc08 http://njdokj.info/dc6c76c1559a9c4a33095fa45e1c4deb/6918a5be439d5096e8054e591d20dc08 [url]http://njdokj.info/dc6c76c1559a9c4a33095fa45e1c4deb/6918a5be439d5096e8054e591d20dc08[url]


Hire C# Developer commented on Monday, 14-Jun-2010
The Post is good & about Visual Studio Macros (C# Developers).The author wanted to define a block of code (classes, properties, methods, if statements, switch statements, using statements, etc.).

Name:
URL:
Email:
Comments:

CAPTCHA Image Validation