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 alt+shift+insert
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 down and home,
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 alt + shift + insert 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 down 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 alt + shift +
insert 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 ctrl + Z and take a different approach. Notice how using ctrl + 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 alt + shift + insert, and select all the lines again by repeatedly pressing ↓ until you’ve reached the second to last string.
Now press End. 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 left once. Then type a space.
Then press End again and type a +. Escape out of the multi-cursor and disable Column Mode by pressing alt + shift + insert.
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.
alt+j, alt+shift+j and ctrl+alt+shift+j
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 alt + j 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 ctrl + F3 on the following selections: "
, .
, ."
.
TIP:
Spoilers ahead…
"
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 alt + j once and see what happens.
Now repeat alt + j until you’ve selected all the occurrences.
You’ll notice that the last occurrence is working code, and we don’t want to change that last one. So
press alt + shift + j 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 ctrl + shift + a, smart braces
, enter
.
From the multi-cursors positioned after the append
you just typed, press shift + end to select all the
strings, and then press (
.
As an alternative to repeatedly pressing alt + j, and if you’re 100% sure that you won’t include too much, you can also press ctrl + alt + shift + j and add all occurrences to your cursors in one go.
Undo ctrl + z your corrections and try it out.
Notice how alt + shift + j still deselects the last occurrence. This is because ctrl + alt + shift + j is merely a repeated alt + j.
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
Pretend we’ve got a Transfer Object PersonTO
that represents a person in our problem domain.
We would like to to create a TestBuilder for this class so we can easily create and configure objects for our Unit Tests.
TestBuilders also typically reside in the same package as the class you want to build,
but in the test folder structure rather than in the main
package.
In IntelliJ, you can create a Unit Test for a class by pressing ctrl + shift + t from the class you’re currently editing.
We will now use this to our advantage while creating a TestBuilder. Open PersonTO
with ctrl + n, and
press ctrl + 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 + alt + o to Organize
Imports.
Go back ctrl + alt + left to the PersonTO
, and copy all private fields over to the PersonTOTestBuilder
.
Create an empty constructor for the TestBuilder with alt + insert. Press up and ctrl + enter to choose an 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 alt + insert, select Setters
, then press shift + end to select all
the fields, and press ctrl + Enter.
Using Enter would also work, but it s 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 + alt + shift + j.
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 + right. Then press ctrl + 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 down, 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
We’ve got a made up Status
enum containing a bunch of statuses that contain another made up SubStatus
.
In the corresponding Unit Test, aptly named StatusTest
, we want to test that the static methods return the correct Statuses
based on their
SubStatus
. The implementation is already there, we just need a good list summation in our .containsOnly
of the NOT_REALLY
test.
In this exercise, we will be using multi-cursors to get a list of elements we can use for our test.
Open Status.java
, and use alt + j 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 ctrl + shift + a to help with the selection.
Copy (ctrl + c) these.
Now navigate back to StatusTest.java
. Before you paste, enable Column Mode (alt + shift + insert), 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:
alt + insert 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>
We’ll try and create a CSV list from this XML.
So let’s open (ctrl + shift + n) Batman.xml
, and navigate to the directory where it’s at
with ctrl + F1.
Create a new file with alt + insert and call it persons.csv
.
Copy the contents of Batman.xml
into persons.csv
. We can already delete the first line with ctrl + y.
We know that every </
denotes the end of one field, but if we were to use ctrl + alt + shift + j 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 ctrl + y.
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 + alt + shift + j, 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 alt + j on the opening angular brackets (<
)
and ctrl + w to select all tag names.
Paste them at the top (home
) 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 + alt + shift + j.
Expand selection with ctrl + w, 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 ctrl + y delete
line.
And that’s it.