TextMate News

Anything vaguely related to TextMate and macOS.

Clever Completion

TextMate has always had completion and, as I’ve said before, it remains the best reason to wear out your ⎋ (esc) key. The idea is simple and I bet you are already familiar with it, but, just to recap, if I had this Ruby code:

class BoundingBox
  def initialize(min_x, min_y, max_x, max_y)
    @min_x = min_x
    @min_y = min_y
    
    @max_x = max_x
    @max_y = max_y
  end
end

I could later use completion to finish off this line:

class BoundingBox
  def max_x=(x)
    fail "max_x must be > min_x" unless @m‸
    # ...
  end
end

If I push ⎋ where my caret currently is (shown with above), TextMate will complete the variable reference to @max_y. It starts with @max_y, because that’s the closest instance variable reference to my caret (from the bottom of initialize()), meaning it had a high chance to be what I was referring to. It’s not really what I wanted though, so another press of ⎋ cycles me to the second choice of @max_x. That is what I want, so I add > and another @m. Then I can complete until I get @min_x (four presses of ⎋ or just one press of ⇧⎋ to go backwards, since it’s the furthest reference from my caret).

In this article will discuss:

  • Reasons to use completion
  • Suffix completion
  • Prefix completion
  • Mid-word completion
  • Intelligent scoped completion

Already Better

The previous example pretty much worked in TextMate 1 and it’s definitely still around in TextMate 2. I say “pretty much” because this example is already showing off how much smarter TextMate 2 is when it comes to completion.

You see, TextMate 1 had a pretty fixed idea of what a “word” really is and, since completion works on words, it wasn’t super clever. By default, it would have ignored the leading @ that identifies an instance variable and just started completing m words. That means the first sequence would have gone min_x, must, and then max_x (since those are the closest to my caret). Because TextMate 2 did use the @ it got to the right answer in one less step. In bigger examples it could save even more cycling. This actually turns out to be a big change with some pretty sweeping implications for TextMate 2 and we’ll get to those, but first let’s take a few detours…

Why use completion?

I bet some of you are wondering if completion is really worth an article all by itself. Frankly, it is.

Programming is hard enough all on its own, so anything that can make it easier is a win. The fact is that completion reduces errors. You’re just less likely to typo a variable or method name while using it. Since TextMate is often used for dynamic languages, like Ruby which silently uses nil for newly referenced instance variables, this becomes doubly important.

Make friends with your ⎋ key.

TextMate 2 is Now on the Right Side

Completion in TextMate 1 only used content to the left of the cursor. In other words, it finished typing words. TextMate 2, now uses the left and right sides. What does that give us? Well, prefix completion for one thing.

Say I’m still finishing off that method we looked at earlier and I make a mistake entering it:

class BoundingBox
  def max_x=(x)
    fail "max_x must be > min_x" unless @max_x > @min_x
    ‸x = x
  end
end

I meant to type @max_x = x there, instead of x = x. I run my tests, they catch the bug, I come back and notice the problem. Prefix completion makes the fix trivial. I click in front of the first x and push ⎋ until it fixes the variable reference (two times for this case).

Selecting What You Don’t Want

Technically, TextMate 2 uses the content to both the left and right of the caret for completion. This means you can also do a mid-word completion. I can type an @m_x, stick my caret between the m and the _, and push ⎋ to rotate between @max_x and @min_x (with the order determined by what’s closest to my caret).

I know what you’re thinking though. It’s kind of a contrived example, right? When are you going to use that?

Well, it turns out that TextMate 2 has another trick up its sleeve that just might change your mind. I said that it uses what’s to the left and right of the caret, but I left out a detail. It also removes any selection.

Knowing that, let’s say you used @min_x somewhere when you meant to use @max_x. Again, your tests catch the bug. Good thing you have those tests! So you go to fix it. One possible solution is that you click behind the n, press ⇧⇠ twice to select in, and push ⎋ to make the correction. It’s guaranteed to be just one press of the ⎋ key this time too, because, even thought TextMate 2 removes the selection to make the completion, it does use it to filter out the choice you didn’t want. Thus @max_x will be the only matching variable in our code with @min_x ruled out.

Are you keyboard jockeys scoffing at my use of the word “click” above? Alright, let’s get fancy. If your caret is just after a @min_x you wish to change to a @max_x, try this: press ⌃⇠ to jump over a sub-word chunk (the x in this case), then ⌃⇧⇠ to jump over the next sub-word chunk (min_) while selecting it, and finish the maneuver off with an ⎋ to make the correction. (Note: if the OS swallows those sub-word bindings for Mission Control or Spaces, just add an ⌥ (option) to them: ⌃⌥⇠ and ⌃⌥⇧⇠.) The m and the _ don’t change the result, so that’s a three-keystroke correction that’s guaranteed to be exactly what you want.

It’s All About Character and Class

Let’s go back to that concept of a word that I talked about earlier. I said all of this works because TextMate 2 has an improved idea of what is in a word. In truth, TextMate 2 can assign named grouping to any set of characters. As is usually the case, this concept of character groups is managed by scope, allowing TextMate 2 to adapt to multiple languages.

Ruby considers @max_x to be a set of variable characters (with the @) because the Ruby bundle scopes it as variable.other.readwrite.instance.ruby. That scope is inside of the scope source.ruby so it inherits this magic setting from the Source bundle:

{ characterClass = 'variable'; }

That setting is scoped to variable, so it affects everything under that distinction. In plain English it says, inside of source code files, treat variables as a group of related characters. A Ruby instance variable includes the leading @, so it’s in the same group of characters as letters that follow it. Global variables and their leading $ (in Ruby) would be treated the same.

But the real question is, what else does TextMate 2 consider to be related in code files? Several things, including my personal favorite:

{ characterClass = 'escape'; }

That’s scoped to constant.character.escape, which means that escape sequences are now self contained chunks of characters separate from the content around them.

What can we do with that? Well, say we have this simple Ruby statement:

lines = "‸Just\nTesting"

That string contains three words as far as TextMate 2 is concerned. That’s not just for completion either. If I place the caret at the beginning of the string and use ⌥→ to jump forward by words, I’ll skip to the end of Just, \n, and then Testing. Compare that to TextMate 1, which considered Just and nTesting to be words with a little punctuation (\) between them.

But this article isn’t about movement, as cool as it is. It’s about completion. So let’s add a little more code:

lines = "Just\nTesting"
if lines =~ /\bTe‸/
  # ...
end

If we push ⎋ with our caret after the Te, TextMate 2 will complete Testing from the string above. Notice how much context it’s using to make this decision. We’re inside a Regular Expression here, so TextMate 2 knows the word boundary escape (\b) isn’t part of the word to complete (Te). It also looks for possible completions inside of that Ruby string, where it knows the newline escape (\n) isn’t part of the matching completion (Testing).

Augmented Completion

If that’s not enough for your completion needs, you can augment the command yourself. TextMate 2 also takes this option one step further by passing the entire document to completion commands on stdin.

That’s some clever completion, if you ask me!

categories General TextMate 2