Slow start, eh? I haven't written a post in here since I started this blog, as I've been a bit, uh, distracted by other work-like things. One of those things was implementing box selection in the new editor.

Quick side note: coming up in the near future, I'll write a bit about two extensions I put up on the extension gallery for the beta: GoToDef (ctrl+click does go to definition) and ItalicComments.

First off, here's a video that Brittany put together on the new box selection stuff that we've (Brittany as a PM, Luke as QA extrodinaire, and Ameen on some of the box selection UI stuff) been working on and recently checked in:

<a href="http://video.msn.com/?mkt=en-US&playlist=videoByUuids:uuids:3e57917d-1b92-4188-b898-25a1d64a408e&showPlaylist=true" target="_new" title="Box Selection and Multi-Line Editing Demo">Video: Box Selection and Multi-Line Editing Demo</a>

You can jump on over to the article on the VS Editor blog to read the full announcement-y version. I wanted to share a bit about getting it written, though.

Though it's not necessarily a technically complicated feature, it's one of those things that ends up touching a good deal of the codebase (effectively everything that wants to know about what text is currently selected). Though I could have "hidden" box selection, in a sense, by forcing people who wanted to know about selection mode to go looking for it, I decided for the slightly more onerous path of making people who ask about the selection deal with it, at least to some degree.

It's a somewhat irksome decision to make; an early prototype of the box selection feature went the opposite route, leaving the API alone as much as possible. While it simplified some of the cases (like asking for the selected span while the selection was in stream mode), it meant that things would fail in odd ways in box selection mode, since it was a piece of the API that wasn't necessarily obvious. It would be kinda like having some type called Gizmo, with a property on it called GizmoText, and another property called JustKiddingcommaHereIsTheRealText. Not quite that bad, though :)

The scarier one is that you could have written code against the old API that didn't change at the semantic level of the programming language (there was still a property, called SelectionSpan, that returned a SnapshotSpan), but the but the behavior at the semantic of the editor had changed, especially relative to other consumers that were doing "the right thing" with the new API.

I'm all about trying to make as few changes for consumers as possible, but if the consumer really does need to know about the change, you're doing them a disservice by allowing the same code to compile and run before and after the change, but with different behavior.

So, the new model is a bit more complicated than the original, though it lends itself to decently simple usage (using Linq or just iterating over the collection with foreach).

A selection has a collection of SelectedSpans or VirtualSelectedSpans (yeah, I changed the tense; I don't really have a great reason for why, besides that it sounds slightly better to me and would hopefully be a bit more obvious of a change than just adding an "s" to the old property name and making the ensuing build breaks slightly harder to figure out). The Virtual version takes into account virtual space, which is frustrating enough to deal with that it probably deserves its own blog post :)

In most cases, you can treat box selection and stream (regular) selection the same by writing code like:

string selectionText = string.Join(newLine, textView.Selection.SelectedSpans.Select(s => s.GetText()))

A box selection on the clipboard is just an endline-separated string; when we read it back in for a paste, we look out for a special clipboard data object (that VS has used for awhile), and take that to mean "split this string back into separate lines and insert them a line at a time.

Multi-line insertion

One of the new (at least to VS) things about the implementation is multi-line insertion mode. If you make an "empty" box selection (or make a non-empty box selection and then delete it), you can type, backspace, and delete as if you had a gigantic cursor - whatever you type gets inserted on each line, backspace deletes a character on each line, and delete behaves similarly. It may not be a feature people will use all the time, but it's one of those nice things that makes you slightly happier a couple of minutes a week. As a recently filed bug report said, it'll keep you from cursing me for 5 minutes each week.

So, what would you use it for? Here are a few things (most of them are covered in the video):

  • Appending something to the front of field names (depending on the file I'm in, the language it's written in, and who wrote it: "_", "m_", things like that).
  • Adding "private" or "internal" in front of a set of fields.
  • Adding "//" at the beginning of a couple of lines.
  • Given a set of variable names that you've box selected, copied, and pasted, you could add various pieces of code around it, like "Console.WriteLine(" and ");".
  • Since a box selection can now be entirely in virtual space, you can add a comment block past the end of a line by creating an empty box in virtual space and typing "//"
  • Whatever else you write in the comments :)

An interesting side-effect of being able to create box selections entirely in virtual space is that alt+click (even before you drag) sets the cursor in virtual space. Originally, I thought this felt kinda buggy, and spent a bit of time thinking about how to work around it, until I realized the use for it.

I hate lining up arguments to method calls. Seriously hate. At some point, maybe the languages in VS will make TAB be nice and emacs-y and line up arguments; until that day, I spend a couple of seconds like this:

TAB, TAB, TAB, oh, crap, too far, backspace, backspace, NO, missed it again, space, space, space, space, space, space...

Now, I'm not usually big on the mouse, being a vim guy at heart, but here's one case where I can bow to the mouse:

var foo = some.Property.MethodCall(firstLongParameterName,
                                   **alt+click here**

Yeah, sign me up.

Anyways, comments welcome.