VS Code and Go: a superhero's guide
JOKER: Where does he get those wonderful toys?
—“Batman” (1989)
Not all superheroes wear capes, and you don’t need any fancy accessories to be one, either. All you need is intelligence, dedication, and a keen sense of justice. But assuming you have those, a few well-chosen tools can really help you a lot.
Visual Studio Code is the most popular Go editor, and no wonder: it’s the Batman’s utility belt of editors. With the Go extension installed, it’s the perfect accessory for a Go superhero. Let’s cape up and take a tour of the seven superpowers of VS Code for Gophers.
1. Editing
The first thing to master in VS Code is the basics of typing text, editing it, and moving it around. Sure, you can do everything by pointing and clicking with the mouse, but that’s slow, and experienced programmers tend to avoid unnecessary mousing. Instead, learn yourself some keyboard superpowers, for great justice.
VS Code uses various modifier keys, and I’ll refer to them using the (Mac-specific) symbols that VS Code itself uses in its documentation:
- ⌘ (“command”)
- ⌥ (“option”, or “alt”)
- ⇧ (“shift”)
- ^ (“control”, or “ctrl”)
The symbols on your keyboard may be slightly different, but you’ll figure it out.
Here are some of the most useful keyboard shortcuts to know when editing Go code. These are the default shortcuts on macOS; on Windows, use Ctrl instead of ⌘, and Alt instead of ⌥.
Move line(s) up / down
Shortcuts: ⌥↑ (up), ⌥↓ (down)
Instead of selecting lines, cutting, clicking to reposition the cursor, and pasting, you can move the current line or selected lines directly.
New line below / above
Shortcuts: ⌘Enter (below), ⇧⌘Enter (above)
When the cursor is in the middle of a line, you don’t have to jump to the end before inserting a new line below: just type ⌘Enter instead. Similarly, ⇧⌘Enter inserts a new line above.
Delete current line
Shortcut: ⇧⌘K
You don’t have to select the whole line, or lines, and then cut: ⇧⌘K deletes the line the cursor is on immediately, or selected lines if there’s a selection.
Indent / outdent line
Shortcuts: ⌘] (indent), ⌘[ (outdent)
Because VS Code will auto-format the current file every time you save it, you don’t need to manually indent and outdent lines very often. When you do, though, ⌘] and ⌘[ will move the current line or selection one indent stop right or left.
Comment / uncomment lines
Shortcut: ⌘/
If you need to temporarily enable or disable a block of lines, select them and type ⌘/ to comment them all out. To uncomment them, use ⌘/ again.
2. Multi-cursors
Multi-cursors are one of the neatest features of VS Code. When you need to perform the same editing operation on several lines at once, you can ⌥-click to insert a new cursor everywhere you need it. Then just type, and your new text will be inserted at each of those cursors.
Add new multi-cursor with mouse
Shortcut: ⌥-click
Hold down the ⌥ key, position the mouse pointer where you want it, and click the left mouse button. Each click will insert a new multi-cursor.
For example, to add a trailing comma to each line of a struct literal, ⌥-click at the end of each line and then type a single comma.
Alternatively, you can hold ⌥ and drag the mouse to select a chunk of code. Each ⌥-drag adds a new selection, and when you type, it will replace all your selections.
Add multi-cursor above / below
Shortcut: ⌥⌘↑ (above), ⌥⌘↓ (below)
To add multi-cursors without using the mouse, press ⌥⌘↓. This will add a multi-cursor on the next line at the current cursor position. Keep pressing ⌥⌘↓ to insert more multi-cursors on successive lines.
Everything you can do with the single cursor, such as editing commands, you can do in many places at once with multi-cursors. Type Esc to go back to single-cursor mode.
Select next
Shortcut: ⌘D
Highlight some text, and type ⌘D. You’ll see that the next occurrence of the selected text in the file is also highlighted, and has a multi-cursor. Keep typing ⌘D to select the next occurrence, and so on.
Finally, when you’ve selected all the occurrences of the text that you want to change, just type, and your typing will replace all the selections. Or you can use the navigation keys to move all your multi-cursors. Press Esc to cancel the multiple selections.
3. Commands and shortcuts
VS Code has hundreds of built-in keyboard shortcuts, but it has many more capabilities than you could ever assign to a shortcut. Each thing VS Code can do is known as a command, and you can run commands by typing ⇧⌘P and entering them directly.
Enter command
Shortcut: ⇧⌘P
For example, type ⇧⌘P to open the command entry window, and then type “keyboard”. You’ll see a picklist of matching commands.
Use the ↑↓ keys to navigate to “Help: Keyboard Shortcuts Reference” and press Enter. This will open the keyboard shortcuts cheat sheet in your default browser.
In the picklist, you’ll see the currently-configured keyboard shortcut for each command next to it. For example, next to “Help: Keyboard Shortcuts Reference” you’ll see “⌘K ⌘R”. This means that instead of having to enter the command manually, you can type ⌘K then ⌘R to activate it using a shortcut.
Finding commands by name
You don’t have to know the exact name of a command to run it; VS Code will show you all the commands that contain the string you enter. For example, if you can’t remember the command to show test coverage, type ⇧⌘P and enter “coverage”: you’ll see “Go: Toggle Test Coverage In Current Package”. (There are many more “Go:” commands to investigate.)
While the cheat sheet lists some of the most useful shortcuts, you can see absolutely all of them with the command “Preferences: Open Keyboard Shortcuts” (⌘K ⌘S). You can scroll through the list to see what’s available, or search by typing in the text box.
Finding commands by shortcut
To find what command is associated with a given shortcut, type the shortcut keys into the search box, using the strings cmd
, shift
, and option
for the modifier keys. For example, to find out what ⇧⌘G does, search for “shift cmd g” (it’s “Find Previous”, since you ask).
Custom shortcuts
If you want to add a shortcut for a command that doesn’t have one, or change a shortcut to something more convenient, double-click the relevant command. A window will open inviting you to type the shortcut you want to use, then press Enter.
If the shortcut you enter is already assigned to something else, you’ll see a message saying (for example) “2 existing commands already have this keybinding”. Click this message to see what those commands are.
It’s probably a good idea to stick with the existing default keybindings as much as possible. Otherwise, you’ll find it confusing if you have to use someone else’s VS Code, or they will if they use yours. But by all means add new shortcuts for commands you use often.
Some useful Go shortcuts
For example, I run tests a lot, so I’ve configured a shortcut key for the “Go: Test Package” command. Any time I make a change to the code, I can press my shortcut key and immediately see the test results in the “Output” window, without having to manually run go test
in a shell, or click the “run package tests” link in the test file.
Similarly, I’ve assigned a shortcut to the “Go: Toggle Test Coverage In Current Package” command, which is very useful. It highlights in green all code that’s executed by some test, and code that’s never executed by any test is shown in red. Repeat the command to toggle this highlighting off again. You’ll also see the percentage of code covered, in the “Output” window: for example, “92.6% of statements”.
4. Navigation
Did you ever wonder how much time you spend just scrolling and clicking around the file explorer pane, trying to find the file you want? There’s a faster way. You can open any file in the current workspace just by typing part of its name.
Open file by name
Shortcut: ⌘P
To open a file in the editor if you know its name, press ⌘P and enter a few characters of the filename. VS Code will show you a picklist of matching files.
If the one you want is highlighted, just type Enter to open it. Otherwise, use the ↑↓ keys to change the highlighted file, and then Enter to open it.
Switch to previous
Shortcut: ⌃Tab
If you’ve previously edited a file in this session, you can open it directly without even typing part of its name. Instead, hold down ⌃Tab. This will show a picklist of recently edited files. You can pick the one you want with the mouse, or navigate to it with the ↑↓ keys while you keep holding down ⌃Tab.
Alternatively, you can keep holding ⌃, and press Tab to move down the picklist of recent files one by one. Release the ^ key when you’ve highlighted the one you want, and it will open in your current editor.
The most recent file will be highlighted first, though, so if you just press and release ⌃Tab once, you’ll switch straight to the last file you edited. This means you can just hit ⌃Tab quickly and repeatedly to switch back and forth between the same two files, which is handy. Alternatively, if you’d like to see both files at once, see below for how to split the editor horizontally.
Split editor horizontally
Shortcut: ⌘\
It’s very useful to be able to see two files at once, side by side, when editing Go code. For example, you might want to have a function in one window, and its test in the other. To split the editor window into two, type ⌘\. You can then use ⌘P to open the file you want in each editor.
Focus left / right editor
Shortcut: ⌘1 (left), ⌘2 (right)
When you have side-by-side editors, you can use ⌘1 to jump to the left-hand one, and ⌘2 to jump to the right-hand one. If you have more than two editors open at once, typing ⌘ and the appropriate number key will jump to that editor.
5. Symbols
You don’t have to navigate your code just by moving the cursor or switching files. You can also use symbols (in Go, symbols are named functions, types, or variables, for example).
Go to definition
Shortcut: ⌘-click
If you hold down the ⌘ key and hover the mouse over a code symbol, you’ll see that it’s underlined, indicating that it’s now a hyperlink. If you click on it, the editor will jump to the definition of that symbol.
For example, if you see a call to a function ListItems
in the code, and you’re wondering where that function is defined, ⌘-click on it to see. This works with standard library functions and other imported packages, too.
Go back
Shortcut: ⌃-
If you’ve just used ⌘-click to find the definition of some symbol, and now you want to go back to where you were, type ⌃- (control-hyphen). Keep pressing ⌃- to further retrace your steps through the codebase.
Go to symbol
Shortcut: ⌘T
If you want to find or edit a Go symbol (that is, a named function or variable), you could use the general “search text in workspace” function (⇧⌘F), but there’s a quicker way. Typing ⌘T and the first few characters of the symbol’s name will display a picklist of matching symbols.
Use the ↑↓ keys to highlight the symbol you want, and then Enter to jump to that symbol in the editor. For example, to jump to a function called ListItems
, type ⌘T followed by “ListItems”, then Enter.
To find a method, use the name of the type followed by a dot, followed by the method name. For example, to jump to a method on Pipe
named Exec
, type ⌘T followed by “Pipe.Exec” and Enter.
Rename symbol
Shortcut: F2
This is a powerful Go refactoring tool. If you want to change the name of some symbol, such as a function, throughout the project, position the cursor on it and type F2 (or right-click and select “Rename symbol”). Type the new name and press Enter, and it will be automatically renamed everywhere it’s used.
This is, naturally, much faster than manually finding and changing every occurrence of the name. And it’s more intelligent than a text-based search and replace, because “Rename symbol” is Go-aware: it only changes the text when it’s being used as a Go symbol. So it won’t, for example, replace that word inside a comment, or inside a string literal, and this is usually the behaviour you want when refactoring.
6. Snippets
If you find yourself typing more or less the same piece of code pretty often, you can save it as a snippet. These are preconfigured chunks of text that VS Code can autocomplete for you when you type a certain word or abbrevation.
For example, there are many snippets that come with the Go extension. In a Go file, try typing tf
and pressing Tab. You’ll see the following snippet inserted:
func Test(t *testing.T) {
}
This is very handy, but that’s not all. You’ll notice that the cursor has been placed after the word Test
in the function name, waiting for you to complete the name of the test.
If you type something here and press Tab, the cursor now moves automatically to the beginning of the test body. This really speeds things up when you’re writing lots of short tests, for example.
Built-in Go snippets
There are many more built-in snippets provided by the Go extension, including:
ff
—fmt.Printf
forr
—range
loopfunc
— function declarationif
—if
statementims
—import
blockmeth
— method declarationswitch
—switch
statementtdt
— table-driven test
Listing all snippets
To see more, type ⇧⌘P and run the command “Insert Snippet”. You’ll see a picklist of all available snippets appropriate for the kind of file you’re editing, organised by topic. For example, if you’re editing a Go file, “Insert Snippet” will show you all the built-in Go snippets. Type to search, or use the mouse or arrow keys to scroll the list.
Custom snippets
You can even create your own snippets for bits of code that you use often, with the command “Preferences: Configure User Snippets”. Snippets are organised into different files by language or editing mode; for example, to add a new Go snippet, select the go.json
file in the picklist.
This will edit the snippet file, and a comment shows you the required JSON format for defining snippets. The minimum you need for a snippet is a name (this is shown in the picklist), a prefix
(what you type to invoke the snippet), and a body
(the text to insert). For example:
"if error in test": {
"prefix": "ife",
"body": "if err != nil {\n\tt.Fatal(err)\n}\n"
},
Note that you can use \n
to insert newlines, and \t
to add indents.
Once you’ve entered your new snippet, save the file and switch back to the Go code. Enter your prefix, and you should see your snippet presented as one of the Intellisense completion options. Press Tab to insert it.
Placeholders
Remember how the tf
snippet actually positioned the cursor in the middle of the snippet, so you can add the name of the test function? You can do that with custom snippets too. Use $1
, $2
, and so on, as the placeholders for where the cursor will go. For example:
"fmt.Sprintf": {
"prefix": "fsp",
"body": "fmt.Sprintf(\"$1\", $2)",
},
Note that if your body
text contains double quotes, you’ll need to escape those with a leading \
character, as in this example.
When you activate this snippet, the cursor will be positioned inside the format string, at the $1
position. Type something here and press Tab to move to the next insertion point, at $2
. And so on.
You can specify some default text for the placeholder too, like this:
"fmt.Sprintf": {
"prefix": "fsp",
"body": "fmt.Sprintf(\"${1:%v}\", ${2:data})",
},
Now when you activate the snippet, the default text for placeholder $1
will be highlighted. To accept it, just press Tab, or enter some new text to replace it.
7. Integrated terminal
VS Code’s built-in terminal window is especially useful when writing Go programs. Without having to switch applications, you can use the shell and Go tools to manage your code, while it’s visible in the editor at the same time.
Open / close integrated terminal
Shortcut: ⌃` (control-backtick)
The terminal will use your default shell and environment, and the current directory will by default be the folder that the VS Code window is editing.
On the other hand, you can right-click on a folder in the Explorer sidebar and select “Open in Integrated Terminal” to get a terminal there.
The code
command
While you can do everything in the integrated terminal that you could in a standard terminal application, one useful thing to do is to control VS Code itself. For example, you can open files for editing from the command line, or pipe the output of some command into an editor window.
To do this, you’ll need the code
command installed in your PATH
. Fortunately, VS Code can do this for you. Type ⇧⌘P and run the command “Shell Command: Install ‘code’ command in PATH”.
You can now use the code
command in your integrated terminal (or any terminal). For example:
code go.mod
This will open the go.mod
file in your working directory in the current editor.
Piping text to VS Code
To send some command output to VS Code (for example, to make it easier to read lengthy text), use the |
(pipe) symbol in your command and follow it with code -
, like this:
ls |code -
This will run the ls
command and put its output into a new editor window.
Using code
as your default editor
You can also use VS Code to input information to commands. Many commands that need user input will use your “default editor” (configured via the EDITOR
environment variable). For example, if you run git commit
with no commit message, it will open your default editor so that you can add a message.
If you want to use VS Code as your default editor, use the code
command with the --wait
flag, like this:
export EDITOR="code --wait"
This will have the command (for example, git commit
) wait while you enter and edit your text. When you’re happy with it, close the VS Code editor window, and the command will continue.
Multiple terminals
To open more terminals, type ⌃⇧` repeatedly. You’ll see each new terminal session shown in a sidebar at the right of the terminal; click to select the one you want. Alternatively, to split the current terminal window side-by-side, press **⌘**.
Hyperlinks in terminal text
The terminal is smart enough to recognise filenames. If you ⌘-click the name of a file in the terminal, it will open the file in the editor. This also works with compiler errors or test output. For example, suppose you run tests and you see a failure like this (either in the terminal or the “Output” window):
script_test.go:42: oh no
Hold ⌘ and click on the filename to jump straight to the failing line in the editor. You can do the same with a compiler error:
./script.go:257:1: syntax error: non-declaration
statement outside function body
Being able to ⌘-click and go directly to the error saves a lot of time. Also, when you’re editing code and you have multiple errors or warnings, another quick way to navigate between them is to press F8 to “Go To Next Problem”.
Conclusion
There are many immensely powerful code editors available, some of which need a significant investment of time and effort to learn. On the other hand, there are simple point-and-click editors that don’t do much, but are very approachable for beginners.
VS Code strikes a good balance between the two. It’s easy to get up and running with simple editing, and you may never need to go much beyond that. But if you are a power user, there’s a mind-boggling amount of power available under the hood, just a keyboard shortcut away. And as impressive as the built-in capabilities are, you can enhance them with over 30,000 extensions for every imaginable language or text format.
It’s also possible to customise just about every detail of the way VS Code works to suit your own unique desires. There’s even an extension to make it work somewhat like Vim (I don’t recommend this, since the resultant hybrid can be rather confusing, but you do you).
If you do a lot of programming, VS Code can dramatically increase your speed and productivity, and make your life much easier in many respects. You’ll need to invest a bit of time in learning to get the most out of it, but that investment will repay you handsomely in the long term.
Diving into VS Code
I recommend that if you’re completely new to VS Code, you use the immersion approach to learn it. Force yourself to use VS Code exclusively for all your text editing tasks, all day, and keep this up for at least a week. It will be a little slow and painful at first, as you constantly refer back to this article, or the cheat sheet, to remind yourself of how to do something.
But after a couple of days you’ll start gaining muscle memory, and you’ll find you no longer have to think about how to do common editing tasks. Gradually, the things you use most often will become entirely automatic, and you’ll also start to get your configuration exactly how you like it. The editing experience will grow more and more comfortable, until you wonder how you ever managed without it.
Learning new superpowers
On the other hand, if you’ve used VS Code before, or you’re already a reasonably experienced user, but now you want to master some of the advanced features described here, I recommend focusing on one at a time. Pick some feature such as snippets, or the integrated terminal, and make a conscious effort to include it in your daily workflow. This avoids overloading your brain with too much new stuff at once. Once you’ve become thoroughly familiar with a given feature, command, or shortcut, pick another one to learn, and so on.
Also, don’t hesitate to explore, investigate, and play around. Browse the commands menu and the shortcuts list to find what’s available, and try things out to see what they do. Try out some extensions for your favourite languages or tools.
Extensions, extensions everywhere
For Go programmers, the Go extension is a no-brainer, but you’ll also find useful extensions for Python, Java, Ruby, C#, and most other languages.
On the tooling front, check out the Git, Kubernetes, Terraform, and Docker extensions.
Here are some general-purpose extensions you might also find useful:
Filter Text — use Unix commands such as
sort
andfmt
to manipulate selected textRewrap — word-wrap comments to a specified line length (useful for Go documentation comments).
Settings Sync — sync your settings and customisations across machines via GitHub
Spell Right — spellchecker for multiple languages
For great justice
You’ll find that your VS Code utility belt can expand, like Batman’s, to accommodate just about whatever tool you need. It may take you a little time to grow used to your powers, like any new superhero, and there may be one or two minor accidents along the way. But stick with it, and you’ll soon be out there swinging and slinging with the best of them.
Thankfully, the cape is optional.