Get Sh*t Done

Roadmaps

As developers we have a number of tasks that we need to get done regularly; starting new features, opening files, finding files, searching for bits of code, etc. If you have been using an editor other that Vim for a while you are probably very comfortable getting everything done with that editor. Moving to a new editor is like moving to a new city. Everything is unfamiliar, you don’t know how to get from here to there, and everything takes two to five times longer than it used to.

This chapter will help you get past those feelings of frustration that come along with drastically changing your development environment. I will present a number of situations that are likely to come up often and ways to handle those situations with these new tools you have been learning about. Think of this chapter as a kind of roadmap to this new development environment. If you get stuck somewhere, look at the relevant section for help.

Starting Things

You are at the start of something many times throughout a project. You, of course, have to start the project itself, which usually entails many pieces of knowledge that are not used again. You start something every time you begin work on a new feature as well. This section will give you some tips on getting things started.

Starting a Project

There are a few things I do whenever I start a new project. First off, you have to decide where to keep the project. I keep everything in a folder in my home directory named Projects. No split between work and side projects, just ~/Projects/project_name. I never have to think about where my source code is.

Creating the actual project and setting up the git remote repository is going to be specific to your particular language / framework and Git hosting service. I trust you can manage that part on your own.

Once the project is created the next thing I typically do is write a tmux script to create the project specific development environment. You can start with a few basic windows as shown in the Scripted Environments section.

One thing that might not be obvious when creating a tmux script for a project is the power of the send-keys command. You can use it to perform any task that can be done on the command line. One thing I like to add to any Rails tmux script is to clear out the log files before starting the Rails server. While the Rails server is running locally it will continuously add to the development log. That log file can get very large over time. Rails has a rake task to clear out the log, so I just call that task before starting the server.

# Create a new window to be the rails server.
  new-window -n server -t [session_name]
  send-keys -t [session_name] 'cd [project_path]' C-m
  send-keys -t [session_name] 'rails log:clear' C-m
  send-keys -t [session_name] 'bundle exec rails server' C-m

You would need to replace [session_name] with the actual tmux session name you created earlier in the script, and replace [project_path] with the real path.

Starting a Feature

There are a number of steps you should take whenever starting a new feature in a project. These are mostly simple maintenance tasks, but they can save you a lot of trouble.

Update Your Repository

Make sure you have the most recent version of the source code. I’m assuming Git usage here, but the concept is valid for other version control systems as well. I’ve seen it happen a number of times. A developer creates a branch from the master branch, codes up their new feature, checks it in and makes a pull request only to find out that their master branch was not up to date and some other code is causing a conflict. Always, before creating a new branch, make sure master is updated. This is simple with the Oh My Zsh git plugin aliases.

$ gfo
  $ ggl

Those translate to git fetch origin, which will ensure that your local repository knows about any remote changes, and git pull origin $(current branch), which will actually merge any changes to your master branch. The $(current_branch) shell variable is used in a number of the git aliases. It is updated to the current branch whenever you checkout a new branch. If you are on the master branch ggl will actually run git pull origin master.

This can’t protect you from all conflicts, but it will certainly prevent easily avoidable conflicts from not having the most up to date code.

Create a Branch

Always create a new branch for any work you do. Even if this is your own side project and you are the only person working on it. You might decide part way through a feature that you would rather take a different approach, or the requirements might change. You can always delete a branch and start over.

Use the gb alias to see a list of local branches. To delete local branches that are no longer active use gbd [branch name], or to delete a branch that has not been merged use gbD [branch name]. To create and checkout a branch at the same time use gcb [branch name].

Keep Your Branch Updated

If you are working on a team and your feature is taking some time to finish you might need to do some merging. It is a good idea to keep your branch up to date with the master branch. You can merge the remote master branch into your feature branch with the gmom alias. If the branch you need to merge is not the master branch you can use gm [branch_name].

Editing Things

There are a number of situations that you will run into on a regular basis while editing code. This section will give you some tools to deal with those editing tasks.

Repeating Changes

The Vim . command is used to repeat the previous change. A change can be as small as removing a single character with the x command, or as large as deleting an entire file. Suppose you have three consecutive lines you wish to delete. You could use the dd command three times (dddddd), or you could use Visual Mode to select the lines and then delete them (Vjjd), but the fastest way, as in fewest keystrokes, would be to use the dd command once, then the . command twice (dd..).

Anything you type in Insert Mode is also considered a change. This makes it easy to find and selectively replace text in a file. Suppose you had a variable named address that you wanted to rename to user_address in some sections of the file but not others; first find the variable name with the search command /address<Return>. That will find and highlight all of the occurrences of the variable. The cursor will be placed at the first occurrence. This happens to be one you wish to change, so enter ciw to remove the variable and enter Insert Mode. Now type the new variable name, user_address, and then hit <Esc>. That change can now be repeated with the . command. Move through the rest of the matches in the file with n or N and press . whenever you want to replace the variable.

Ever been in a situation where you need to add a semi-colon or a comma to the end of a number of lines? You could use $ to move to the end of the line then a;<Esc> to add the semi-colon, then use j to move to the next line and repeat those commands. This can be shortened quite a bit with the . command. Remember that A can be used to enter Insert Mode at the end of the current line, so the movement and insert can be shortened to A;<Esc>. Now all you need to do is use j to move down a line then press . to add the semi-colon.

Re-Indenting Code

Moving code around generally means having to re-indent it. The Vim commands to indent or outdent code are > and < respectively. These are Vim operators, so they can take Motion or Text Object modifiers. To indent everything inside a pair of brackets you can type >i} (indent > inside i brackets }).

Many times the section of code that needs to be re-indented is not easily targeted. It may only be a single line inside of the brackets, or it could be a larger section of HTML between some div tags. In these cases it is usually better to use Visual Mode. Position the cursor at the start of the code you want to re-indent then press v to enter Visual Mode. Now you can use any Motion command to position the cursor at the end of the code to be re-indented. Once you are satisfied with the selected code press > or < to perform the re-indent desired.

Both of these methods will only re-indent the code one indentation level. What if you need to re-indent multiple levels? This is where the . command comes in. The Vim . command repeats the last change. So, to re-indent code three levels you could use >i}, for the first indent, then two dots .. to repeat the same indent action two more times.

Commenting Code

There are often times while editing where you would like to comment out a section of code temporarily or uncomment some code that can now be used. Unfortunately there isn’t a built in operator for commenting code. There is a plugin available which provides a commenting operator, but I will cover the non-plugin way first.

To comment a block of code you use the Block Visual Mode. Position the cursor where you wish to have the comment block start. Enter Block Visual Mode with <C-v> and then use the j command to move down to where the comment block should end. Now press i to enter insert mode. The cursor will be at the start of your selection, don’t worry this is normal. Type the comment character and then <Esc>. Once you <Esc> out of Insert Mode the rest of the selected lines will be commented.

The plugin mentioned above is the vim-commentary plugin by Tim Pope. It adds a gc operator that can be used with Vim Motion and Text Objects. For example, to comment out a block of code in braces you could use gca}. It also works with Visual Mode, just select the text you want to be commented then press gc. It also works for uncommenting, simply select the commented code and use gc again.

I tend to keep my Vim setup pretty minimal, but the vim-commentary plugin is small, focused and clearly a win for faster editing so I added the plugin without reservation. If you would like to add it as well just add the following lines to your Vim configuration.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  " Add the vim-commentary plugin for commenting code blocks.
  call minpac#add('tpope/vim-commenentary')

Save the configuration file and reload Vim then run the plugin update command :PackUpdate. You should see a message that the plugins are up to date and that there was one newly installed plugin.

Code Completion

If you are coming from an IDE you are probably used to having strong support for code completion. Typically, as you type a function or method name the IDE will provide the rest of the name for you, or a list of possible names based on what you have typed so far. Some will also offer to complete the function arguments as well.

Vim has code completion capabilities as well, but it works in a different way than it does in most IDEs. An IDE is typically built for a single language (such as IntelliJ for Java), or a small set of languages (such as Xcode for C based languages and Swift). That gives the IDEs the ability to be very specific with their suggestions. Vim, on the other hand, is a general purpose editor that can be used with any language. It can’t specialize its completion capabilities for a single programming language.

The way Vim’s completion works is to scan a number of places for words. The places Vim looks for potential completions can be changed with a configuration setting. By default it looks at the current buffer, other loaded and unloaded buffers, the tags file if there is one, and included files. I have never found a reason to change the setting, but it is also possible to have Vim search a dictionary or thesaurus file. Look at the Vim help topic for completion if you would like to know more (:h cpt).

When you trigger the autocomplete menu Vim looks at the first few characters you have typed and removes any non-matching words from its list and shows the matching words in a drop down list to you.

You open the autocomplete pop up menu with the <C-n> or <C-p> keystrokes. These commands only work while you are in Insert Mode, which makes sense because you would be in the process of adding text when you want to autocomplete some code. The <C-n> command opens the autocomplete menu with the selected item at the top of the list. Pressing <C-n> while the menu is open will select the next lower item and so on until you reach the bottom of the list. The <C-p> works in the opposite way. It opens the autocomplete menu with the last item in the list selected. Pressing <C-p> while the menu is open will select the previous item in the list.

As you highlight items in the autocomplete list the text in the file is also updated. You don’t have to press <Return> or any other keystroke to select the item in the list, it is already in the buffer. To continue on editing just start typing the rest of the text you want.

Suppose you want to add a new plugin to your Vim configuration, you can complete the minpac calls easily. Navigate to where you wish to add the new plugin and enter insert mode. Type call mi<C-n> to trigger the autocomplete menu. You should see minpac, minpac#add, minpac#init and other uses of minpac that are in the file. Now press <C-n> until the minpac#add item is selected. Now you can continue typing the rest of the command. When you type the open parentheses for the minpac#add function call the autocomplete menu will disappear.

If you trigger the autocomplete menu with <C-n> and the list is very large you can use <C-p> to move to the bottom of the list, which is the original text you typed before opening the autocomplete menu. You can then type additional character and the autocomplete list will refine itself to only items that match what you have typed.

If you open the autocomplete menu and then realize you don’t actually want to autocomplete the text you can dismiss the menu and remain in Insert Mode with <C-e>. You can also dismiss the menu with <Esc>, but that will exit Insert Mode and return you to Normal Mode.

Code Snippets

One thing that most newer editors have that Vim doesn’t is code snippet management. Code snippets are short boilerplate pieces of code that a developer can reuse over and over; typically changing a piece of the snippet each time it is used. You enter a short mnemonic that identifies the snippet and then press a trigger key, usually the <Tab> key and the snippet is inserted into your buffer at the current cursor location.

There are a number of plugins for Vim that add this functionality. The most popular one, and the one I’ll cover here is called UltiSnips. It has very good documentation and a number of screencasts available for learning it in depth.

First let’s get it installed. Open your Vim configuration file and add the UltiSnips plugin along with a couple global settings.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  " Add UltiSnips snippet manager.
  call minpac#add('SirVer/ultisnips')
  
  " Look for snippet files in the following directories.
  let g:UltiSnipsSnippetDirectories=[$HOME.'/dotfiles/vim/UltiSnips']
  
  " Editing window should be in a vertical split.
  let g:UltiSnipsEditSplit="vertical"

Save the file, exit Vim and restart it. Now run :PackUpdate to install the plugin.

The first setting, g:UltiSnipsSnippetDirectories, tells UltiSnips where to look for snippet files. It is an array, so multiple locations can be set, but here it is just being set to a sub-directory of our Vim configuration in the dotfiles project. This will allow you to keep all of the snippets you create in source control with the rest of your configuration files. UltiSnips manages a separate snippet file for each kind of source code file. You can edit the snippet file for the current file type by entering the :UltiSnipsEdit Ex command. So, if you are editing a C source file and use the :UltiSnipsEdit command UltiSnips will look for a file named ~/dotfiles/vim/ultisnips/c.snippets. If the file exists it will open it, if it doesn’t exist it will be created.

The second setting, g:UltiSnipsEditSplit determines the behavior of the :UltiSnipsEdit command. By default the snippets file will be opened in the current window, replacing the current buffer. Changing the setting to vertical will make the UltiSnips buffer open in a vertical split. You could also have it open in a horizontal split if that suits your needs.

UltiSnips provides the ability to work with code snippets, but it doesn’t include the snippets themselves. There is another project, called vim-snippets, that contains snippets usable by UltiSnips, but I would advise against using it. You will be far better off creating your own snippets. This will allow you to create only the snippets you need and you will be able to remember the snippet mnemonics much more easily.

If you would like to use the vim-snippets project add the plugin after the UltiSnips plugin and change the snippet directories setting as follows:

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  call minpac#add('honza/vim-snippets')
  
  " Look for snippet files in the following directories.
  let g:UltiSnipsSnippetDirectories=["UltiSnips", $HOME.'/dotfiles/vim/UltiSnips']

You will need to save the file and restart Vim and run :PackUpdate as normal to get the snippets plugin installed.

Create a Simple Snippet

Now that UltiSnips is installed lets create a simple snippet. This will just be enough to get you going. I won’t cover some of the advanced snippet features that UltiSnips provides. Watch the screencasts linked from the UltiSnips documentation to learn more.

We’ll make an HTML tag snippet because it should be easy enough for anyone to understand. First, open Vim and create a new HTML file named index.html. It can be anywhere on your system. Add the basic HTML boilerplate below to the file.

<!DOCTYPE html>
  <html>
    <head>
      <title>Snippet Example</title>
    </head>
    <body>
    <body>
  </html>

With that in place we can now edit our HTML snippets. Enter the :UltiSnipsEdit command in Vim to open the UltiSnips editor. You should see a new split window with a file named ~/dotfiles/vim/UltiSnips/html.snippets.

Every snippet is wrapped in a snippet ... endsnippet block. The snippet command takes the trigger mnemonic for the snippet, a description of the snippet, which will only be shown when you list all snippets, and snippet options. This will be a generic HTML tag snippet, so I’ll just use ‘t’ as the trigger mnemonic. I’ll also add a short description.

snippet t "A generic HTML tag snippet"
  endsnippet

Between the snippet and endsnippet commands you add the text that will be filled in when the snippet is triggered. Just to see it working lets just add some static text.

snippet t "A generic HTML tag snippet"
  <div>
    Hello Snippets
  </div>
  endsnippet

Save the snippet file with :w, move over the the HTML file, create a new line inside the body tags and enter t<Tab>. The div tags and the ‘Hello Snippets’ text should be entered into the HTML page.

Having this static text snippet is not very helpful though. We can improve the snippet to work for any HTML tag by adding tab stops. A snippet tab stop is a location in a snippet that will allow you to enter additional text. A tab stop is specified with a dollar sign and a number, so $1 would be tab stop number one and $2 would be tab stop number two.

When you trigger the snippet the cursor will be placed at the first tab stop. You can either add some text or not, depending on the situation. Pressing <C-j> will position the cursor at the next tab stop. You can move to a previous tab stop with <C-k>. You can repeat a tab stop multiple times in a snippet. If you do any text entered will be mirrored at both locations. This is exactly what we need for the HTML tag snippet.

Undo the change you made the the HTML file with u and then move back to the html.snippets file and modify it as shown below.

snippet t "A generic HTML tag snippet"
  <$1>
    Hello Snippets
  </$1>
  endsnippet

Save the snippet file again and move back to the HTML file. Enter t<Tab> and you will see the tag angle brackets entered along with the ‘Hello Snippets’ text and the cursor will be placed inside the first tag. Type div and you will see that the text is mirrored to the end tag.

This is pretty handy, but we can improve the snippet a bit with a couple simple changes. First, the $0 tab stop is a special tab stop. It marks the end position that the cursor will move to after all other tab stops in the snippet have been visited. We’ll add that to the inside of the tag and remove the unneeded text.

Also, a tab stop can have placeholder text. This text will be the default used whenever the snippet is triggered, but it is selected so you can immediately overwrite it if you need to. Undo the changes to the HTML file again and move back to the snippet file.

snippet t "A generic HTML tag snippet"
  <${1:div}>
    $0
  </$1>
  endsnippet

Save the changes to the file and move back to the HTML file. Enter t<Tab> and you should see the HTML tags filled out with div. Replace that with p and then press <C-j>. The cursor will now be inside the paragraph tags ready for you to enter any text.

This is just a brief introduction. I hope you can see how much snippets can improve your productivity with creating pieces of boilerplate code.

Finding Things

An important part of working on any project is being able to find the files, functions and symbols that you need to edit or work with at any given time. Vim has a number of different ways to find things. The tools work at different granularity levels. The Netrw plugin is useful for browsing a directory hierarchy and finding files. Ctags is a tool that scans your code and keeps track of the location of function and class definitions in the project. Finding arbitrary symbols in the project can be accomplished with the :grep Ex command. Lastly, I will cover some more details about the / and ? file search commands.

Find Files

The Netrw plugin is bundled with Vim and Neovim by default and provides, among other things, a browsable tree view of a directory. It actually has the ability to edit files on remote machines; the tagline for the project is “Network oriented reading, writing and browsing”. We are mostly interested in the local browsing capabilities here.

Many of you are probably accustomed to text editors that have a tree view pane that provides the ability to expand and collapse folders, add new files, open files for editing and so forth. The Netrw plugin works in much the same way, however it generally does not remain visible all the time as the tree views in other editors do.

You can start Netrw in a number of ways. The :Explore Ex command will open Netrw in the current window and use the entire window. If you would rather open Netrw in a split window you can do it with :Sexplore for a horizontal split or :Vexplore for a vertical split. You can also shorten those commands to :Ex, :Sex and :Vex. The image below shows Netrw opened while in the dotfiles project.

Netrw User Interface
Netrw User Interface

Another, perhaps simpler way, to open Netrw is to use the :e[dit] Ex command. If you supply a directory name to the edit command it will open Netrw with that directory as the current directory. You can specify a full path or a relative path to the directory. If you want to open the directory that Vim was started in, generally the project directory, you can simply type :e .. The . represents the current directory just as it does in the shell. This works the same for split windows as well, :split. and :vsplit. both activate Netrw in a split window.

Now that Netrw is open what can you do with it? First off, realize that it is just a buffer in Vim. Many of the movement command you already know work the same in this buffer as they would in a source file buffer. You can use the regular h, j, k and l commands to move around. You can search for text with the / and ? commands and so on.

When the cursor is on a file pressing <Return> will open the file in a new buffer. If the cursor is on a directory pressing the <Return> key will open the directory. The display after opening a directory depends on the view that is set. You can move back to the parent directory with the - (minus) key.

There are four different views available in Netrw. You can cycle through the different views by pressing the i command. The default view is called thin and just shows the directories and files contained in the current directory in a single column. The long view is like the thin view, but it also shows directory and file information, much like the long option for ls. The wide view shows the directories and files in the current directory in multiple columns.

The behavior of those three modes when you enter a directory is to redraw the buffer with just the contents of the new directory. You can only see the contents of a single directory at a time.

The tree view acts a little differently. In tree view Netrw shows hierarchy lines to denote directory structure. When you open a directory the existing directories and files are not replaced. Instead the files and directories inside the directory you opened are added to the view and indented so you can see the hierarchy.

Netrw Tree View
Netrw Tree View

If you would like the tree view to be the default when Netrw opens you can add the following setting to your .vimrc or init.vim file.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  let g:netrw_liststyle = 3

The full list of options are:

The banner at the top of the Netrw buffer shows the current working directory, some sorting related information and some helpful key commands. You can remove the banner for the current session by pressing the I key. If you would like to permanently remove the banner add this setting to your Vim configuration file.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  let g:netrw_banner = 0

That should be enough information about Netrw to allow you to browse a project directory structure for a file. There is much more that can be done with Netrw though. You can create, delete and rename files and directories, copy files between directories, print files. The list goes on. You can find out more by pressing <F1> in the Netrw buffer to show the documentation.

Find Function and Class Definitions

The ability to jump from the usage of a class name, method name or function name to its definition can be very handy when editing code. Rather than having to remember which file the code is in, opening it and navigating to the definition you simply use a key command and you are instantly there.

Unfortunately, to get this functionality working requires a little bit of setup. Exuberant Ctags is an application that scans source code and creates a keyword to location index that can be used to navigate the project. The application used to be built in to Vim, but it was separated out to its own project some years ago. This means you will have to install it yourself if you don’t already have it.

On Ubuntu you can use Apt.

$ sudo apt install exuberant-ctags

For macOS use Homebrew.

$ brew install ctags

You can verify it is installed by checking that it responds with its version number.

$ ctags --version
  Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert

Now that the ctags application is available we need to have it scan our project’s source code and generate a tags file. The simplest way to generate the tags file is to just run ctags and pass it the source code files to scan. For instance, if you wanted to scan all the Ruby files in the current directory you would use:

$ ctags \*.rb

When the ctags application is complete there will be a tags file in the current directory. Vim can then use the tags file to navigate through your source code.

As you edit the code in the project and move things around the tags file will need to be updated. There are a couple different methods you can use to keep the tags file up to date when you edit your project. It is possible to run external commands from within Vim with the :! command. You can manually update the tags file whenever you like by running :!ctags -R. The -R argument tells ctags to recurse through all of the directories in the project. That might include directories that you don’t want ctags to scan though. You can provide an --exclude argument to have ctags skip directories.

Typing that command by hand every time would be time consuming and error prone. A better approach would be to create a Vim key mapping. In your Vim configuration file you can add the following key mapping to trigger ctags with the <F5> key.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  :nnoremap <F5> :!ctags -R --exclude=.git --exclude=node_modules --exclude=log

You can replace the <F5> key with some other key mapping if you like. You can also add or remove other directories to skip as suits your projects.

Tim Pope, a very active Vim enthusiast has also created a way to use Git hooks to automatically update your tags file whenever you commit, merge or pull new code. The solution is a bit involved and beyond the scope of this book, but if you would like to investigate it you can find instructions in his blog post describing it.

Now you should have a reliable way to regenerate the tags file as needed, so how do you use it in Vim? First Vim has to know where the tags file is. Vim has a tags setting that provides the paths to look for tags files. You can view the setting with :set tags?. By default it is set to ./tags,tags which will make Vim look in the root directory Vim was started in as well as the current directory. If you need to have Vim look in additional paths you will need to add something like the following to your Vim configuration.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  set tags+=[additional_path]

You don’t need the brackets, just replace [additional_path] with the actual path you need to add.

Now the there is a current tags file available and Vim should know where to find it you can start navigating your source code. When the cursor is positioned on a class, function or method name you can press <C-]> and Vim will move to the definition of that class, function or method. If the definition is in a different file that file will be opened in a new buffer.

If you need to go back to where you started you can use <C-t>. It remembers how many times you have used the <C-]> command, so you can keep using <C-t> to continue moving backwards through the list.

There are some cases where a function or method is re-used. For example, in an object hierarchy where a single method is overridden in multiple classes. Vim uses a ranking system to determine which is the most appropriate version of the symbol to find. If you would rather see a list of matches you can use g<C-]>. That will display a numbered list of matches and a prompt. Entering a number will make Vim navigate to the selected match. Pressing <Return> alone will cancel the navigation.

Find Patterns

Most developers are familiar with the grep command. It allows you to search files for regular expressions. For example, if I wanted to see all of the places in the Z Shell configuration file that mentions setting a theme I could use:

$ grep THEME ~/dotfiles/zsh/.zshrc
    ZSH_THEME="candy"
    # Setting this variable when ZSH_THEME=random
    # ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" )

If you add the -n flag grep will also show the line number of each match.

$ grep -n THEME ~/dotfiles/zsh/.zshrc
    13:ZSH_THEME="candy"
    16:# Setting this variable when ZSH_THEME=random
    20:# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" )

With that information you could open the file in Vim and navigate to the location to make any edits you need.

There is no reason to do this on the command line. Vim has its own built in :grep command. To perform the same search in Vim you can enter :grep THEME ~/dotfiles/zsh/.zshrc. Run this command in Vim and the command area will expand to show you the matches. Pressing <Return> will open the file of the first match and position the cursor on the correct line.

To navigate through all of the matches you can use the :cnext, :cprev, :cfirst, and :clast Ex commands. The command area in Vim will update to let you know the current match number and the total number of matches.

Find Patterns In Buffer

The :grep command is great for finding text in any file in your project. If you know that what you want to find is in the file you are currently editing :grep can be overkill. In this case you can use the Vim search commands, / and ?.

The / command searches forward from the cursor position and the ? command searches backwards. Otherwise the two commands operate in the same way. When you type / the Vim search prompt will open. This is the same area you use to enter Ex commands. As you type into the search prompt you will see text matches highlighted in the buffer. When you are satisfied with your search press the <Return> key and the cursor will be moved to the first match.

If there are multiple matches you can move to the next match with the n command. Move to the previous match with the N command. The direction of these commands is relative to the original search direction. So, if you stated the search with ? then the n command will move upwards in the buffer.

You can repeat the previous search by just entering / or ? and then pressing <Return>. This allows you to change the direction of a search without having to re-enter the pattern. You can also move through the search history with the <Up> and <Down> arrow keys in the search prompt.

There are some settings regarding search that can improve the experience. The first is incsearch. This is enabled by default and shows the text matching the search pattern as it is being typed. If the pattern is invalid or nothing is found nothing will be highlighted. This can give you a nice visual cue to how many matches are in the buffer. You still need to press <Return> to complete the search.

Another setting that is handy for searching is hlsearch. This is a setting that is different between Vim and Neovim. The default for Vim is for hlsearch to be off, but in Neovim it defaults to on. When hlsearch is on the highlighted matches continue to be highlighted after you complete the search. If hlsearch is off the highlighted matches are no longer highlighted after you complete the search. You can still move through the matches with n and N in both cases.

To make sure that search highlighting is active add the following to your Vim configuration.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  # Enable search match highlighting
  set hlsearch

It can be handy to have the search matches highlighted after you complete the search, but it can also be annoying to see the highlighted text after you no longer need to. You can turn off the highlighting with the :nohlsearch command, which can be shortened to :noh. This turns off the search highlighting until the next search command is run.

Having to type :noh all the time to remove the match highlights can get annoying. It is a good target for a custom key mapping. Add the following to your Vim configuration.

~/dotfiles/vim/.vimrc or ~/dotfiles/vim/init.vim
  # Map <Leader>h to :nohlsearch
  nnoremap <silent> <Leader>h :nohlsearch

Now when you press ,h the match highlights will be turned off until the next search.

Remembering Locations

Most editors have some kind of bookmarking feature. Vim is no exception. It simply calls them Marks. To create a mark at your current location type m and then any letter. If you use a lowercase letter the mark will be local to the current buffer. If you use an uppercase letter the mark will be available globally within Vim. For example, to mark a function named initiate_payment() you could use mi and then you could get back to that function from anywhere in the buffer. If, however, you were working on multiple files you could use mI and then you would be able to get back to that function from any buffer.

To return to a marked location you can use either the quote () character and the mark letter you used or a back tick (`) character and the mark letter. The difference between the two is the quote version moves you back to the same line as the mark and the back tick version moves you to the exact location, line and column, where you made the mark.

Vim also keeps track of some automatic marks that can be handy to use. The `` mark will take you back to the position in the file before the last jump. Suppose you are working on a bit of code and need to see some details in the initiate_payment() function. Use mi to jump to that function, find what you need, perhaps yank some text, then you can return to your original location with `` . You can also get to the location of the last change made with `. and the location of the last insertion with `^.

Building Things

Vim provides a method for you to build your code from within the editor. The :make Ex command can be used to run Make for your project. It isn’t limited to only running Make however. By changing the makeprg setting you can use the :make command to run any compiler or linter you desire.

One benefit of using :make within Vim is that any errors reported during the build process are gathered in the Quickfix list. The Quickfix list was introduced in the Finding Things section when I described the :grep Ex command. Using the Quickfix list commands you can easily navigate to the files and lines of errors to fix them.

Make and the Quickfix List

Let’s start with a simple example. The source code for the book includes a small C project called addfrac that includes a Makefile to build the code. It is a simple program that asks the user to enter two fractions and then adds them and displays the result. You will need a C compiler installed to follow along on your machine, but it isn’t necessary to understand the concepts.

First, open your terminal and change directory to the source directory for addfrac. You should see two files, the addfrac.c source code, and the Makefile file that includes instructions for Make. Start Vim in this directory. Once Vim is running enter the :make command. Vim will run your system Make behind the scenes and display the results in the command area.

I have introduced a couple of problems in the addfrac.c source code. One warning and two errors, though the errors are both related to the same piece of bad code. When the :make command is finished you will see some of the output from the build process in the command area. It will show the warnings and errors along with the source lines where they occurred. As usual, at the very bottom of the command area the “Press ENTER or type command to continue” message is displayed.

If you press <Return> the command area will collapse and you will no longer be able to see the errors and warnings. The information is stored in the Quickfix list though. You can see the contents of the Quickfix list by opening it with :copen. The warning should be highlighted in the list. It is complaining about the function requiring an integer pointer, but an integer is being passed. A quick look at the source code confirms this. The address-of operator was left off of the num1 argument.

The Quickfix list window still has the focus and the warning should be highlighted. If you press <Return> now the source code window will become active and the cursor will be placed on the line and column where the warning occurs. The fix is simple, just change num1 to be &num1 to pass the address of num1 rather than the integer itself.

Save the change with :w and move to the next item in the Quickfix list with :cnext. The next two errors are complaining about line 14 where the denominator is being calculated. There is a typo, the two input denominators should be multiplied, but instead of the multiplication operator there is a dollar sign instead. Enter :cnext again to see the second error. It is complaining about an undeclared identifier ‘$’ and the cursor should be right on the dollar sign. You can simply replace it with r*.

Write the change to the source file and then run :make again. This time you should not see any errors or warnings. The Quickfix list is still open and shows the compiler command line, but no other information. You can close the Quickfix list with :cclose.

Make Targets

You can also give a target name to the Vim :make command. Most Makefiles have some utility targets for creating documentation, running tests, installing the application or cleaning out intermediate files. If you followed along with the previous example and fixed the code you should have the addfrac executable in the addfrac directory. If you run :make clean the clean target in the Makefile will be run which will remove the executable. The project documentation will generally have some information about the Make targets available.

Finishing Things

Let’s face it, most projects are never finished. The closest we developers get to finishing anything is when we finish a story or feature. Even then ‘finishing’ is really only a short pause between when the code goes live and when the bug reports or enhancement requests come in.

Still, there are some workflow steps that generally fall at the end of a coding session. These are the steps you should go through once you have finished the feature or bug fix.

Tests

Always make sure you run the full suite of tests for your project before you check in your code. You do have tests right? You may have been running a particular set of tests while you were writing your code. It can take some time to run the full suite of tests for a project, so developers typically speed things up by just running the tests around the code they are writing. There is no problem with this while developing the feature, but you should also make sure that your changes didn’t affect the rest of the code by running the full suite of tests.

There isn’t a specific Ex command for running tests in Vim, but you can use the shell command. For example, to run all of the RSpec tests for a project you could enter :!rspec. If you wanted to only run the tests in the current file you could use :!rspec %. The % sign in Vim stands for the current buffer.

You could also choose to use the vim-test plugin. It provides an abstraction layer over many testing environments, ranging from C# to VimScript and many more in-between. It provides a number of Ex commands for running tests in Vim. Use :TestNearest to run a single test in the current buffer nearest to the cursor location. You can also :TestFile, :TestSuite and :TestLast to run the tests in the current file, all of the tests in the project or just the last test run respectively.

By now you should have a good feel for installing plugins. The project documentation is thorough and should help you with installing and configuring vim-test.

Pushing Your Code

I covered some Git aliases that are helpful when starting a new feature in the Starting Things section. Here is the bookend to that discussion. These are the Git aliases that you can use while finishing your feature and pushing your code.

I mentioned that you should keep your branch updated, particularly if your feature takes more than a day. The gmom alias will merge the master branch from the origin into your branch. It is probably a good idea to do this before you push your code to make sure your branch is as up to date as possible.

Use gst, which expands to git status, to see all of the changes that have been made to your branch. Please don’t be that developer that just blindly adds every change all of the time. Watch for files that should be added to .gitignore and the dreaded .DS_Store files on macOS. When it is clear that every change can be staged you can use gaa, which expands to git add --all, to add everything in one shot.

You could also stage and commit your changes at the same time with the gcam alias, which expands to git commit -a -m. The -a flag tells Git to stage all of the changes. If you have already staged the changes use gcmsg, which expands to git commit -m.

Finally, the ggp alias expands to git push origin $(current_branch). Use it to push your changes to the remote repository.

Cleaning Up

You will probably need to start working on a new bug fix or feature now. You can switch back to the master branch with gcm, which expands to git checkout master. At this point you would go back to the Starting Things section and begin again.

Later on, when your code is reviewed and the pull request has been merged into the master branch you should remove the local branch, just to keep your local repository clean. You can list the local branches with gb, which just expands to git branch. To delete a branch use gbd, which expands to git branch -d. You will need to supply the branch name.

Summary

This chapter basically boiled down to many little recipes for getting things done in your new Terminal development environment. I believe it will save you time and effort when you get stuck and need a quick answer on how to perform a particular task.

We covered starting projects and features, many different ways to enhance your editing tasks, how to find anything from files down to characters on a particular line, how to build your code and finally how to finish a feature and clean up after yourself.

This book has been a relatively quick overview of these tools. There is still much to learn about each. It is my hope that you be productive with these tools now, and over time you will deepen your understanding of them.