<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Letters from the (VS) Editor</title><link>http://blogs.msdn.com/b/noahric/</link><description /><dc:language>en-US</dc:language><generator>Telligent Evolution Platform Developer Build (Build: 5.6.50428.7875)</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/LettersFromTheVSEditor" /><feedburner:info uri="lettersfromthevseditor" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>47.677471</geo:lat><geo:long>-122.121383</geo:long><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2FLettersFromTheVSEditor" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FLettersFromTheVSEditor" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2FLettersFromTheVSEditor" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/LettersFromTheVSEditor" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FLettersFromTheVSEditor" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FLettersFromTheVSEditor" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FLettersFromTheVSEditor" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FLettersFromTheVSEditor" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><item><title>Moving on</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/1tG45LYyTb0/moving-on.aspx</link><pubDate>Tue, 08 Feb 2011 04:42:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10126002</guid><dc:creator>Noah Richards</dc:creator><slash:comments>4</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10126002</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10126002</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2011/02/07/moving-on.aspx#comments</comments><description>&lt;p&gt;It's been an interesting journey, these last 3 &amp;frac12; years.  I was hired at Microsoft, after an internship the summer before my senior year, to work on a long-lead effort creating new IDE components and developer tools.  On my first day back, my boss from the internship sat me down and broke the news that that project I'd come back to work on had been canceled.  Instead, I was told we'd be putting the editor from that project into Visual Studio, which would require a fairly &lt;i&gt;small&lt;/i&gt; shim layer to get things working.  Since then, I’ve had 3 leads (Tim, Fiona, and Michael), moved offices only once (!), written a &lt;b&gt;lot&lt;/b&gt; of shims, released &lt;a href="http://visualstudiogallery.msdn.microsoft.com/site/search?f%5B0%5D.Type=SearchText&amp;f%5B0%5D.Value=noah+richards"&gt;14 extensions to the VS gallery&lt;/a&gt; with ~130,000 (non-unique) downloads, worked with a lot of other VS teams to make the editor not crash too much, helped Blend/Powershell/DynamicsAX host the editor outside of VS, wrote box selection, contributed to a number of other VS2010 editor features, fixed about 400 bugs, and collected only one “official” demerit.&lt;/p&gt;

&lt;p&gt;I feel that it's time for something new, so I've decided to pursue career opportunities outside Microsoft.  My last day here is February 18th, after which I'll be starting a new job in Kirkland.&lt;/p&gt;

&lt;p&gt;I'm not sure what will happen to this blog.  If the articles are taken down, you can the source (in markdown) for most of them on my &lt;a href="https://github.com/noahric/blog"&gt;github blog project&lt;/a&gt;.  You can also continue to find me on twitter (&lt;a href="http://twitter.com/noahsmark"&gt;@noahsmark&lt;/a&gt;), and email me (I'm noah, the domain is noahsmark.com).&lt;/p&gt;

&lt;p&gt;I'm not sure who all reads this blog, but I want to say thanks to you anyways.  One of my favorite parts of working on the editor has been working with people writing extensions for Visual Studio, so thanks for all the questions and feedback.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10126002" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/1tG45LYyTb0" height="1" width="1"/&gt;</description><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2011/02/07/moving-on.aspx</feedburner:origLink></item><item><title>Code Snippet: Ensuring a file is in a project</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/A1b7euMXgyo/code-snippet-ensuring-a-file-is-in-a-project.aspx</link><pubDate>Fri, 28 Jan 2011 18:16:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10121646</guid><dc:creator>Noah Richards</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10121646</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10121646</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2011/01/28/code-snippet-ensuring-a-file-is-in-a-project.aspx#comments</comments><description>&lt;p&gt;Every couple of weeks, I'll get a question about getting an arbitrary file on disk into a project in VS &lt;i&gt;without&lt;/i&gt; asking VS to open in a document window directly.  It seems to be pretty hard to find information for (when I needed to do it, it took a few email threads with various other teams to figure it out), so here's a code snippet I use for this.   It works by adding the file to the miscellaneous files project (if it isn't already in another project) and then returning the hierarchy/itemid for the file, which can be used with other methods involving the shell, opening documents, locking documents, etc.&lt;/p&gt;

&lt;hr /&gt;

&lt;div style="font-family:Consolas; font-size:8pt; overflow: auto; white-space: nowrap"&gt;

&lt;span style='color:gray'&gt;///&lt;/span&gt;
&lt;span style='color:gray'&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br /&gt;

&lt;span style='color:gray'&gt;///&lt;/span&gt;
&lt;span style='color:green'&gt;If the given file isn't in a project, put it in the miscellaneous files project, and return&lt;/span&gt;&lt;br /&gt;

&lt;span style='color:gray'&gt;///&lt;/span&gt;
&lt;span style='color:green'&gt;the project/itemid of the file.&lt;/span&gt;&lt;br /&gt;

&lt;span style='color:gray'&gt;///&lt;/span&gt;
&lt;span style='color:gray'&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;

&lt;span style='color:gray'&gt;///&lt;/span&gt;
&lt;span style='color:gray'&gt;&amp;lt;returns&amp;gt;&lt;/span&gt;&lt;span style='color:green'&gt;hresult (failure code)&lt;/span&gt;&lt;span style='color:gray'&gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;br /&gt;

&lt;span style='color:blue'&gt;static int&lt;/span&gt;
&lt;span&gt;EnsureFileInAProject(&lt;/span&gt;&lt;span style='color:blue'&gt;string&lt;/span&gt;
&lt;span&gt;fileName,&lt;/span&gt;
&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;IVsUIHierarchy hierarchy,&lt;/span&gt; &lt;span style='color:blue'&gt;out&lt;/span&gt; &lt;span style='color:blue'&gt;uint&lt;/span&gt; &lt;span&gt;itemId)&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IVsUIShellOpenDocument shellOpenDocument = ServiceProvider.GlobalProvider.GetService(&lt;/span&gt;&lt;span style='color:blue'&gt;typeof&lt;/span&gt;&lt;span&gt;(SVsUIShellOpenDocument))&lt;/span&gt;
&lt;span style='color:blue'&gt;as&lt;/span&gt;
&lt;span&gt;IVsUIShellOpenDocument;&lt;/span&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IOleServiceProvider unused;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;int&lt;/span&gt;
&lt;span&gt;pDocInAProject;&lt;/span&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:green'&gt;// Before we can take a lock, we need to make sure the given doc data is in a project.&lt;/span&gt;&lt;br /&gt; 

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:green'&gt;// First, ask the shell if it already is&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;if&lt;/span&gt;
&lt;span&gt;(ErrorHandler.Failed(shellOpenDocument.IsDocumentInAProject(fileName,&lt;/span&gt;
&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;hierarchy,&lt;/span&gt;
&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;itemId,&lt;/span&gt;
&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;unused,&lt;/span&gt;
&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;pDocInAProject)))&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.Fail(&lt;/span&gt;&lt;span style='color:#A31515'&gt;"IsDocumentInAProject failed, which is unexpected."&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;return&lt;/span&gt;
&lt;span&gt;VSConstants.E_FAIL;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:green'&gt;// We need to add it to the miscellaneous files project if it isn't in a project.&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;if&lt;/span&gt;
&lt;span&gt;(pDocInAProject == (&lt;/span&gt;&lt;span style='color:blue'&gt;int&lt;/span&gt;&lt;span&gt;)__VSDOCINPROJECT.DOCINPROJ_DocNotInProject)&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;var&lt;/span&gt;
&lt;span&gt;externalFilesManager = Shell.Package.GetGlobalService(&lt;/span&gt;&lt;span style='color:blue'&gt;typeof&lt;/span&gt;&lt;span&gt;(SVsExternalFilesManager))&lt;/span&gt;
&lt;span style='color:blue'&gt;as&lt;/span&gt;
&lt;span&gt;IVsExternalFilesManager;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;if&lt;/span&gt;
&lt;span&gt;(externalFilesManager ==&lt;/span&gt; &lt;span style='color:blue'&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.Fail(&lt;/span&gt;&lt;span style='color:#A31515'&gt;"Can't find the miscellaneous files project to use."&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;return&lt;/span&gt;
&lt;span&gt;VSConstants.E_FAIL;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:green'&gt;// Make sure the misc files project doesn't actually *open* the document&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;uint&lt;/span&gt;
&lt;span&gt;createFlags = (&lt;/span&gt;&lt;span style='color:blue'&gt;uint&lt;/span&gt;&lt;span&gt;)__VSCREATEDOCWIN.CDW_RDTFLAGS_MASK | (&lt;/span&gt;&lt;span style='color:blue'&gt;uint&lt;/span&gt;&lt;span&gt;)_VSRDTFLAGS.RDT_PlaceHolderDoc;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:#2B91AF'&gt;Guid&lt;/span&gt;
&lt;span&gt;emptyGuid =&lt;/span&gt; &lt;span style='color:#2B91AF'&gt;Guid&lt;/span&gt;&lt;span&gt;.Empty;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;int&lt;/span&gt;
&lt;span&gt;unusedPos;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IVsWindowFrame unusedFrame;&lt;/span&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;if&lt;/span&gt;
&lt;span&gt;(ErrorHandler.Failed(externalFilesManager.AddDocument(createFlags, fileName,&lt;/span&gt; &lt;span style='color:#2B91AF'&gt;IntPtr&lt;/span&gt;&lt;span&gt;.Zero,&lt;/span&gt;
&lt;span style='color:#2B91AF'&gt;IntPtr&lt;/span&gt;&lt;span&gt;.Zero,&lt;/span&gt;
&lt;span style='color:blue'&gt;ref&lt;/span&gt;
&lt;span&gt;emptyGuid,&lt;/span&gt;
&lt;span style='color:blue'&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span style='color:blue'&gt;ref&lt;/span&gt; &lt;span&gt;emptyGuid,&lt;/span&gt;
&lt;span style='color:blue'&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span style='color:blue'&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span style='color:blue'&gt;out&lt;/span&gt; &lt;span&gt;unusedPos,&lt;/span&gt;
&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;unusedFrame)))&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.Fail(&lt;/span&gt;&lt;span style='color:#A31515'&gt;"Couldn't add the document to miscellaneous files project."&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;return&lt;/span&gt;
&lt;span&gt;VSConstants.E_FAIL;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IVsProject miscProject;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;if&lt;/span&gt;
&lt;span&gt;(ErrorHandler.Failed(externalFilesManager.GetExternalFilesProject(&lt;/span&gt;&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;miscProject)) ||&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (hierarchy = miscProject&lt;/span&gt; &lt;span style='color:blue'&gt;as&lt;/span&gt; &lt;span&gt;IVsUIHierarchy) ==&lt;/span&gt;
&lt;span style='color:blue'&gt;null&lt;/span&gt;
&lt;span&gt;||&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ErrorHandler.Failed(hierarchy.ParseCanonicalName(fileName,&lt;/span&gt;
&lt;span style='color:blue'&gt;out&lt;/span&gt;
&lt;span&gt;itemId)))&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.Fail(&lt;/span&gt;&lt;span style='color:#A31515'&gt;"Couldn't get the misc files project or item we just added."&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;return&lt;/span&gt;
&lt;span&gt;VSConstants.E_FAIL;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;

&lt;span&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
&lt;span style='color:blue'&gt;return&lt;/span&gt;
&lt;span&gt;VSConstants.S_OK;&lt;/span&gt;&lt;br /&gt;

&lt;span&gt;}&lt;/span&gt;&lt;br /&gt;

&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10121646" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/A1b7euMXgyo" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/code/">code</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2011/01/28/code-snippet-ensuring-a-file-is-in-a-project.aspx</feedburner:origLink></item><item><title>Editor fundamentals: Text-Relative Adornments</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/Yr_hxm1lZf4/editor-fundamentals-text-relative-adornments.aspx</link><pubDate>Wed, 25 Aug 2010 17:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10053777</guid><dc:creator>Noah Richards</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10053777</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10053777</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/08/25/editor-fundamentals-text-relative-adornments.aspx#comments</comments><description>&lt;!-- Editor fundamentals: Adornments, and working with the view --&gt;

&lt;p&gt;Tagging along with the last editor fundamentals post on &lt;a href="http://blogs.msdn.com/b/noahric/archive/2010/07/08/editor-fundamentals-push-vs-pul.aspx"&gt;the pull/event model&lt;/a&gt;, today's article is the application of that model to handling &lt;strong&gt;text-relative&lt;/strong&gt; adornments.&lt;/p&gt;

&lt;h2&gt;The adornment "manager"&lt;/h2&gt;

&lt;p&gt;Within the editor, we use a fairly common pattern for structuring adornments.  There are essentially two components:&lt;/p&gt;

&lt;p&gt;First, a view creation listener (&lt;code&gt;IWpfTextViewCreationListener&lt;/code&gt;), keyed off a &lt;code&gt;ContentType&lt;/code&gt; and &lt;code&gt;TextViewRole&lt;/code&gt;.  Most general adornment providers use "text" and &lt;code&gt;PredefinedTextViewRoles.Interactive&lt;/code&gt;.  It's also handy to put your &lt;code&gt;AdornmentLayerDefinition&lt;/code&gt; here, keeping all your MEF components in one place.&lt;/p&gt;

&lt;p&gt;Second, you have an adornment manager, for which there is no interface to implement or base class to inherit.  This will be your controller (in the Model-View-Controller sense), that controls adds adornments to the adornment layer when necessary.  It's created by the view creation listener above.&lt;/p&gt;

&lt;h2&gt;Adding adornments to the layer&lt;/h2&gt;

&lt;p&gt;The obvious way to deal with adornments is, unfortunately, not the simplest or best performing.  To understand how to handle this best, you have to step back from adornment API specifically and consider how the view works in general.&lt;/p&gt;

&lt;p&gt;Much like a text buffer is some data (text, in the form of &lt;code&gt;CurrentSnapshot&lt;/code&gt;) and an event for when that data changes, a view is some data (specific to a certain &lt;code&gt;ITextSnapshot&lt;/code&gt;) and an event for when that data changes (&lt;code&gt;LayoutChanged&lt;/code&gt;).  There are other events in there, for when the viewport size changes or when horizontal scrolling happens, but we'll ignore those for now.&lt;/p&gt;

&lt;p&gt;Though it is easiest to imagine the view as an extremely tall list of lines inside a scrollable viewport, that effect is faked.  The view is only really &lt;em&gt;the lines that are currently visible&lt;/em&gt;, which it transposes when scrolling and redraws when the buffer changes.&lt;/p&gt;

&lt;p&gt;Likewise, it is best to think about adornment layers in the same way.  Though you have an essentially infinite canvas, the adornment layer should only contain adornments that are either viewport-relative (e.g. an adornment that always shows up in the top-right corner of the editor, regardless of what text is visible) or text relative (e.g. the red breakpoint marker under pieces of text).  The former can essentially ignore layout completely, but the latter is intimately tied to layout.&lt;/p&gt;

&lt;p&gt;So, the obvious way to handle adornment layers is to figure out where all the adornments should go, regardless of where they appear in the file, and push them all onto the adornment layer.  The API is generally suggestive of this, as the adornment layer has &lt;code&gt;Add&lt;/code&gt;/&lt;code&gt;Remove&lt;/code&gt; events like any other collection.  However, there are two issues with using the API in this way: performance and, well, difficulty in making it work.&lt;/p&gt;

&lt;p&gt;For performance, the issue is the same reason why the view doesn't render every line in the file all the time.  If it did so, resources would be dedicated relative to the size of the file, meaning you'd have difficulty scaling as file size increased.  If you only need to scale to the viewport height, though, you'd have a reasonable upper bound (barring zooming out).  In the abstract, also, you'd know that you are only doing exactly as much work as you need, since the only purpose of adornments is for the user to see them, and the user can only see what's currently visible in the buffer.  Besides that, you can't actually determine the correct &lt;em&gt;y&lt;/em&gt;-coordinate of a line outside of the visible region, because the view doesn't compute it (to do so, it would have to render every line in-between to add up their heights).&lt;/p&gt;

&lt;p&gt;For correctness, you need to consider how text-relative adornments work.  Though you aren't required to &lt;em&gt;add&lt;/em&gt; them in response to a layout, they will be &lt;em&gt;removed&lt;/em&gt; for you if they (1) have a &lt;code&gt;VisualSpan&lt;/code&gt; (an associated span of text) and (2) any part of that text is reformatted in the view, either because it was scrolled into view or because the underlying text was changed.  As such, you can't just throw these text-relative adornments into the view and assume they won't disappear at some point.  You &lt;em&gt;have&lt;/em&gt; to listen for &lt;code&gt;LayoutChanged&lt;/code&gt; anyway, so you may as well use that as the primary mechanism for adding adornments to the view.&lt;/p&gt;

&lt;h2&gt;Data sources&lt;/h2&gt;

&lt;p&gt;Virtually all of the built-in adornment managers work by consuming tags, via an &lt;code&gt;[Import]&lt;/code&gt;ed &lt;code&gt;IViewTagAggregatorFactoryService&lt;/code&gt;.  This carries the normal benefits of tagging and fits into the same pattern as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;LayoutChanged&lt;/code&gt; &amp;rarr; Draw adornments per line&lt;/p&gt;

&lt;p&gt;If use use a pattern like this for the tagging aspect, you'll get to share most of the complex parts of the code path (in draw per line):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TagsChanged&lt;/code&gt; &amp;rarr; Get intersecting lines &amp;rarr; Clear adornments on the line &amp;rarr; Draw adornments per line&lt;/p&gt;

&lt;p&gt;When you structure it like this, your adornment manager truly is &lt;em&gt;just a controller&lt;/em&gt;.  It acts as a pass-through for data coming from the tagger and finding an eventual home as an adornment in the adornment layer, but it doesn't store this information locally.&lt;/p&gt;

&lt;h2&gt;Layout + tagging&lt;/h2&gt;

&lt;p&gt;Here's roughly what your &lt;code&gt;LayoutChanged&lt;/code&gt; handler will look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for each line in e.NewOrReformattedLines
    for each tag in tagAggregator.GetTags(line.ExtentAsMappingSpan)
        create an adornment for the tag
        add that adornment to the visual layer
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(Where either the inner loop or the body of the inner loop are best placed in a helper method, so they can be called on &lt;code&gt;TagsChanged&lt;/code&gt; as well)&lt;/p&gt;

&lt;p&gt;The logic is fairly simple, and your manager is now acting more like a translator from a tag type to a visual.  Because you only respond to layout, you don't need to worry about adding unnecessary adornments for lines that aren't visible, and you don't need to worry about tracking buffer changes to update when text changes.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;TagsChanged&lt;/code&gt; handler will be similar:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for each tagSpan in e.Span.GetSpans(view.Textsnapshot)
    for each line in view.TextViewLines.GetTextViewLinesIntersectingSpan(tagSpan)
        remove adornments on that line
        add new adornments on that line
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you want to combine these two, you'd probably want to extract out a method for &lt;code&gt;DrawAdornmentsOnLine&lt;/code&gt; or &lt;code&gt;DrawAdornments(NormalizedSnapshotSpanCollection)&lt;/code&gt;.  At the very least, the code for creating an adornment from a tag and/or adding an adornment to the layer should be in a shared method.&lt;/p&gt;

&lt;p&gt;One last caveat: this pattern works best when your adornments don't span multiple lines.  Using these methods, if an adornment spans multiple lines, you can end up removing/adding adornments more than once, depending on how the tag spans are placed.  If your adornments are &lt;em&gt;mostly&lt;/em&gt; on single lines, it isn't something necessarily worth worrying about as there won't be other ill-effects of this method (you won't double-draw or skip drawing adornments).  If you do either know you have lots of multi-line adornments or have profiled it to see it's a performance issue, you'll want to do something a bit smarter about collecting lines into normalized spans and querying tags over those spans.  You could do that for the simple case anyway, but it's a little more complicated than is conducive for a blog post.&lt;/p&gt;

&lt;h2&gt;Other considerations&lt;/h2&gt;

&lt;p&gt;An interesting consideration is how an adornment manager should behave around outlining.  The good news is that it is easy if you use tagging and operate over lines at a time instead of using spans, e.g. use the &lt;code&gt;NewOrReformattedLines&lt;/code&gt; property on the &lt;code&gt;TextViewLayoutChangedEventArgs&lt;/code&gt;, instead of &lt;code&gt;NewOrReformattedSpans&lt;/code&gt;.  Just use the &lt;code&gt;ExtentAsMappingSpan&lt;/code&gt; property on the line instead of using the regular &lt;code&gt;Extent&lt;/code&gt;, as the example above shows.  The line's mapping span skips collapsed outlining regions, as well as any other type of adornment that is eliding text (there aren't others in the product, though extensions can provide their own).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(This is also the reason why, if you go the normalization route that I mentioned just a second ago, you&lt;/em&gt; still &lt;em&gt;want to walk and normalize the information on a line-by-line basis, rather than using the pre-normalized &lt;code&gt;NewOrReformattedSpans&lt;/code&gt;.  If you do want to go this route, convert the mapping spans to snapshot spans on the &lt;code&gt;ITextView.TextSnapshot&lt;/code&gt;, put them all in a list/enumeration of &lt;code&gt;SnapshotSpan&lt;/code&gt;, combine the result in a &lt;code&gt;NormalizedSnapshotSpanCollection&lt;/code&gt;, and iterate over that when calling &lt;code&gt;GetTags&lt;/code&gt;.  Clear as mud, I know, but it gets to be somewhat second nature once you've written a couple of these.  Just remember that the &lt;code&gt;Normalized(Snapshot)SpanCollection&lt;/code&gt; classes, methods, and static methods are a huge helper when working with collections of &lt;code&gt;(Snapshot)Span&lt;/code&gt;s)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, if you do want to display adornments for text that is hidden, just switch back to &lt;code&gt;Extent&lt;/code&gt;.  How you visualize that information will take some consideration, especially if your adornment really is underneath the text and not just an annotation in the vicinity of the text.&lt;/p&gt;&lt;/!--&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10053777" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/Yr_hxm1lZf4" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/performance/">performance</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/tagging/">tagging</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/fundamentals/">fundamentals</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/view/">view</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/adornments/">adornments</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/08/25/editor-fundamentals-text-relative-adornments.aspx</feedburner:origLink></item><item><title>Spell Checker Update - Perf bug on large C# files</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/wdcVNImdPp0/spell-checker-update-perf-bug-on-large-c-files.aspx</link><pubDate>Thu, 12 Aug 2010 17:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10049231</guid><dc:creator>Noah Richards</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10049231</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10049231</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/08/12/spell-checker-update-perf-bug-on-large-c-files.aspx#comments</comments><description>&lt;!-- Spell Checker Update - Perf bug on large C# files --&gt;

&lt;p&gt;I pushed an update (v2.22) to the &lt;a href="http://visualstudiogallery.msdn.microsoft.com/7c8341f1-ebac-40c8-92c2-476db8d523ce"&gt;Spell Checker extension&lt;/a&gt; just a little bit ago which fixes a pretty major and annoying performance issue with large C# files.  I say "fixes", but it's really just a big work around, courtesy Michael (who will post an entry on &lt;a href="http://blogs.msdn.com/b/micleh/"&gt;his blog&lt;/a&gt; &lt;strong&gt;any day now&lt;/strong&gt;.  It's interesting enough that I thought I'd share a little bit of information about performance, as it applies to this extension.&lt;/p&gt;

&lt;p&gt;For reference, if you haven't seen it, the bug manifests itself by pegging your CPU if you open a C# file of, say, over 1000 lines.  It isn't a hard value like that, being a function of how many multi-line statements (comments, strings, etc.) you have in the file, but I've yet to hit it on a file smaller than 1000 lines.&lt;/p&gt;

&lt;p&gt;First, a little digression to talk about how the extension works:&lt;/p&gt;

&lt;h3&gt;Somewhat greedy spell checking&lt;/h3&gt;

&lt;p&gt;The spell checker's overall loop is fairly simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a file is opened, scan the entire buffer for spelling mistakes.  This puts the entire buffer into a known state.&lt;/li&gt;
&lt;li&gt;When text is changed, mark lines that contain those changes as dirty.&lt;/li&gt;
&lt;li&gt;After about 500ms of idle time (e.g. not during fast typing), scan the dirty lines again for spelling mistakes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The one big design decision made here is that the spell checker is greedy; it doesn't just spell check what is visible, which would have actually escaped the bug that caused this performance issue on large C# files, as long as you weren't zoomed out or didn't have a really tall monitor.  However, doing it the greedy way means that there are a few other useful features that could be written: showing spelling errors in the error list, displaying marks next to the scrollbar for all spelling errors in the file, etc.  I'm undecided which approach is simpler, as the state management of being greedy is somewhat complicated.&lt;/p&gt;

&lt;p&gt;Anyways, because of this decision and because certain parts of spell checking are really expensive, there is another important thing to consider:&lt;/p&gt;

&lt;h3&gt;Foreground (UI) and background threads&lt;/h3&gt;

&lt;p&gt;To get around the expense of doing things greedily, the spell checker runs as much as it can on a background thread, such as the actual spell checking logic.  One interesting thing that can't run on the UI thread is the code that determines where the natural text is, meaning comments and strings in code files.  To do this, this extension consumes the same classification information (essentially syntax highlighting) that the editor uses to color text in the view.  Unfortunately, the new classification interfaces don't really define what it means to be called on a background thread.  Even if they did, though, it wouldn't matter for the built-in languages, as &lt;em&gt;nearly every (public) API in VS needs to be accessed on the UI thread&lt;/em&gt;.  There are exceptions, but they are limited and not entirely well defined in all cases.&lt;/p&gt;

&lt;p&gt;So the extension does what it can, but it has to block in small chunks on the UI thread to ask for classification information before spinning of a background thread to go check spellings on those lines.&lt;/p&gt;

&lt;h3&gt;Put them together, and what do you get?&lt;/h3&gt;

&lt;p&gt;The issue with large C# files was actually due to trying to figure out what parts of the file are comments and strings and not the spell checking itself.  As it turns out, the C# colorizer, wrapped by editor shims in order to produce classification information, had an assumption that there was really only one component consuming that information, which is &lt;em&gt;a perfectly valid assumption&lt;/em&gt; in the pre-VS2010 world.  Moreover, that interface is still the pre-VS2010 &lt;code&gt;IVsColorizer&lt;/code&gt;, so the issue is basically that the editor is breaking an unspecified/assumed contract of the old behavior.&lt;/p&gt;

&lt;p&gt;Luckily, on not-too-huge files, a cache in the C# colorizer would cover up the issue.  However, the cache only worked for so many lines, so once you have more than 256 lines of multi-line statements, you'd end up in a nasty loop where (simplified):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The spell checker asks for classification information over the entire file.&lt;/li&gt;
&lt;li&gt;All the multi-line statements in the file are marked as dirty by the C# colorizer and it sends out an event to inform consumers.&lt;/li&gt;
&lt;li&gt;The spell checker goes back through those lines and asks for classification information again (if you'd commented-out or commented-in a section of code, it needs to check those lines again).&lt;/li&gt;
&lt;li&gt;All the multi-line statements in those areas are marked as dirty.&lt;/li&gt;
&lt;li&gt;Repeat at step #3.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Work around?&lt;/h3&gt;

&lt;p&gt;Not a simple one, unfortunately: Michael hand-wrote a &lt;a href="http://github.com/NoahRic/Spellchecker/blob/master/SpellChecker.Implementation/NaturalTextTaggers/CSharp/CSharpCommentTextTagger.cs"&gt;simplified C# parser&lt;/a&gt; that understands comments, strings, and everything else.  It's a rather elegant and clean piece of code, which is pretty representative of Michael's coding style, but the most elegant solution would have been to not have to write a parser :)  One of the big promises of the new editor is that it exposes a lot of this information specifically so people don't have to do things like this.&lt;/p&gt;

&lt;h3&gt;(Extension authors) What should I do to avoid this?&lt;/h3&gt;

&lt;p&gt;It's not something to be terribly worried about, unless you have an extension doing something similar: asking to classify the entire file and responding to classification changed events by re-classifying.  If you are just consuming classification information on a lazy basis (e.g. on a line when it changes), you shouldn't run into this.&lt;/p&gt;

&lt;p&gt;The underlying bug in that colorizer has been fixed in our internal builds of the product, so it'll certainly make it in to future versions, but it won't make it into QFE form or anything like that.  After all, the only known repro is with (a now defunct version of) this spell checker.&lt;/p&gt;

&lt;p&gt;If you do run into the issue, though, please let us know, and we can try to help you out with a workaround.&lt;/p&gt;&lt;/!--&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10049231" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/wdcVNImdPp0" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/performance/">performance</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/extensions/">extensions</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/spellcheck/">spellcheck</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/michael/">michael</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/bugfix/">bugfix</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/08/12/spell-checker-update-perf-bug-on-large-c-files.aspx</feedburner:origLink></item><item><title>Q&amp;A: ITextSnapshot.GetText()</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/JWQRXPsu01s/q-amp-a-itextsnapshot-gettext.aspx</link><pubDate>Tue, 13 Jul 2010 22:25:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10037814</guid><dc:creator>Noah Richards</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10037814</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10037814</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/07/13/q-amp-a-itextsnapshot-gettext.aspx#comments</comments><description>&lt;!-- Q&amp;A: ITextSnapshot.GetText() --&gt;

&lt;p&gt;&lt;em&gt;(This is part of the &lt;a href="http://blogs.msdn.com/b/noahric/archive/tags/q_2600_amp_3b00_a/"&gt;Q&amp;amp;A Series&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This question comes from Cameron Peters, from the &lt;a href="http://blogs.msdn.com/b/noahric/archive/2010/05/04/q-a-read-only-regions.aspx"&gt;previous Q&amp;amp;A on read-only regions&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How expensive, relatively is it to call Snapshot.GetText? I have a classifier/tagger that works well with small files (under 100K), but which bogs down as the file goes beyond that, and I'm wondering if my generous use of GetText() could be part of the problem.  Many of the sample classifiers (on which I based my work), operate this way.  For instance, the SmartTag Walkthrough at http://msdn.microsoft.com/en-us/library/ee334190.aspx uses this line of code in OnLayoutChanged():&lt;br/&gt;
  &lt;code&gt;if (!snapshot.GetText().ToLower().Equals(e.OldSnapshot.GetText().ToLower()))&lt;/code&gt;&lt;/br /&gt;
  ...is there a better way to do this sort of thing?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is that you should &lt;strong&gt;never use this method&lt;/strong&gt;.  Really.  Jack, our architect, wanted to remove the method sometime after the VS2010 CTP, but we didn't want to break people already writing extensions against the editor.  Oh, and we had an editor to finish :)&lt;/p&gt;

&lt;p&gt;I'll follow up to get the sample changed so that we aren't essentially telling people to use a method that we're telling people not to use, so sorry about that.&lt;/p&gt;

&lt;h2&gt;The longer story: specificity&lt;/h2&gt;

&lt;p&gt;The more drawn out answer is that there are extremely few cases where it is a good idea to evaluate* the entire buffer in one go.  You can nearly always be more exacting about exactly which parts of the buffer you are curious about.&lt;/p&gt;

&lt;p&gt;*I do mean "evaluate".  The buffer is stored in a piece tree, and sticking it all in a single string involves the cost of walking the tree and re-assembling the string.  Not only is there a performance penalty from re-constituting the string, but a string that can hold the entire buffer also needs to be allocated in one sequential chunk, so you risk an OutOfMemoryException when contiguous free memory is getting tiny. &lt;/p&gt;

&lt;p&gt;This sample is an interesting one.  I imagine it was written this way for simplicity, as the "real" solution that I would use is slightly more complex.  Well, the solution I imagine the sample will change to is just to remove that code, but if you really wanted to a) handle layout events but b) ignore layouts caused by just case changes, I would:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In addition to listening for layout changes, listen to &lt;code&gt;ITextBuffer.Changed&lt;/code&gt; events.&lt;/li&gt;
&lt;li&gt;These events are delivered with new/old spans and text.  To be the best editor citizen you can:
&lt;ol&gt;
&lt;li&gt;Compare the span length first (since the string properties aren't created until evaluated),  1. Then compare the strings (ignore case) only if the lengths match.&lt;/li&gt;
&lt;li&gt;To be a &lt;em&gt;really&lt;/em&gt; good citizen, consider changes larger than some size to always be "real" changes.&lt;/li&gt;
&lt;li&gt;Never &lt;em&gt;ever&lt;/em&gt; write &lt;code&gt;ToLower().Equals()&lt;/code&gt;, preferring to use the ignore case comparison routine (so you don't create another copy of the entire string).&lt;/li&gt;
&lt;li&gt;Even better &amp;ndash; only look at changes that intersect spans you care about. &lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;When the layout change event comes around, see if there were any "real" changes, and only event over those changes.&lt;/li&gt;
&lt;li&gt;Also, in the layout changed handler, only respond to changes in the &lt;code&gt;NewOrReformattedLines&lt;/code&gt; (or &lt;code&gt;NewOrReformattedSpans&lt;/code&gt;) collection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of these, #2.4, #3, and #4 are all about being minimal and only doing work when absolutely necessary.  #2.1-3 are more general performance considerations that are probably less important.&lt;/p&gt;

&lt;h2&gt;What about a Span from 0 to &lt;code&gt;ITextSnapshot.Length&lt;/code&gt;?&lt;/h2&gt;

&lt;p&gt;You'll see this oftentimes when raising changed events from taggers or classifiers.  I do it in a few of my extensions and samples.&lt;/p&gt;

&lt;p&gt;The answer here is a bit less well-defined.  The editor components that consume taggers and classifiers tend to handle these situations without issue because they only look at the parts of the changed event that they care about, like what's visible on the screen currently.  This isn't true of &lt;em&gt;all&lt;/em&gt; editor components though; the outlining manager, for example, has to keep track of the entire file all the time, to handle the state of collapsed regions.&lt;/p&gt;

&lt;p&gt;In general, like most performance issues, the answer is a) wait until you actually know you have a problem, and then b) measure to figure out exactly what is causing the problem.  You may find out that something that you &lt;em&gt;expected&lt;/em&gt; to be expensive really isn't, or something you expected to be really quick really is.  For me, coming from a mostly C++ development experience in college, I'm still surprised every once in awhile that various complicated-looking pieces of managed code &lt;em&gt;aren't&lt;/em&gt; performance problems, and it's hard to break the habit of prematurely optimizing those.&lt;/p&gt;

&lt;h2&gt;...but isn't your earlier advice premature optimization?&lt;/h2&gt;

&lt;p&gt;Erg.  Yeah, sorta.  You can look at advice like this in two ways.&lt;/p&gt;

&lt;p&gt;First, you can consider only the practical outcome.  Editor extensions that handle changes in a minimal way will very likely be more efficient than extensions that don't, except in the case that &lt;em&gt;determining&lt;/em&gt; the minimal set of changes is more expensive than handling things more broadly (which is essentially why I have extensions/samples that create spans from &lt;code&gt;[0, ITextSnapshot.Length)&lt;/code&gt;).  Likewise, it's very possible that these extensions would have been fast enough with the simpler, broader approach.&lt;/p&gt;

&lt;p&gt;The other way is to think of it as a qualitatively different way of writing the extension.  In some way, it's like the difference between "polling" (is the data there yet? is the data there yet?) and listening for events.  One may be faster than the other in some circumstances, but the point is that you understand the data flow differently depending on the approach.  In the above example, handling buffer change events directly, to see which should affect a given smart tag, is a different way of thinking about the underlying problem of handling user-generated changes than trying to figure out that information at layout time.&lt;/p&gt;

&lt;p&gt;Of course, the first is more practical, so that's the one you should probably concentrate on :)  This article was really just for entertainment, then, unless you are using &lt;code&gt;ITextSnapshot.GetText()&lt;/code&gt; and it is causing performance problems.&lt;/p&gt;

&lt;h2&gt;One last thought: parsers&lt;/h2&gt;

&lt;p&gt;The place where this breaks down is when you have a parser that only works over full files, so even if you just insert a single character on a single line, it still needs to reparse the world.&lt;/p&gt;

&lt;p&gt;I know that "use a different parser" isn't really the most tractable answer, so the best you can do in that case is to throttle how often you parse.  That's an answer for a whole different post, though, so stay tuned to the blog if you are interested about that type of problem.&lt;/p&gt;

&lt;h2&gt;More questions?&lt;/h2&gt;

&lt;p&gt;If anyone has questions like this, you can always post them on the &lt;a href="http://social.msdn.microsoft.com/Forums/en-US/vseditor"&gt;Editor forum on MSDN&lt;/a&gt;, and they'll get answered.  I'll answer questions like this one on this blog, though the answers will always be long and rambling, so use the forums if you appreciate brevity.&lt;/p&gt;&lt;/!--&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10037814" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/JWQRXPsu01s" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/performance/">performance</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/snapshots/">snapshots</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/q_2600_amp_3B00_a/">q&amp;amp;a</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/07/13/q-amp-a-itextsnapshot-gettext.aspx</feedburner:origLink></item><item><title>Editor fundamentals: Push vs. Pull</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/UQY4oy7YdiA/editor-fundamentals-push-vs-pul.aspx</link><pubDate>Thu, 08 Jul 2010 17:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10036043</guid><dc:creator>Noah Richards</dc:creator><slash:comments>3</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10036043</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10036043</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/07/08/editor-fundamentals-push-vs-pul.aspx#comments</comments><description>&lt;!-- Editor fundamentals: Push vs. Pull --&gt;

&lt;p&gt;(This is a new series I plan to write in occasionally, where I'll talk more generally about some of the design fundamentals of the editor and best practices for extensions)&lt;/p&gt;

&lt;p&gt;One of the things I learned early on, probably incorrectly, is that you can look at how components communicate and basically split the common patterns into two camps.&lt;/p&gt;

&lt;p&gt;In the first, one of the components pushes data out; it knows when it has it, and it informs everyone who wants to know (by registration or just &lt;em&gt;a priori&lt;/em&gt; relationships) about said data.  This is the (roughly) the model of text messaging, where you have messages pushed out to your phone when they are sent and you push them out to other phones when you send them.&lt;/p&gt;

&lt;p&gt;In the other, components ask for data when they need it; they know when they need to consume it, and know when they need to ask.  This is the model we are all used to when browsing the web: when you want information, you go out and fetch it.  The internet doesn't &lt;em&gt;push&lt;/em&gt; data to you, for the most part.&lt;/p&gt;

&lt;p&gt;Among the latter camp, there also seems to be two primary splits; the first is "polling", which exemplifies just how annoying the English language can be.  It's pronounced exactly the same way, and the concept actually always mixes with the more general idea of "pulling" data in my brain, but it does have a specific meaning when I think about it.  In polling, the person asks for data over and over again, not necessarily knowing when it changed.  HTTP is mostly polling, though does support various caching-related headers to at least reduce the amount of polling you'd need to do to stay up-to-date with a page.&lt;/p&gt;

&lt;p&gt;Instead of that, there is also the event-driven route, where the client doesn't push the actual data, but just notifications that the data may be different in some way or the actual updated data.&lt;/p&gt;

&lt;p&gt;I'm sure there are real terms for all these, and that I've horribly misconstrued some important points.  My apologies for that.&lt;/p&gt;

&lt;p&gt;This simplified view of the world, though, is enough for me to talk about what I really wanted to talk about, and that is this:&lt;/p&gt;

&lt;p&gt;The old editor was a &lt;strong&gt;push&lt;/strong&gt; world.  The new editor is a &lt;strong&gt;pull/event&lt;/strong&gt; world.&lt;/p&gt;

&lt;h2&gt;Old editor: central storage&lt;/h2&gt;

&lt;p&gt;If you've used markers in the old editor or read the article I posted &lt;a href="http://blogs.msdn.com/noahric/archive/2009/06/06/editor-perf-markers-vs-tracking-spans.aspx"&gt;comparing markers in VS2008 to tracking spans in VS2010&lt;/a&gt;, you'll have a pretty good idea of what to expect of the various services you'd get in the old interfaces.&lt;/p&gt;

&lt;p&gt;With only a few exceptions, the editor acts like a large data repository.  Clients, usually language services, push things like markers, outlining regions, and completion items (intellisense) to the editor.  In terms of "extension points" in the old editor, mostly features that you imagine languages providing, nearly every one was some piece of information being pushed into the editor, to store and (at least partially) own.&lt;/p&gt;

&lt;p&gt;The furthest one from this would be colorization, where the editor requests that information on-demand, but even that has a central storage component, in that languages can request line state be kept by the editor for each line in the file.&lt;/p&gt;

&lt;p&gt;The API for these extension points is fairly natural and simple.  Markers are something of a one-thing-fits-all, where markers give you the ability to track changes, place adornments like breakpoints, put glyphs in the indicator margin, mark regions as read-only, and provide help and tooltips.  You figure out what things you want to be in the editor, and then you push it out to the editor.  It'll stay put until the text it marks is deleted, essentially, as the editor tracks how it moves and when it should go away.&lt;/p&gt;

&lt;h4&gt;The downsides&lt;/h4&gt;

&lt;p&gt;For many of these, then, the editor was just a glorified list, with custom collection APIs (adding, removing, iterating) and some event-like capabilities like marker clients, which are informed when markers are moved or invalidated.  But it isn't a very specialized list, being that it needs to serve fairly general needs, so the APIs are neither consistent nor very rich.&lt;/p&gt;

&lt;p&gt;One of the interesting patterns that cropped up, looking at the usage of these APIs, is that many languages tended to be storing copies of the same data.  Take outlining, for example.  Many languages created markers for outlining regions, which is not required as part of the outlining API, so they could track the way the outlining regions changed through buffer edits.  Others kept custom collections that more closely matched their language models, so it was easier to update that list as those models changed.  To update the editor outlining regions, then, they would iterate over their own collections and the editor's regions simultaneously, noting which regions needed to be added or removed.  It wasn't uncommon for some single piece of information, like the extent of an outlining region, to be stored in three or four different places.&lt;/p&gt;

&lt;p&gt;And the last issue was that there wasn't really an opportunity for lazily computing this information, as there was no hint that the editor needed to evaluate the information in a specific region of the file.  Language services were generally good enough to do work on idle as to not block the user, but there wasn't a good opportunity to avoid work that the user may not care about &lt;em&gt;just now&lt;/em&gt;.  That sounds vague, I know, but it should make more sense after explaining how the new editor does it.&lt;/p&gt;

&lt;p&gt;I'm not really sure that these issues were unavoidable, but some of them seem inherent stumbling blocks of the push approach.  By choosing between (a) forcing your data store into a component you have no design control over or (b) duplicating your data twice, you're probably going to pay in either resource usage (CPU time or memory) or difficulty in accomplishing the task at hand.&lt;/p&gt;

&lt;h2&gt;New editor: decentralization&lt;/h2&gt;

&lt;p&gt;The model in the new editor is that the only thing necessarily stored in the "editor" is the content of the text buffers.  Nearly everything else is cache, or only as much state as is needed to be coherent.&lt;/p&gt;

&lt;p&gt;Text markers, like visible breakpoints, are a good example of how this works.  Clients implement an &lt;code&gt;ITagger&amp;lt;ITextMarkerTag&amp;gt;&lt;/code&gt;, which they list as being available to the editor with a MEF-exported &lt;code&gt;ITaggerProvider&lt;/code&gt;.  The provision of the tagger is on-demand, when the editor determines that the tagger provider matches the declared content type and tag type of a buffer and the component requesting tags (usually some component that is created per-text view, also matching against content type and text view roles).&lt;/p&gt;

&lt;p&gt;A tagger has a method, &lt;code&gt;GetTags&lt;/code&gt;, and an event, &lt;code&gt;TagsChanged&lt;/code&gt;.  When something in the editor wants to know about text markers in a given area, they'll call &lt;code&gt;GetTags&lt;/code&gt;, and then assume that the information is valid until the buffer changes around that area or until the tagger raises the &lt;code&gt;TagsChanged&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The call to &lt;code&gt;GetTags&lt;/code&gt; is indirect, through an &lt;code&gt;ITagAggregator&amp;lt;ITextMarkerTag&amp;gt;&lt;/code&gt;, but that's incidental to the point.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since most view-level components only ask about lines as they show up or are dirtied in the view (I'll talk more about this in the next installment of this series), the net effect is that the tagger is only asked for areas of the buffer when the user needs that information.&lt;/p&gt;

&lt;p&gt;A lot of extension points are accessible through tagging, somewhat similar to markers, in a sense.  Some do have a bit of extra state that the editor does manage, such as outlining regions.  These are provided through tagging, but the actual collapsed state is stored in the editor, since it is managed across multiple providers and the state can be different in any number of text views over the same buffer.  But that is (literally) view-specific state, and the stuff we consider buffer-level model, the collaps&lt;em&gt;ible&lt;/em&gt; regions themselves, are owned by the taggers that provide them.&lt;/p&gt;

&lt;p&gt;There are a few other upsides to this approach, relative to the push model:&lt;/p&gt;

&lt;p&gt;API like this lends itself to lazy computation, as the extension knows &lt;em&gt;exactly when the data is needed&lt;/em&gt;.  As an example, a few people internally have played around with adornments to replace things that represent colors in source files (hex color values in CSS, &lt;code&gt;Colors&lt;/code&gt; and &lt;code&gt;Brushes&lt;/code&gt; in WPF, things like that).  Because these tend to be self-evident, these extensions don't need to walk the file or even listen to buffer changes.  All they do is respond for requests for information as they are received by scanning the text and seeing if they have any applicable information.&lt;/p&gt;

&lt;p&gt;You could accomplish this in a hacky way with the old editor markers, say by tracking what parts of the view are visible at any point in time and only pushing out information that matches that part of the view.  It would be ugly, though, and unnatural.&lt;/p&gt;

&lt;p&gt;Also, you don't need to pay to store everything twice if you decide to keep data in a format better fitting your own needs.  While the editor may keep a cache of some things, it generally isn't substantial.  The most amount of cached information is the view, which keeps visible lines around (for as long as they are visible), and visible adornments, which you can think of as the temporary manifestations of these underlying data components.&lt;/p&gt;

&lt;h4&gt;The downsides&lt;/h4&gt;

&lt;p&gt;There are downsides here, as well.  No silver bullet or magical solution.&lt;/p&gt;

&lt;p&gt;First, designing components in this way seems to come less naturally to people.  Maybe that's just because people are used to the push model of providing data, or maybe it is really because most people's mental model of data flow doesn't work like this.  It's especially common for people providing adornments to treat it as if you need to &lt;em&gt;push&lt;/em&gt; the adornment out to the view (instead of &lt;em&gt;providing&lt;/em&gt; the adornment when requested), especially since the API actively lends itself to this (with methods like &lt;code&gt;AddAdornment&lt;/code&gt; and &lt;code&gt;RemoveAdornment&lt;/code&gt; on &lt;code&gt;IAdornmentLayer&lt;/code&gt;), but I see that entirely as a problem for the editor to fix by providing more obvious APIs, better documentation, and a larger body of samples and templates.  Adornments are an especially confusing topic, which is why I'm going to cover that specifically in the next part of this series.&lt;/p&gt;

&lt;p&gt;Second, some extensions don't care about making their own data structures, and the overhead of doing so makes things less simple.  We've tried to mitigate this for tagging, unfortunately without being obvious about it, by providing a &lt;code&gt;SimpleTagger&amp;lt;T&amp;gt;&lt;/code&gt; type.  It implements the tagging interface, so you can return it from an &lt;code&gt;ITaggerProvider&lt;/code&gt;, and you can use it like a simple list.  That's still more overhead (and certainly less obvious) than an API that forces/provides a simple Add/Remove push model.&lt;/p&gt;

&lt;p&gt;Third, things like &lt;code&gt;GetTags&lt;/code&gt; tend to be moderately chatty, though not necessarily more so than the old interfaces did, since there was a decent amount of back and forth around enumerating markers/outlining regions/etc. and adding/removing them to stay in sync with external lists.  For these interfaces, the chattiness is that the editor doesn't do much caching or sharing at the tag aggregation level, so each consumer gets its own &lt;code&gt;ITagAggregator&lt;/code&gt;, and each call for tags from each consumer results in a call to &lt;code&gt;GetTags&lt;/code&gt;.  As such, &lt;em&gt;expensive&lt;/em&gt; lazy computation without any sort of caching at the tagger level isn't a great idea, as you'll pay that cost per-consumer instead of per-update.  This is something we have ideas for improving, but we're mostly waiting to see if it will become a problem in practice.&lt;/p&gt;

&lt;h3&gt;Specifics to generalizations&lt;/h3&gt;

&lt;p&gt;I'm not sure how much of this applies to the general issue of push/pull and poll/event.  After working with it for a few years, I have to say I'm pretty happy with the general outcome, and the problems that we &lt;em&gt;aren't&lt;/em&gt; having.&lt;/p&gt;

&lt;p&gt;The biggest issues we are having are essentially from the confusion department.  Interfaces are non-obvious, documentation is spotty, samples are sparse, editor bloggers are about as bright as a turnip (hey! that's not nice), MEF is a bit too far on the magical side (read: hard to debug), certain things cry out for better extension points, and the list goes on and on.&lt;/p&gt;

&lt;p&gt;The only thing I can really say to this is that I think that developing extensible applications really has to be considered an ongoing enterprise.  I don't think it would be really possible to, in the 2-4 years it took to build the new editor (depending on how you count), create an editor with feature parity with the one that has been in usage for at least the last decade &lt;em&gt;and&lt;/em&gt; design a perfect extensibility layer.  I'd also like to think that &lt;em&gt;something&lt;/em&gt; is usually better than nothing, though there is that gigantic rat's nest of compatibility.&lt;/p&gt;

&lt;p&gt;(You can't see me right now, but when I hear or even type the word "compatibility", I stick my fingers in my ears and yell LA LA LA LA until the thought goes away)&lt;/p&gt;

&lt;p&gt;I do think one of the things that helped us do as well as we did is that the extensibility API we designed wasn't really the "extensibility" API; it was &lt;em&gt;the&lt;/em&gt; API for the editor, which happens to be publicly accessible.  I'll continue to sing the praises of that story until the day men in suits come to collect me.&lt;/p&gt;&lt;/!--&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10036043" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/UQY4oy7YdiA" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/performance/">performance</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/tagging/">tagging</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/extensions/">extensions</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/pull/">pull</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/fundamentals/">fundamentals</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/push/">push</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/07/08/editor-fundamentals-push-vs-pul.aspx</feedburner:origLink></item><item><title>A quick update</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/bYjRbvtWfvM/a-quick-update.aspx</link><pubDate>Tue, 22 Jun 2010 05:11:02 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10028319</guid><dc:creator>Noah Richards</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10028319</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10028319</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/06/21/a-quick-update.aspx#comments</comments><description>&lt;!-- Title: A quick update --&gt;

&lt;p&gt;Sorry for (another) long hiatus, again.  I've been pretty busy, the last few weeks especially, working on fixing bugs and some interesting internal projects that may make their way to the public eye soon-ish (like in the next few months soon-ish, which probably isn't that soon).  &lt;/p&gt;

&lt;p&gt;In the interim, I wanted to note that I just pushed out an updated version (2.2) of &lt;a href="http://visualstudiogallery.msdn.microsoft.com/en-us/4b286b9c-4dd5-416b-b143-e31d36dc622b"&gt;Go To Definition&lt;/a&gt;.  The notable part of the change is that it moves the handling to mouse-up instead of mouse-down, meaning that it should play nicely now with word selection logic and drag/drop.  Source changes, as always, can be &lt;a href="http://github.com/NoahRic/GoToDef/commit/5939a2946fe31f5f11f924816902e16be8f4673e"&gt;viewed on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, if you are currently using VS2010 RTM and are finding issues, please &lt;a href="https://connect.microsoft.com/VisualStudio"&gt;file bugs on Connect&lt;/a&gt;.  A lot of the bugs I'm fixing right now are filed on Connect, so it's your chance to find something in VS to get changed.  You can always email me directory, through this blog, but it'll be easier to track and get others to vote on issues that you've opened publicly through Connect.&lt;/p&gt;

&lt;p&gt;Finally, completely unrelated to work: if you aren't a Microsoft employee and you'd like to get an idea of some of the strangeness that goes on here, take a gander at &lt;a href="http://www.bugbash.net/"&gt;Bug Bash&lt;/a&gt;.  Microsoft employees should take a look, too, as long as they don't mind being reminded of some of the ridiculous-ities of work.&lt;/p&gt;
&lt;/!--&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10028319" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/bYjRbvtWfvM" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/extensions/">extensions</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/gotodef/">gotodef</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/06/21/a-quick-update.aspx</feedburner:origLink></item><item><title>Q&amp;A: Read-only regions</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/BSgPbmMpr7Q/q-a-read-only-regions.aspx</link><pubDate>Tue, 04 May 2010 17:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10007224</guid><dc:creator>Noah Richards</dc:creator><slash:comments>8</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10007224</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10007224</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/05/04/q-a-read-only-regions.aspx#comments</comments><description>&lt;!-- Q&amp;A: Read-only regions --&gt;

&lt;p&gt;This question was asked recently on the (internal) editor discussion alias:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is it at all possible to make parts of the text buffer read-only? Could I, for example, mark certain spans as not modifiable, so that the user wouldn't be able to change their contents?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The short answer is &lt;em&gt;yes&lt;/em&gt;; in fact, marking regions of the buffer as read-only is the primary model of controlling the read-only-ness of the buffer, and doing something like setting the entire buffer read-only is a special case of that (marking the region that covers the entire buffer as read-only).&lt;/p&gt;

&lt;h2&gt;The general model&lt;/h2&gt;

&lt;p&gt;The API for creating read-only regions looks very much like creating regular edits.  The entry point is to create a &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.text.ireadonlyregionedit_members.aspx"&gt;read-only region edit&lt;/a&gt; (&lt;code&gt;ITextBuffer.CreateReadOnlyRegionEdit&lt;/code&gt;), which is what you use to make the actual edits of adding or removing read-only regions.  To answer the original question, here's an example of how to make an entire buffer read-only:&lt;/p&gt;

&lt;pre style='color:#000020;background:#f6f8ff;'&gt;&lt;code&gt;ITextBuffer textBuffer;&lt;/span&gt;
IReadOnlyRegion region&lt;span style='color:#406080; '&gt;;&lt;/span&gt;

&lt;span style='color:#200080; font-weight:bold; '&gt;using&lt;/span&gt; &lt;span style='color:#308080; '&gt;(&lt;/span&gt;IReadOnlyRegionEdit edit &lt;span style='color:#308080; '&gt;=&lt;/span&gt; textBuffer&lt;span style='color:#308080; '&gt;.&lt;/span&gt;CreateReadOnlyRegionEdit&lt;span style='color:#308080; '&gt;(&lt;/span&gt;&lt;span style='color:#308080; '&gt;)&lt;/span&gt;&lt;span style='color:#308080; '&gt;)&lt;/span&gt;
&lt;span style='color:#406080; '&gt;{&lt;/span&gt;
    region &lt;span style='color:#308080; '&gt;=&lt;/span&gt; edit&lt;span style='color:#308080; '&gt;.&lt;/span&gt;CreateReadOnlyRegion&lt;span style='color:#308080; '&gt;(&lt;/span&gt;&lt;span style='color:#200080; font-weight:bold; '&gt;new&lt;/span&gt; Span&lt;span style='color:#308080; '&gt;(&lt;/span&gt;&lt;span style='color:#008c00; '&gt;0&lt;/span&gt;&lt;span style='color:#308080; '&gt;,&lt;/span&gt; edit&lt;span style='color:#308080; '&gt;.&lt;/span&gt;Snapshot&lt;span style='color:#308080; '&gt;.&lt;/span&gt;Length&lt;span style='color:#308080; '&gt;)&lt;/span&gt;&lt;span style='color:#308080; '&gt;)&lt;/span&gt;&lt;span style='color:#406080; '&gt;;&lt;/span&gt;
    edit&lt;span style='color:#308080; '&gt;.&lt;/span&gt;Apply&lt;span style='color:#308080; '&gt;(&lt;/span&gt;&lt;span style='color:#308080; '&gt;)&lt;/span&gt;&lt;span style='color:#406080; '&gt;;&lt;/span&gt;
&lt;span style='color:#406080; '&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you create (add) a region, you get back a handle to the region, which you'll need to hold on to if you ever want to remove it again, like this:&lt;/p&gt;

&lt;pre style='color:#000020;background:#f6f8ff;'&gt;&lt;code&gt;&lt;span style='color:#200080; font-weight:bold; '&gt;using&lt;/span&gt; &lt;span style='color:#308080; '&gt;(&lt;/span&gt;IReadOnlyRegionEdit edit &lt;span style='color:#308080; '&gt;=&lt;/span&gt; textBuffer&lt;span style='color:#308080; '&gt;.&lt;/span&gt;CreateReadOnlyRegionEdit&lt;span style='color:#308080; '&gt;(&lt;/span&gt;&lt;span style='color:#308080; '&gt;)&lt;/span&gt;&lt;span style='color:#308080; '&gt;)&lt;/span&gt;
&lt;span style='color:#406080; '&gt;{&lt;/span&gt;
    edit&lt;span style='color:#308080; '&gt;.&lt;/span&gt;RemoveReadOnlyRegion&lt;span style='color:#308080; '&gt;(&lt;/span&gt;region&lt;span style='color:#308080; '&gt;)&lt;/span&gt;&lt;span style='color:#406080; '&gt;;&lt;/span&gt;
    edit&lt;span style='color:#308080; '&gt;.&lt;/span&gt;Apply&lt;span style='color:#308080; '&gt;(&lt;/span&gt;&lt;span style='color:#308080; '&gt;)&lt;/span&gt;&lt;span style='color:#406080; '&gt;;&lt;/span&gt;
&lt;span style='color:#406080; '&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In case you missed it, the (intentional) side-effect of this design is that &lt;strong&gt;only the "owner" who creates a read-only region can remove that region&lt;/strong&gt;.  This means that one person can mark parts of or the entire buffer read-only, thus preventing anyone else from editing it (as that person expects), without any way for other people to pull that protection out from under the original owner's proverbial feet.&lt;/p&gt;

&lt;h2&gt;To snapshot or not to snapshot&lt;/h2&gt;

&lt;p&gt;Sometime in the last year, we got reports that read-only regions were causing performance issues in general C# debugger stepping scenarios.&lt;/p&gt;

&lt;p&gt;I don't remember the exact details, but the gist is that the C# language service was setting and unsetting read-only-ness on every step.  There wasn't a simple way to determine that it was a "step" and not just a blanket "start running the debugger again", so it was a rather costly expense for what was essentially too short-lived for the user to even notice (or at least shouldn't be noticeable, assuming things are nice and fast).&lt;/p&gt;

&lt;p&gt;At the time, generating new read-only regions was slow because each read-only region edit create a new &lt;code&gt;ITextSnapshot&lt;/code&gt; for that buffer, with similar costs to making an edit.  In this case, it was setting read-only regions on more than a single file, so I think it was more like making an edit on lots of files simultaneously.&lt;/p&gt;

&lt;p&gt;The question, at the time, was, "why does the editor need to generate a new snapshot when read-only regions change?"  It's an interesting design choice, to say the least.  As I understand it (I wasn't on the team at the time, so this is hearsay), people thought there would be utility in being able to ask, "On snapshot &lt;code&gt;foo&lt;/code&gt;, was region &lt;code&gt;bar&lt;/code&gt; read-only?", to see what the read-only state of the buffer was historically, much like how snapshots let you look at the buffer contents historically.&lt;/p&gt;

&lt;p&gt;As it turns out, though, that's not especially useful information to have.  There is some symmetry there with asking what edits happened on old snapshots (technically versions), but there's isn't anything you can really &lt;em&gt;do&lt;/em&gt; with that information.  You can only edit the current version of the buffer, so the only real question people need to ask is, "On &lt;strong&gt;buffer&lt;/strong&gt; &lt;code&gt;foo&lt;/code&gt;, was region &lt;code&gt;bar&lt;/code&gt; read-only?".&lt;/p&gt;

&lt;p&gt;So, Jack changed read-only regions so that they are conceptually and literally buffer-specific instead of snapshot-specific, and most of that performance issue wasn't anymore.  There were actually other changes made, in the language service, that made this change mostly unnecessary for that specific scenario, but it was still the change that we wanted to make.&lt;/p&gt;

&lt;h2&gt;Dynamic read-only regions&lt;/h2&gt;

&lt;p&gt;The other late entry to the game was the concept of dynamic read-only regions.  This one was written by Sergei, at least in part to support (VS2008 and previous) markers, which can prevent edits over the span of text they cover.&lt;/p&gt;

&lt;p&gt;These regions are inserted and removed just like regular ol' read-only regions, with one extra argument in &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.text.ireadonlyregionedit.createdynamicreadonlyregion.aspx"&gt;&lt;code&gt;CreateDynamicReadOnlyRegion&lt;/code&gt;&lt;/a&gt;: a &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.text.dynamicreadonlyregionquery.aspx"&gt;&lt;code&gt;DynamicReadOnlyRegionQuery&lt;/code&gt;&lt;/a&gt;.  The contract for that callback is fairly simple; it's told whether or not the query ("is this read-only?") is happening as part of an edit or just as a simple request, and it returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One reason for including information about whether the request is an edit is to use this feature for something like source code integration's check out on edit feature.  You could imagine, for files that aren't yet checked-out, putting a dynamic read-only region over the file, returning &lt;code&gt;true&lt;/code&gt; when asked about read-only-ness for non-edits, and then attempt to perform the checkout on edit.&lt;/p&gt;

&lt;p&gt;This also leads to another guiding principle of read-only regions: if you need to make an edit, &lt;em&gt;never ask about read-only regions before making the edit&lt;/em&gt;.  Instead, try to make the edit and handle failure gracefully (the various methods on &lt;code&gt;ITextEdit&lt;/code&gt; return whether or not that part of the edit succeeded).  There are methods for asking about which spans in a buffer/snapshot are read-only, but you shouldn't use these if you intend to make an edit with the knowledge.  As the saying goes, never ask permission, as for forgiveness (or, in the editor's case, be prepared to deal with the punishment).&lt;/p&gt;

&lt;h2&gt;More questions?&lt;/h2&gt;

&lt;p&gt;If anyone has questions like this, you can always post them on the &lt;a href="http://social.msdn.microsoft.com/Forums/en-US/vseditor"&gt;Editor forum on MSDN&lt;/a&gt;, and they'll get answered.  I'll answer questions like this one on this blog, though the answers will always be long and rambling, so use the forums if you appreciate brevity.&lt;/p&gt;
&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10007224" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/BSgPbmMpr7Q" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/q_2600_amp_3B00_a/">q&amp;amp;a</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/read_2D00_only/">read-only</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/05/04/q-a-read-only-regions.aspx</feedburner:origLink></item><item><title>Extending extensions</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/3RVZX5kaM7c/extending-extensions.aspx</link><pubDate>Fri, 30 Apr 2010 17:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10004951</guid><dc:creator>Noah Richards</dc:creator><slash:comments>2</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=10004951</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=10004951</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/04/30/extending-extensions.aspx#comments</comments><description>&lt;!-- Extending extensions --&gt;

&lt;p&gt;If you'd ever taken a peek at the &lt;a href="http://github.com/noahric/spellchecker"&gt;code for the Spell Checker extension&lt;/a&gt; before a couple weeks ago, you may have noticed that there were some definition interfaces intermingled with the various implementation classes.  One such example is the &lt;code&gt;NaturalTextTag&lt;/code&gt;, which the spell checking logic aggregates to know what parts of a text buffer to spell check.  The extension has two defined; one for plaintext files, which says "spell check the whole file", and one for code files, which says "spell check all comments and strings".&lt;/p&gt;

&lt;p&gt;However, the real intent was that this tag could be produced by other extensions, for other languages, like determining which portions of an HTML page should be checked, or, say, Markdown files.  Essentially, the Spell Checker should be an &lt;em&gt;extensible extension&lt;/em&gt;, no different than the many other extensible things in the editor.  After all, there's no real reason to believe that the editor provides every extensibility point that you could ever need, or that there isn't a need for extensions themselves to interoperate in ways that we didn't foresee.&lt;/p&gt;

&lt;p&gt;The spell checker is a great example of this.  If spell checking was built-in, it would (hopefully) have lots of extensibility points: plugging in custom dictionaries, being able to modify behavior depending on the type of file being edited, adding new spelling actions other than the default, etc.  So why should it be any different if an extension provides spell checking?&lt;/p&gt;

&lt;p&gt;The problem, essentially, comes down to &lt;strong&gt;contracts&lt;/strong&gt;.  For the sake of simplicity, we'll just think of these as &lt;em&gt;types&lt;/em&gt;.  We need a way for these extensions to agree on types, like &lt;code&gt;INaturalTextTag&lt;/code&gt;, which means that they both need to essentially share an assembly.  Ideally, they'd also both be distributable via VSIX file, which precludes &lt;em&gt;actually sharing installed assemblies&lt;/em&gt;, to a degree, since you don't get control over where your assemblies end up on disk. &lt;/p&gt;

&lt;h2&gt;Sharing type definitions&lt;/h2&gt;

&lt;p&gt;In the product itself, we've shared out some definition assemblies that anyone can add a reference to and use.  The various built-in &lt;code&gt;ITag&lt;/code&gt; types, for example, are all well-known, so any extension can provide its own outlining logic, or errors/squiggles, or syntax highlighting.&lt;/p&gt;

&lt;p&gt;The reason this works without requiring every extension to ship a copy of the editor definition assemblies is that these assemblies are essentially already loaded by the time your extension is loaded (more correctly, Visual Studio knows where to find them when it comes time to load them).  Go go magic managed code and all that.&lt;/p&gt;

&lt;h2&gt;Binding paths&lt;/h2&gt;

&lt;p&gt;One of the features of Visual Studio that I've used previously to work around issues with loading custom tag types (see the &lt;a href="http://blogs.msdn.com/vsxteam/archive/2009/10/15/visual-studio-2010-sdk-beta-2-readme.aspx"&gt;last issue on this page&lt;/a&gt;) is actually a much more generally useful way of adding a directory to the list of paths that Visual Studio considers when looking for assemblies to load (the BindingPaths registry key).  This fixes the issue of custom tag types, as it makes it so the MEF component loader can find the assembly to load for the type information it has saved in the component model cache.&lt;/p&gt;

&lt;p&gt;The issue of sharing definition assemblies is effectively the same issue, then.&lt;/p&gt;

&lt;p&gt;When you think of assembly sharing, there are basically two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The assembly needs to be available at &lt;strong&gt;build time&lt;/strong&gt;, so you can compile your extension with the correct type references.&lt;/li&gt;
&lt;li&gt;The assembly needs to be available at &lt;strong&gt;run time&lt;/strong&gt;, so you can load your extension that references these types.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We usually conflate the two, but they truly are distinct.&lt;/p&gt;

&lt;h2&gt;Overall workflow&lt;/h2&gt;

&lt;p&gt;So the workflow here is fairly simple:&lt;/p&gt;

&lt;p&gt;First, you need the physical definition assembly as a reference to build against.  This may mean building and checking in a version of the assembly to your source control, or taking advantage of something like git's submodule support to link to the live source from which the definition assembly is built.  Also, you want to be sure to &lt;em&gt;not&lt;/em&gt; include that definition assembly in your VSIX.  It may not actually be hugely problematic if you do (I never really tested this out), but you certainly won't need it.&lt;/p&gt;

&lt;p&gt;Then, you create a reference from your VSIX to the VSIX you are depending on.  In the &lt;code&gt;.vsixmanifest&lt;/code&gt; designer this is fairly simple; in the &lt;code&gt;References&lt;/code&gt; section, simply click the &lt;code&gt;Add Reference&lt;/code&gt; button and add the information you need (if you have the extension already installed, it'll let you select it instead of typing in the name/ID manually).  The only tricky part is picking the min/max version number.  For the spell checker extension, I'm softly guaranteeing that I won't update the definition assemblies at least until the next major version number (3.0), so I'd use 2.999 as the max version number.  Picking an existing minor version number as the max would not be a great idea, since I update these fairly frequently (five times or so last Friday, during a bug bash at work).&lt;/p&gt;

&lt;p&gt;So now, when the user wants to install your VSIX, he has to first install whatever it depends on (I hope this becomes a more automatic feature in the future, instead of requiring the user to manually install dependencies).  That means that, assuming the dependencies play nicely with binding paths, your extension can depend on the types it needs be available to it at runtime.&lt;/p&gt;

&lt;p&gt;As of now, I don't know of any extensions on the gallery that actually extend the spell checker, though I do know of one person who was working on it, as he was the motivation for figuring out how to do all this.  I have heard questions, like &lt;a href="http://twitter.com/jaredpar/status/12355942774"&gt;this tweet from Jared Parsons&lt;/a&gt; about wanting to have a per-project dictionary, which would be well served by an extension that would keep a per-project dictionary file and ensure it is added and updated in source control (for Jared's or my sake, TFS or git, depending on what we're working on).&lt;/p&gt;

&lt;p&gt;If you are interested in creating an extension to the spell checker or creating your own extensible extension, feel free to ask me for help.  I'm very interested to see what, if anything, people do in this space, especially since one of my hopes for tagging is that there would be some amount of shared user-generated tag types for everyone to party on.&lt;/p&gt;

&lt;h3&gt;One remaining question&lt;/h3&gt;

&lt;p&gt;The one thing that remains unanswered for me is: what if you want to light up a feature in another extension but not require it always be installed?  Specifically, the scenario is this:&lt;/p&gt;

&lt;p&gt;If you have Markdown Mode installed and the spell checker installed at the same time, Markdown Mode should enable markdown-specific natural text parsing logic.  If you have the spell checker installed by itself, it doesn't have any specific knowledge of markdown files.  If you have the Markdown Mode extension installed by itself, it doesn't do any spell checking on its own.&lt;/p&gt;

&lt;p&gt;The trick is that I don't want the Markdown Mode extension to &lt;em&gt;require&lt;/em&gt; the spell checker extension be installed, I just want it get specific spell checking when both extensions are installed on the same machine.&lt;/p&gt;

&lt;p&gt;I haven't really figured this one out yet.  I don't know if Markdown Mode can get away with not shipping the spell checker definition assembly, which is likely half the fault of how MEF is set up in Visual Studio and half the fault of how the editor handles failures.&lt;/p&gt;

&lt;p&gt;The issue is that the &lt;code&gt;TagType&lt;/code&gt; attribute on tagger providers doesn't use exact matching of types.  If a tagger provider says it produces an &lt;code&gt;ISpecificTag&lt;/code&gt;, and someone creates an aggregator for &lt;code&gt;IBaseTag&lt;/code&gt; from which &lt;code&gt;ISpecificTag&lt;/code&gt; inherits, the &lt;code&gt;ISpecificTag&lt;/code&gt; tagger provider will created and just work through the magic of covariance.&lt;/p&gt;

&lt;p&gt;To do this, though, the logic that matches up taggers needs the actual type hierarchy of &lt;code&gt;ISpecificTag&lt;/code&gt; to see if it is assignable to an &lt;code&gt;IBaseTag&lt;/code&gt; reference.  In the case of the spell checker, with &lt;code&gt;INaturalTextTag&lt;/code&gt;, it needs to know what that type is.&lt;/p&gt;

&lt;p&gt;The answer here is really that Markdown Mode's natural text tagger &lt;em&gt;doesn't care&lt;/em&gt;; if the spell checker isn't around and so it's natural text tag's hierarchy isn't available, it just doesn't want to be loaded.&lt;/p&gt;

&lt;p&gt;The problem is that when the tag aggregator is matching up tag types, it will try to evaluate the type of the MarkdownMode tagger provider, and it doesn't expect or guard against the exception that will be thrown when the VS MEF-hosting logic can't figure out what type to load.  If it did (just catch the exception and move on), then we'd get the effect we're looking for.&lt;/p&gt;

&lt;p&gt;So, the only obvious workaround seems to be to include a copy of the definition assembly in Markdown Mode (and add it to the binding path), though that may introduce other problems down the line.  Hopefully the issue in the tag aggregator can be dealt with, and so this problem won't be &lt;em&gt;permanent&lt;/em&gt;, but it won't be fixed anytime in the immediate future.&lt;/p&gt;
&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10004951" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/3RVZX5kaM7c" height="1" width="1"/&gt;</description><category domain="http://blogs.msdn.com/b/noahric/archive/tags/editor/">editor</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/extensions/">extensions</category><category domain="http://blogs.msdn.com/b/noahric/archive/tags/spellcheck/">spellcheck</category><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/04/30/extending-extensions.aspx</feedburner:origLink></item><item><title>Long time no bloggy.</title><link>http://feedproxy.google.com/~r/LettersFromTheVSEditor/~3/zTP80hNBhW4/long-time-no-bloggy.aspx</link><pubDate>Mon, 19 Apr 2010 17:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9998232</guid><dc:creator>Noah Richards</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://blogs.msdn.com/b/noahric/rsscomments.aspx?WeblogPostID=9998232</wfw:commentRss><wfw:comment>http://blogs.msdn.com/b/noahric/commentapi.aspx?WeblogPostID=9998232</wfw:comment><comments>http://blogs.msdn.com/b/noahric/archive/2010/04/19/long-time-no-bloggy.aspx#comments</comments><description>&lt;!-- Long time no bloggy. --&gt;

&lt;p&gt;Been a long time since I've blogged, sorry about that.  I won't get too deep into things that have happened since my last post, but here's the short list:&lt;/p&gt;

&lt;h2&gt;Visual Studio 2010 shipped&lt;/h2&gt;

&lt;p&gt;Yeah, the obvious one.  We (individual developers) actually aren't that involved for the last month or so, but there were a few last minute fixes and activities &lt;em&gt;around&lt;/em&gt; the launch of VS to get finished up.&lt;/p&gt;

&lt;p&gt;All in all, we shipped on time (where on time means "the last date that we picked to ship, no delaying last-minute issues") and, so far as I know, haven't managed to physically destroy everyone's computer or break anyone with an attached tablet device (again).  That's what the RC is for :)&lt;/p&gt;

&lt;h2&gt;Extension updates galore&lt;/h2&gt;

&lt;p&gt;I've pushed out some updates in the last few weeks, a ton in the last week or two to the &lt;a href="http://visualstudiogallery.msdn.microsoft.com/en-us/7c8341f1-ebac-40c8-92c2-476db8d523ce"&gt;Spell Checker extension&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Fixing bugs for the future&lt;/h2&gt;

&lt;p&gt;As with everything that has a shipping deadline, you can't fix everything that you'd want to, given infinite time and resources.  Around the time that VS2010 shipped, our (the editor team's) triage team went through the bugs that we'd postponed due to resource constraints and relative priority and split out the ones that we really are just not ever going to fix and the ones that we should be fixing.&lt;/p&gt;

&lt;p&gt;Since then, I've been fixing lots of little issues that have been bugging me for awhile, which includes everything from misspelled comments and confusing exception messages to silly/stupid little mistakes (like the one where the URL classifier doesn't subscribe to &lt;code&gt;TagsChanged&lt;/code&gt; events on any &lt;code&gt;ITagger&amp;lt;IUrlTag&amp;gt;&lt;/code&gt;, which at least one person has already hit).&lt;/p&gt;

&lt;h2&gt;Other random things at work&lt;/h2&gt;

&lt;p&gt;We're at a stage, between releases, where we step back from working on features and bugs and work on, well, everything else.  As part of that, there are a ton of smaller projects going on in the division.  One that I'm working on with Michael, a coworker on the editor team (who needs to write new articles in &lt;a href="http://blogs.msdn.com/micleh/"&gt;his blog&lt;/a&gt;), involves some of the more advanced features of the editor (text view models and the projection system).  We'll share more about it in the next few months, along with blog posts on the what and how.&lt;/p&gt;

&lt;h2&gt;What's to come?&lt;/h2&gt;

&lt;p&gt;I haven't been writing too many new extensions these days, so expect more posts about editor extensibility in the abstract in the near future.  Also, I'm always open to taking topic ideas, so send me an email or post a comment if there is something you'd like to see written about.&lt;/p&gt;

&lt;p&gt;Also, if you have any feedback about Visual Studio 2010, please either &lt;a href="http://connect.microsoft.com/VisualStudio"&gt;open bugs on Connect&lt;/a&gt;, ping me on twitter (@noahsmark), or send me an email.  The Connect bugs would be the best way to track the status of any bugs you file, but anything sent to me should find its way to the correct team.&lt;/p&gt;
&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9998232" width="1" height="1"&gt;&lt;img src="http://feeds.feedburner.com/~r/LettersFromTheVSEditor/~4/zTP80hNBhW4" height="1" width="1"/&gt;</description><feedburner:origLink>http://blogs.msdn.com/b/noahric/archive/2010/04/19/long-time-no-bloggy.aspx</feedburner:origLink></item></channel></rss>
