Writing to current document with a visual studio macro is extremely slow, suggestions to speedup?

1.3k views Asked by At

Generally I've no complaints about the speed of Visual Studio, but if I write a macro that writes out about 100 lines of code, it takes 1-2 minutes to complete.

That doesn't sound right. Is there another more convenient way to write code blocks?

For one thing, I noticed it fills the undo buffer as if I'm just typing the text by hand. Could I mimic copy/paste behavior, which is much faster?

Here's a somewhat contrived example I tried to test my case:

Sub WriteManyLines()
    DTE.Commands.Raise("{AA61C329-D559-468F-8F0F-4F03896F704D}", 2, Customin, Customout)
    DTE.Commands.Raise("{AA61C329-D559-468F-8F0F-4F03896F704D}", 5, Customin, Customout)
    Dim sb As New StringBuilder()

    For i As Integer = 1 To 100
        sb.AppendFormat("public string Method{0:000}() {{ return ""Method{0:000}""; }}", i)
        sb.AppendLine()
    Next i
    DTE.ActiveDocument.Selection.Text = sb.ToString()
End Sub
3

There are 3 answers

1
JaredPar On BEST ANSWER

This API is very deceptive in that it appears that you are replacing the selected text with the actual result of the StringBuilder. Or in short, a copy / paste. But what you're really doing is typing the result of the StringBuilder (this is why you seen the undo buffer filling up with data).

This type of behavior is true for much of the editing experience which comes from the DTE namespace. If you're interested in the esoteric details I wrote a blog experience about this general problem some time ago.

In order to fix it though you'll want to abandon DTE and get down into IVsTextLines or ITextBuffer (the latter is preferred as it's the newer managed API). To get out of DTE you should be able to execute the following

var vsTextLines = DTE.ActiveDocument.Object("TextDocument") as IVsTextLines;

Edits on an IVsTextLines will go directly to the buffer and avoid the overhead of typing.

If you want to avoid DTE and COM entirely you can use IVsEditorAdaptersFactoryService to map from the COM layer to the new 2010 managed APIs. This interface is typically queried via MEF but I believe you can also use IServiceProvider (which DTE implements) and do a QueryService call for it.

0
KungPhoo On
'' SLOOOW!
 ' DTE.ActiveDocument.Selection.Text = str
'' LIGHTNING FAST
   Dim txtSel As TextSelection
   txtSel = DTE.ActiveDocument.Selection
   txtSel.Delete()
   txtSel.Insert(str, vsInsertFlags.vsInsertFlagsInsertAtEnd)
2
sowrov On

I was facing the same problem as Abel, And thanks to JaredPar hints I was able to fix it in this way: Instead of using IVsTextLines (Which seem to be only available when you have VS-SDK installed) I just use TextDocumentand get the 'EditPoint' object from that. My code look like this:

Dim vsTextDoc As TextDocument = DTE.ActiveDocument.Object("TextDocument")
Dim epoint As EditPoint = vsTextDoc.StartPoint.CreateEditPoint
Dim strBuilder As StringBuilder = New StringBuilder

'append everything to the strBuilder and then
strBuilder.AppendLine("This is a line")
epoint.Insert(strBuilder.ToString())

And Now it feel like lighting fast compare to whatever was happening before with that DTE.ActiveDocument.Selection.Text thing! Hope it will help someone in future :)