I often need to paste a text fragment as a comment in my source code. Unfortunately I can't simply paste the text and then use the Edit-Comment Selectio command. because the Visual Studio editor - at least when working with VB.NET - tries to interpret the pasted text as code and it ruins its formatting, adds or deletes characters, and so forth. In addition to this problem, when I am preparing samples for my books I need to revise all CR-LFs in the text, to wrap longer lines, because Microsoft Press standards mandate that lines aren't longer than 92 characters. All in all, it's a real nuisance.
A few weeks ago I decided to avoid this waste of time and wrote a macro that would do the pasting and the formatting for me. It's a simple and tiny way to increase productivity, that allows me to focus on the things that really matter. If you like tidy code listings, I am sure you'll find this macro useful.
The first problem I had to solve is a limitation of the Clipboard.GetObjectData method. When invoked from a macro, this method always returns Nothing, thus I needed a different way to read the text in the Clipboard. I can surely do this with an API call or by calling a method in a separate DLL, but I thought that reading the Clipboard from a macro shouldn't be that difficult. My next attempt was based on the Paste method of the TextBox control:
' Retrieve the text in the clipboard
Dim tb As New TextBox
tb.Multiline = True
tb.WordWrap = False
tb.ScrollBars = ScrollBars.Both
tb.Paste()
Dim text As String = tb.Text
This approach works nearly always. Every now and then, in fact, the Paste method fails with a cryptic message: "Class already exists". I noticed that this error occurs only if the macro editor is open, therefore during the normal use it doesn't cause much trouble. However, once you get this error, the only way to get rid of it is by restarting Visual Studio. When I posted this first solution on my Italian blog, reader Andrea Ferendeles suggested a different approach, based on the Paste method of the TextSelection object:
' Read the text in the clipbard, through the Selection.Paste method.
Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
Dim sp As EditPoint = sel.ActivePoint.CreateEditPoint()
sel.Paste()
' Select and read the text just pasted, then delete it
sel.MoveToPoint(sp, True)
Dim text As String = sel.Text
sel.Delete()
Once I solved this problem, writing the macro was relatively simple:
Imports EnvDTE
Imports System.Text.RegularExpressions
Imports CodeArchitectsMacros
Public Sub PasteAsComment()
PasteAsComment("80")
End Sub
Public Sub PasteAsComment(ByVal lineLength As String)
Dim maxLength As Integer = CInt(lineLength)
' Read the text in the clipbard, through the Selection.Paste method.
Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
Dim sp As EditPoint = sel.ActivePoint.CreateEditPoint()
sel.Paste()
' Select and read the text just pasted, then delete it
sel.MoveToPoint(sp, True)
Dim text As String = sel.Text
sel.Delete()
' Split in lines not longer than MaxLength
Dim result As String = ""
Dim currLineLength As Integer = 0
For Each m As Match In Regex.Matches(text, "\S+\s*")
If currLineLength + m.Length > maxLength Then
result &= ControlChars.CrLf
currLineLength = 0
End If
result &= m.Value
currLineLength += m.Length
If m.Value.IndexOf(ControlChars.CrLf) > 0 Then
currLineLength = 0
End If
Next
result &= ControlChars.CrLf
' Paste the text in the code editor
sp = sel.ActivePoint.CreateEditPoint()
sel.Insert(result)
sel.MoveToPoint(sp, True)
' Comment and reformat it
DTE.ExecuteCommand("Edit.CommentSelection")
sel.SmartFormat()
End Sub
End Module
As you see, there are actually two macros. The version with zero arguments creates comments lines that are 80 characters or shorter; this is likely to be the version that you'll use more often and you may want to associate it with a keyboard shortcut. The version with one argument allows you to specify the line length and can be used only from the Command window. For example, the following command pastes the current Clipboard content as comments not longer than 60 characters:
Macros.MyMacros.UsefulMacros.PasteAsComment 60
You don't really have to type all these characters each time, because you can associate the command to an alias. using this command:
alias PasteCom Macros.MyMacros.UsefulMacros.PasteAsComment
Once you've created the alias, you can recall the macro as follows:
PasteCom 60
NOTE: In case you never wrote a macro in your programming life, this is how to proceed:
1) run the Tools-Macros-Macros IDE command (or just press Alt+F11) to bring up the Macro IDE
2) in the Macro IDE, select the MyMacros project, then issue the Projects-Add Module to create a new module that stores all your custom macros then paste the macro code inside this module. (As in previous code, most of my macros are gathered in the CodeArchitectsMacros module.)
3) Go back to Visual Studio and display the Macro Explorer window, by means of the Tools-Macros.Macro Explorer menu command (or just press Alt+F8); in the Macro Explorer window, expand the MyMacros node and then expand the CodeArchitectsMacros module
4) optionally, go to the Tools-Options dialog box to assign a keyboard shortcut to the PasteComment macro
You're now ready to test the macro. Switch to Notepad or Word or wherever the text is, copy it into the clipboard, switch back to Visual Studio, place the caret where you want to insert the comment, and run the macro. You can run the macro by double-clicking its node in the Macro Explorer window, by typing the keyboard shortcut (if you assigned one), or by typing the macro's name inside the Commands window (with or without its alias, see above).