Chapter 8 - Multiple Cursors
And now, introducing the feature you have all been waiting for, the one and only:Multiple Cursors!
This is probably the most powerful, and useful, feature any text editor can have. Sadly enough, it might also be one of the most underused features. Let’s take a moment to compose ourselves, make sure we’re wide awake for this chapter, and dive into the wonderful world of multi-line editing.
Toggle Column Mode ⌘ + shift + 8
Part 1
For starters, let’s introduce you to Column Mode.
Let’s open Chapter8
. The output
variable contains a long string concatenation, but there are a few problems with it:
- the
+
is missing to actually DO the concatenation - and there’s a space missing at the end of every string
You could put your cursor at the beginning of the second line, type a +
, and then press ↓ and ⌘ + ←,
and type a +
again. Now you could repeat that process for every line, but there’s an easier way.
Since all the strings are lined up perfectly, wouldn’t it be easier if we could first put a bunch of cursors in front of every line, and then just
type +
? Let’s try to do just that.
First position your cursor at the beginning of the second string ("one hell of a"
).
While watching the bottom right of your screen press ⌘ + shift + 8 once.
You should see the word Column
appear next to UTF-8
. This means you have just toggled on Column Mode
.
Now, hold down shift and press ↓ until you’ve reached the last string ("of column mode"
). There
should be a cursor blinking at the beginning of every line. It might look like one giant cursor, but it is in fact a bunch of them.
Now simply type a +
and be amazed.
Exit out of your multi-cursors by pressing Escape. Exit out of Column Mode by pressing ⌘ + shift + 8 again.
The word Column
should no longer appear in the bottom right, indicating you have indeed exited column select mode
.
Part 2
Let’s undo our changes by pressing ⌘ + Z and take a different approach. Notice how using ⌘ + Z once puts the multiple cursors back.
Now, what we really want is a +
at the end of every string, and to add a space inside all the strings.
Here is how you could do that.
Instead of putting your cursor at the beginning of the second line, put it at the beginning of the first line ("This sure is"
).
Toggle Column Mode again with ⌘ + shift + 8, and select all the lines again by repeatedly pressing ↓ until you’ve reached the second to last string.
Now press ⌘ + → . Notice how the cursors are all at different ending positions.
First let’s add the spaces so our strings aren’t pressed together as much. Move your cursor inside of the string by pressing ← once. Then type a space.
Then press End again and type a +. Escape out of the multi-cursor and disable Column Mode by pressing ⌘ + shift + 8.
This is a great feature when all the lines you want to edit are directly underneath each other. However, that won’t always be the case. Let’s take a look at how we can cope with those situations.
⌃ + g, ⌃ + shift + g and ⌘ + ⌃ + g
In Chapter8
there are two methods that need some fixing. They both tried to use a StringBuilder
but seemed to have forgotten to use
the append()
method. We can’t use Column Mode because the same mistake is repeated in a different method and there are lines in between that we
don’t want to put a cursor at.
We can, however, use ⌃ ctrl + g to add a cursor to a Find buffer (F3 and ⌃ ctrl +
F3).
Take a moment to think about what selection you would want to Find in that class.
First, try and see what would be included by pressing ⌘ + F3 on the following selections: "
, .
, ."
.
[!TIP] Spoiler:
"
won’t be good, because then we’d also end up with a cursor at the end of the string..
won’t be good either, because we don’t want to include the.toString()
.."
however is a near perfect fit.
So let’s select the first ."
at 21:17
and press ⌃ ctrl + g once and see what happens.
Now repeat ⌃ ctrl + g until you’ve selected all of the occurrences.
You’ll notice that the last occurrence is working code, and we don’t want to change that last one. So
press ⌃ ctrl + shift + g to undo that last add to cursor
selection.
Then let’s fix the code by typing append
after the .
. Don’t exit out of your multi-cursor just yet.
Remember how in Chapter 3 we learned about IntelliJ’s Wrapping feature? Maybe you also recall how we said that it was gonna shine in this chapter?
If you haven’t already, enable Wrapping with ⌘ + shift + A, smart braces
, enter
.
From the multi-cursors positioned after the append
you just typed, press shift + «kbd>⌘</kbd> + ↓ to select all the
strings, and then press (
.
As an alternative to repeatedly pressing ⌃ ctrl + g, and if you’re 100% sure that you won’t include too much, you can also press ⌘ + ⌃ ctrl + g and add all occurrences to your cursors in one go.
Undo ⌘ + z your corrections and try it out.
Notice how ⌃ ctrl + shift + g still deselects the last occurrence. This is because ⌘ + ⌃ ctrl + g is merely a repeated ⌃ ctrl + g.
NOTE:
Using this key combination often is also a great way to train your manual dexterity.
Various use cases
Now that we’ve seen the basics of creating and using multiple cursors, let’s try to apply it to some everyday tasks. We will see how using multiple selections can make your developer life so much easier.
Creating a TestBuilder
In IntelliJ, you can create a Unit Test for a class by pressing ⌘ + shift + t from the class you’re currently editing.
We will now use this to our advantage while creating a TestBuilder. Open PersonTO
with ⌘ + o, and
press ⌘ + shift + t.
Overwrite the class name from PersonTOTest
to PersonTOTestBuilder
and press Enter.
You might want to get rid of unnecessary org.junit.Assert.*
imports by pressing ⌃ ctrl + ⌥ + o
to Organize Imports.
Go back ⌘ + [ to the PersonTO
, and copy all private fields over to the PersonTOTestBuilder
.
Create an empty constructor for the TestBuilder with ⌘ + n.
Press ↑ and ctrl + enter to choose empty constructor
from the generation menu.
Create a build()
method that returns a PersonTO
.
Now, while still inside the PersonTOTestBuilder
, generate setters for all the private fields we just copied from PersonTO
:
Press ⌘ + n, select Setters
, then press shift + ⌘ + ↓ to
select all the fields, and press ctrl + Enter.
Using Enter would also work, but it is advisable to use ctrl + Enter when in a separate window, to press the highlighted button, and perform the Default action.
TIP:
Commit this sequence to muscle memory, you will get good mileage from it.
Now we’ve got a bunch of setters in our builder… That’s great, but we also want the methods to be chainable.
Select the “void set
” part of the first setter, and press ⌘ + ⌃ ctrl + g.
Type PersonTOTestBuilder
(because we want a Fluent API, using chainable interfaces).
Now we’ve got some options with our method names. We either want all of our configurable methods to have the with
prefix, or you want them
lowercased.
We can lowercase all of our methods by selecting the first letter: from your multi-cursors position press shift + →. Then press ⌘ + shift + u to toggle lower or upper case.
Now the only thing we need to do is to change the return statement to: return this;
.
Let’s press ↓, then press shift + enter, and type return this;
There you go! We managed to create a TestBuilder in less than a minute of work. Time to be pleased with ourselves and fetch a hot beverage.
Testing Enum Lists
Open Status.java
, and use ⌃ ctrl + g on NOT_REALLY
until you’ve got cursors on all the enums with that
status. Now try to select the Statuses themselves.
TIP:
You might have to disableCamelHumps
with ⌘ + a to help with the selection.
Copy (⌘ + c) these.
Now navigate back to StatusTest.java
. Before you paste, enable Column Mode (⌘ + shift + 8), make sure there’s a bunch of empty lines, and paste your
buffered lines to their destination.
By using Column Mode, we ensure our multiple cursors remain active and usable after pasting.
With our multi-cursors still there, put a ,
behind every copied enum value,
and press ⌃ ctrl + shift + j to join all the lines.
Now: complete the yaReallyStatuses_ContainOnlyDoneAndDunno
test on your own.
TIP:
⌘ + N is context sensitive, meaning IntelliJ will know what you want because you’re in a Unit Test.
Making csv lists from XML
Here’s an excerpt of an XML file containing a bunch of people from DC’s Batman universe.
<?xml version="1.0" encoding="UTF-8"?>
<Persons>
<Person>
<FirstName>Bruce</FirstName>
<LastName>Wayne</LastName>
<Age>24</Age>
<SecretIdentity>Batman</SecretIdentity>
</Person>
<Person>
<FirstName>Pamela Lillian</FirstName>
<LastName>Isley</LastName>
<Age>26</Age>
<SecretIdentity>Poison Ivy</SecretIdentity>
</Person>
<Person>
<FirstName>Edward</FirstName>
<LastName>Nigma</LastName>
<Age>41</Age>
<SecretIdentity>The Riddler</SecretIdentity>
</Person>
</Persons>
So let’s open (⌘ + O) Batman.xml
, and navigate to the directory where it’s at
with ⌘ + 1.
Create a new file with ⌘ + n and call it persons.csv
.
Copy the contents of Batman.xml
into persons.csv
. We can already delete the first line with ⌘ + backspace.
We know that every </
denotes the end of one field, but if we were to use ⌘ + ⌃ ctrl + g on
that, we would also include the </Person>
tags.
These tags though, denote the end of one line, so let’s first get rid of those and replace them with simple new lines
.
This means we can simply get rid of the start tag <Person>
with ⌘ + backspace.
Your file should now only contain items like this one:
<FirstName>Bruce</FirstName>
<LastName>Wayne</LastName>
<Age>24</Age>
<SecretIdentity>Batman</SecretIdentity>
Select all the closing tags by selecting </
and ⌘ + ⌃ ctrl + g, and replace them by a ,
.
Your file should now only contain items that look like this:
<FirstName>Bruce,
<LastName>Wayne,
<Age>24,
<SecretIdentity>Batman,
We will now get rid of the opening tags, so we end up with a CSV-formatted file.
If you want to retain the tag names as a CSV header line you can ⌃ ctrl + g on the opening angular brackets (<
)
and ⌥ Opt + ↑ to select all tag names.
Paste them at the top ( ⌘ + ↑ ) while in Column Mode to retain the multi-cursors, at the end (⌘ + →) of the
line put a ,
and press ⌃ ctrl + shift + j. The j
indicating we wish to join lines together.
Then select all the opening tags by selecting <
and pressing ⌘ + ⌃ ctrl + g.
Expand selection with ⌥ Opt + ↑, delete the tags, and join the lines together (⌃ ctrl +
shift + j).
Now remove the last ,
at the end of the line. You can then still get rid of excess new lines by
pressing ↑ and either ⌃ ctrl + shift + j or <kbd>⌘</kbd> + backspace delete
line.
And that’s it.