As I mentioned in Formatting HTML code snippets with Ten Ton Wrecking Balls, copying code to your clipboard in Visual Studio is often an excercise in futility if you want anything more than plain vanilla text. VS copies code to the clipboard with bizarro-world RTF formatting instead of the sane, simple HTML markup you might expect. This is true even of the brand spanking new VS.NET 2005.
I previously developed a macro that converted highlighted code to simple HTML on the clipboard using two different methods. I've since removed the Word interop method entirely because it's clunky. And I have improved the RTF-to-HTML conversion method substantially. Take this code, for example:
Let's highlight the code execute the FormatToHtml.Modern macro, and then paste the contents of the clipboard into something like FreeTextBox:
That's extra clean, well-formatted <span> colored HTML wrapped in a simple <div>. It preserves the color scheme and indentation from your IDE exactly*, although it does substitute a standard monospace IDE font. View source on this post to see the raw markup.
Now compare this with the craptacular results you'll get when you do a traditional copy and paste! This is how VS.NET 2005's CTRL+C copy functionality should behave. You could even map the CTRL+C shortcut to the macro if you like.
My favorite new feature, however, is that the macro now dynamically removes excessive indenting from copied code. That makes it a lot cleaner when copying code snippets from the TotallyUnnecessaryNamespace namespace. As Cartman would say, super sweet. And it works in Visual Studio 2002, 2003, and 2005. Try it yourself!
Download the FormatToHtml macro (5kb) Updated 4/2006
Here's how to get started with this macro
* Background colors are lost, but that's because the RTF markup VS.NET places in the clipboard doesn't contain the background colors, either. Total bummer.
Posted by Jeff Atwood View blog reactions
« Google search VS.NET macro Avoiding Booleans »
Here's a plugin that is quite awesome..
http://www.jtleigh.com/people/colin/blog/archives/2004/10/copysourceashtm.html
Right, this is the XCOPY and no-UI version of that add-in.
- no pesky installation
- no dialogs prompting you for information
it "just works". In other words, this is the way copy and paste should frickin' work in VS.NET 2005. But it doesn't.
Jeff Atwood on October 27, 2005 05:33 PMAlso, one criticism of the code that add-on is based on:
> Also, one minor niggle I've had with the Manoli formatter is that the xml comments tags and the triplle slashes (such as /// ) should be gray to mimic VS.NET instead of all green. How hard would it be to fix that?
I preserve the color scheme *EXACTLY* as VS.NET places it in the clipboard.
Plus I remove excessive indentation, which is great when you're copying one method out of a class.
And I work with a single mapped hotkey, just like CTRL+C does.
So there's your differentiation ;)
Jeff Atwood on October 27, 2005 05:38 PMAWESOME! Thanks for sharing this wonderful snippet.
The only thing that I changed was instead of:
Private Const CodeFontName As String = "Courier New"
I've put:
Private Const CodeFontName As String = "fixed-width"
That way it'll use whatever fixed-width font is set as the default instead of horrible Courier New.
Also, I think I may be missing some necessary references. (I had to add System.Drawing and System.Web) Where is EnvDTE80? Is that for VS.2005?
Thanks!
Travis on October 27, 2005 05:59 PM> I had to add System.Drawing and System.Web
Yep, you'll need those. The install procedure is documented in the earlier post:
http://www.codinghorror.com/blog/archives/000319.html
> Where is EnvDTE80? Is that for VS.2005?
Yeah, it's for VS.2005; it isn't required for VS.2003.
> Private Const CodeFontName As String = "fixed-width"
I agree, that's what it should be by default! I'll make that change..
Jeff Atwood on October 27, 2005 06:11 PM> Private Const CodeFontName As String = "fixed-width"
Actually, "fixed-width" doesn't work. but this does:
http://www.zvon.org/xxl/css1Reference/Output/property_font-family.html
Private Const CodeFontName As String = "monospace"
Jeff Atwood on October 27, 2005 06:20 PMExcellent..
Jake on October 27, 2005 08:30 PMI just updated the macro to..
- remove leading whitespace from the Text as well as the Html
- use the "monospace" generic font family instead of hard-coding "Courier New"
- fixed a bug with leading whitespace removal for HTML/XML document types
It works amazingly well as a generic replacement for copy when the macro is bound to the standard CTRL+C shortcut.
I also tested it with XML and HTML document types and it now works just as well as with VB.NET and C# code. It's fairly generic.
Jeff Atwood on October 27, 2005 09:01 PMJeff,
CopySourceAsHtml hasn't used Manoli's formatter for almost a year. The latest version (1.2.4, July 04, 2005) allows you to
- copy with (Copy...) or without (Copy) the copy preferences dialog,
- configure which actions are shown in the edit and context menus,
- bind actions to hotkeys,
- remove indentation,
- copy single-byte, multi-byte, and Unicode characters,
- copy line numbers with configurable starting line number,
- copy background colors, line number colors, syntax highlighting colors, etc exactly the way VS.NET does,
- override font, size, generated CSS, etc,
- and so on.
Have a look at the release page and try playing with a recent version:
http://www.jtleigh.com/people/colin/software/CopySourceAsHtml/
Cheers,
Colin
Colin on October 28, 2005 12:28 PMIt's a great add-in (which I referenced in the comments to my original post), but it's still an external add-in; all other things being equal, I prefer a lighter weight IDE macro that does the same thing. Easier to set up, easier to modify, more discoverable, etcetera.
A couple observations on differentiation:
1) My macro works for XML, HTML, and every other IDE filetype, in VS.NET 2003 and VS.NET 2005. Yours only works for C# or VB.NET code in VS.NET 2003.
2) You *aren't* copying background colors. Try setting a background color (eg, for a string); it won't show up in the resulting HTML. That's the same problem I have, and it's because the RTF inserted in the clipboard doesn't have that information.
If you could come up with a way to truly retain background colors, I'd definitely be inclined to use your solution ;)
Jeff Atwood on October 28, 2005 01:04 PMJeff,
1. Versions 1.2.0 and higher parse the RTF generated by VS.NET and can copy anything VS.NET can highlight.
2. Versions 1.2.1 and higher get font and color information from VS.NET
Application > Properties > FontsAndColors > TextEditor > FontFamily
Application > Properties > FontsAndColors > TextEditor > FontSize
Application > Properties > FontsAndColors > TextEditor > FontsAndColorsItems > Plain Text
Application > Properties > FontsAndColors > TextEditor > FontsAndColorsItems > Line Numbers
and use it to supplement the font and color information from the RTF. They can indeed copy background colors. The release page has an example with some ugly colors:
http://www.jtleigh.com/CopySourceAsHtml/
3. I haven't released a version for VS.NET 2005 yet, but that's not because it'll require massive changes, it's because I'm lazy.
Now, to be fair, your macro does has advantages over CopySourceAsHtml:
1. It doesn't need to be installed.
2. It already works with VS.NET 2005.
3. It probably works under VPC. I meant to fix this problem, but... uh... well...
4. You're less lazy than I am.
But you should still grab the latest version and try it out. :)
Cheers,
Colin
Colin on October 28, 2005 01:39 PM> your macro does has advantages over CopySourceAsHtml
Well, it's a Macro. Different strokes. I do like that it's a lighter weight solution (IMO)
> But you should still grab the latest version and try it out. :)
But I did try it out! That's what my last comments were based on.
> Versions 1.2.1 and higher get font and color information from VS.NET
I previously installed the latest version 1.2.4 and I definitely didn't get background colors for strings or numbers (the only two background color settings that I use).
Jeff Atwood on October 28, 2005 02:16 PMThat's not good. I was particularly proud of those background colors. I'll have to take some time next week to figure out why it's not working, solve the VPC problem (it's really the threading problem you work around in your macro, but I've only ever seen it manifest itself under VPC), and make a VS.NET 2005 version. :)
Colin on October 28, 2005 02:46 PMI've changed some parts of your code to clean up the rendered html.
Why don't you specify a black fore color like you've done for the background-color ?
By doing so, you could remove all "span color:black" from your code (and the "span cleaner"), considering text will use the div's color.
Am I wrong ?
Styx31 on November 14, 2005 06:48 AM> By doing so, you could remove all "span color:black" from your code (and the "span cleaner"), considering text will use the div's color.
This is used to "reset" the color from whatever it was back to the default. Conversion from RTF to HTML means directly replacing RTF tags with HTML tags. And since RTF tags *don't* have a strict begin/end relationship like those of HTML, it can get a little squirrelly!
> Am I wrong ?
No, I think that's a great suggestion. I tried it in the source, and miraculously, it works. I was afraid there would be mis-matched SPAN tags, but they all match.
I'll update the source! (EDIT) I spoke too soon. When I tried this on more complex code snippets, I got mismatching SPAN tags. I'm doing a blanket replacement of RTF tags, and that only works if each element is replaced. If I "skip" replacing the cf0 (black) tags, you end up with mismatching SPAN tags and invalid HTML.
Oh well.
Jeff Atwood on November 14, 2005 01:33 PMI found and liked this macro especially as it's based on the IDE configuration and not a list of keywords, making it truly "WYSIWYG" in that sense.
However, I have problems with the HTML clipboard object it creates. It works fine in that FreeTextBox page, but not in a random text editor you're using to make web pages? This really makes it less convenient than it could have been for me.
I can understand why one would find a HTML object to be nice, but for my uses, I wouldn't mind it simply being copied as a text object as HTML code. Right now when I paste it into e.g. Notepad, EditPlus, or TopStyle Pro, I just get plaintext as if the macro didn't do anything. :-(
Jug on November 19, 2005 05:46 PMI might add that this HTML object mode is still very useful for the tools that support it though (I found OpenOffice.org to do), so by no means do I wish the feature should be *exchanged*. :-/ Probably more WYSIWYG HTML editors do as well, but "text" HTML editors may apparently not.
Jug on November 19, 2005 05:52 PM> Right now when I paste it into e.g. Notepad, EditPlus, or TopStyle Pro, I just get plaintext as if the macro didn't do anything. :-(
It's trivial to edit the macro to do this. Simply change the line..
SetClipboardHtml(RtfToHtml(rtf), text)
To..
Dim html As String = RtfToHtml(rtf)
SetClipboardHtml(html, html)
When I try to build this macro, it shows me some errors. It does not know Drawing class and HttpUtility is not a member of Web.
I try to import System.Drawing but can not find it when I type dot behinds System. And I already import System.Web.
How can I fix it? My VS 2005 only installs C#, not VB.NET.
t800t8 on December 3, 2005 01:07 AM> How can I fix it?
You have to use the "Add Reference" menu. Add references to System.Drawing and System.Web.
Jeff Atwood on December 3, 2005 02:54 AMHey Jeff,
Just tried this out, and there seems to be a problem with bold text in the code. I have VS.NET setup to use Bold for types and they all get mangled:
Here's an example (picked out of FreeTextBox):
static {\b WebStoreConfig} _Configuration;
// *** Some worker values
public static {\b DateTime} EmptyDate = {\b Convert}{\b .ToDateTime(}"01/02/1900");
public static {\b DateTime} DateCompare = {\b Convert}{\b .ToDateTime(}"01/03/1900");
Looks like this should be easy to fix though - I'm too tired tonight to deal with it here though :-}
Good catch Rick "Mr. Bold" Strahl.
Clearly I never tested that.. so, here's the fix. Add this at line 310 of the macro:
'-- map bold
rtf = Regex.Replace(rtf, "{\\b\s([^}]+?)}", "<b>$1</b>")
I'll upload an updated version tomorrow.
Jeff Atwood on December 3, 2005 05:49 AMThanks Jeff,
That fixes things - well from your end :-}. It looks like VS.NET is not generating proper RTF from the code, because the bolding is incorrect in many places - or at least it doesn't match what I see in the code editor. I see the same thing if I paste into Word.
sigh - I guess I'll turn off my bolding for types. I've gotten really used to that little bit of highlighting to be able to quickly see all the objects involved.
Rick Strahl on December 3, 2005 11:06 PMThis one rocks, Jeff. Thanks!
Scott Allen on December 4, 2005 06:32 PM> because the bolding is incorrect in many places - or at least it doesn't match what I see in the code editor. I see the same thing if I paste into Word.
This would not surprise me one bit, as the RTF lacks proper background color information as well.
If only we could go back and time and open a bug on this, since it's been broken in exactly the same way all the way back to VS.NET 2002.
Jeff Atwood on December 5, 2005 04:15 AMThanks Jeff, this code works wonderfully. However, I found that the ASP.NET forums are eating up the colorized spans. I hacked your code a bit to emit FONT color="" tags instead of spans and it works sweet.
Danny on December 6, 2005 05:58 PMNice work.
I'm trying to do something similar that's frustrating the heck out of me. I want to programmatically (vb) copy an html table from an asp.net web page into Word or Powerpoint. It sure sounded easy when I started, but so far nothing works. I can get the html of the htmltable object, but pasting it from the clipboard into the receiving app just gives me raw html code instead of a table. I can't find a single resource on the internet to help! If anyone has a solution please email me. Thanks.
Texrat on December 28, 2005 04:57 PMNot sure why, but I get an exception at line 136 where the code sets the clipboard data:
An unhandled exception of type 'System.Runtime.InteropServices.ExternalException' occurred in Unknown Module.
Additional information: Requested Clipboard operation did not succeed.
I'm running VS 2005 in a virtual machine and I have IE 7 on my host machine. Any ideas?
Add this to also map non-standard characters (like the Swedish едц).
'--- ADD BEFORE "map bold"
'-- map non-standard characters (from \'e4 to ä) /Sire 2006-04-05
Dim hex As String = ""
Dim dec As Integer = 0
For Each m As Match In Regex.Matches(rtf, "\\'(?[a-f0-9][a-f0-9])")
hex = m.Groups("hex").Value
dec = Integer.Parse(hex, System.Globalization.NumberStyles.HexNumber)
rtf = Regex.Replace(rtf, "\\'" & hex, "" & dec & ";")
Next
I just updated the macro
- includes international character fix (Simon)
- added "Modern" and "Legacy" HTML methods, to accommodate HTML input boxes that strip out SPAN and DIV (Danny)
- added "*Text" methods, so you can see the HTML as plain vanilla text in your clipboard if that's how you want it (Jug)
- improved speed dramatically on large snippets (tested with entire 1600+ line classes)
I think that accommodates all the feedback so far..
Jeff Atwood on April 7, 2006 01:29 PMIf you're getting the error "Requested clipboard operation did not succeeed", try changing the second argument of Clipboard.SetText from TextDataFormat.Text (default) to TextDataFormat.UnicodeText.
Cheers,
-Richard
Richard on May 2, 2006 05:34 AMIt's useful, but it doesn't seem to be stripping all leading indentation if there is a blank line present in a block, e.g
if (foo)
{
if (bar)
{
this.DoSomething(3);
this.DoSomething(3);
}
}
will not be stripped.
Matt on March 1, 2007 06:10 AM"VS copies code to the clipboard with bizarro-world RTF formatting instead of the sane, simple HTML markup you might expect. This is true even of the brand spanking new VS.NET 2005."
Bad news: This is also true in the brand spanking new VS2008 :(
Good news: The macro still works :)
Does this work with the enhanced syntax highlighting of Visual Assist X (www.wholetomato.com)?
DaveT on March 4, 2008 10:18 AMThanks Jeff for providing this great tool! I hacked together a workaround for the background highlight color issue. Basically, I hard coded the macro to search for the ASP.NET delimiters and apply CSS styling onto them. Of the code I want to post, the ASP.NET markup is the only type that I highlight. The code is too long to post here, so feel free to copy my "solution" from http://www.alistforeverything.com/post/2008/04/Source-Code-Syntax-Highlighting-in-Blog-Posts-with-FormatToHtml.aspx#continue.
snyhol on April 24, 2008 05:38 PM| Content (c) 2008 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved. |