Getting Started With Vim - A Practical Guide
Tags: programming, career, learning, coding
Published: 2021-10-18 01:11:09

---
title: Getting Started With Vim - A Practical Guide
date: 2021-10-17 00:00:00 UTC
tags: ['Programming', 'Career', 'Learning', 'Coding']
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cg86gv7lrrlakdcoaa1d.png
canonical_url: https://jakewiesler.com/blog/getting-started-with-vim
published: true
---

So you want to use Vim full time do ya? It’s not going to be easy, but I believe the choice is worth it. [I made the switch from VSCode to Vim a few weeks ago](https://jakewiesler.com/blog/leaving-vscode-for-vim). I’ve been diving deep and learning a ton, and I still feel like I’ve only scratched the surface. To be honest, I don’t even feel qualified to write this post. But my recent efforts have to count for something, _right_?

Vim presents you with a thousand roads to wander. There is no shortage of configuration options, plugins and unfamiliar syntax, leaving newbies like us lost and paralyzed with decision fatigue. If this is how you feel, you’re not alone. But I urge you to push through.

Vim can be an incredibly powerful tool in your arsenal. When you have it tuned to your liking, it's an absolute joy to use. This post is aimed at guiding you through the difficult bits so you can get to the joyful bits as fast as possible. I will go through everything I know so far, helping you go from 0 to 1.

We will create a strong foundation that you can build on over time. One that gets out of your way and allows you to produce your best work. So grab a coffee (maybe 2) and get your mind right. We will dine in the gates of Vim this day!

## Table of contents

- [Neovim](#neovim)
- [General Settings](#general-settings)
- [Plugins](#plugins)
  - [Manage plugins with vim-plug](#vim-plug)
  - [Pick a color scheme](#a-color-scheme)
  - [Search with telescope](#telescope)
  - [Better status lines with lightline](#lightline)
  - [Git integration with vim-fugitive](#vim-fugitive)
  - [gitsigns for git decorations](#gitsigns)
- [Language Support](#language-support)
  - [Using lspconfig](#lspconfig)
  - [Language servers](#install-a-language-server)
  - [Autcompletion with nvim-cmp](#autocompletion)
- [Remaps](#remaps)
- [Tips and Tricks](#tips-and-tricks)

## Neovim

Instead of using good ole’ Vim, the one that comes preinstalled on your MacOS or Linux machine (sorry Windows users), we will opt to use [Neovim](https://neovim.io/). If you’re unfamiliar, Neovim [“is not a rewrite but a continuation and extension of Vim”](https://neovim.io/charter/). There isn’t much difference between Vim and Neovim from a user’s perspective. However, Neovim is optimized _out-of-the-box_ and has an active development team that is pushing the tool to greater heights quickly.

### Installation

First, you’ll need to install Neovim on your machine. [There are many ways to do this](https://github.com/neovim/neovim/wiki/Installing-Neovim). I use MacOS and opted for Homebrew:

```shell
brew install neovim
```

Once the install is complete, you can test that it’s working by using the following command in your terminal:

```shell
nvim --version
```

### init.vim

Vim uses a `.vimrc` file to hold your configuration settings, but Neovim is a little different. It uses an `init.vim` file located at the path:

```markup
~/.config/nvim/init.vim
```

The `~/` represents your home directory. You might already have a `~/.config` directory, as it is common amongst other tools. Whatever your situation, you will want to create an `init.vim` file at the path above.

```shell
mkdir -p ~/.config/nvim
cd ~/.config/nvim
touch init.vim
```

In this file, you can define general settings, plugins, color schemes, keybindings (called remaps) and more.

## General settings

Now that your config file is created, let’s open it up:

```shell
nvim init.vim
```

The file will be empty. Let’s add some basic settings:

```vim
" enables syntax highlighting
syntax on

" Better colors
set termguicolors

" number of spaces in a <Tab>
set tabstop=4
set softtabstop=4
set expandtab

" enable autoindents
set smartindent

" number of spaces used for autoindents
set shiftwidth=4

" adds line numbers
set number

" columns used for the line number
set numberwidth=4

" highlights the matched text pattern when searching
set incsearch
set nohlsearch

" open splits intuitively
set splitbelow
set splitright

" navigate buffers without losing unsaved work
set hidden

" start scrolling when 8 lines from top or bottom
set scrolloff=8

" Save undo history
set undofile

" Enable mouse support
set mouse=a

" case insensitive search unless capital letters are used
set ignorecase
set smartcase
```

I’ve collected these settings from community suggestions and others' dotfiles. I believe they are a great base without being too opinionated. Comments have been added for each setting to give some insight as to what they do. If you want more information on each, you can type `:h <setting-name>` inside Neovim to bring up the help manual for that specific setting. For example:

```markup
:h incsearch
```

Will bring up the help section for the `incsearch` option.

Note: If you have suggestions on other settings to include, [let me know on Twitter](https://twitter.com/jakewies)!

## Plugins

Now that we have some basic settings out of the way, let’s get to the fun part. _Plugins!_ In Vim, plugins are scripts that operate in a global scope or per filetype. Authoring these plugins is done in [Vimscript](https://learnvimscriptthehardway.stevelosh.com/). I’ve never worked with Vimscript, but I’ve heard it’s tricky, and a barrier to entry for developers who want to build plugins or contribute to Vim’s core.

[Neovim’s vision is to](https://neovim.io/charter/):

- Enable new contributors
- Remove barriers to entry
- Unblock plugin authors
- Deliver a first-class scripting alternative to Vimscript

This alternative scripting language is [Lua](https://www.lua.org/). Neovim still supports Vimscript, but the core development team encourages plugin authors to write plugins in Lua. I’ve only been exposed to the language for a few weeks, but it is relatively simple to understand. Here are a few resources if you’d like to learn more:

- [lua.org](https://www.google.com/search?q=Lua&oq=Lua&aqs=chrome..69i57j69i59l3j0i512j69i60l3.1250j0j1&sourceid=chrome&ie=UTF-8)
- [nanotee/nvim-lua-guide](https://github.com/nanotee/nvim-lua-guide)
- [Learn X in Y Minutes, Where X = Lua](https://learnxinyminutes.com/docs/lua/)

You will need to use Neovim for a little bit before you can understand where your pain points are. For this reason, I’m going to keep my list of plugin recommendations short. I will only recommend plugins that I feel are “blue chips” in the Neovim community.

That being said, both Vim and Neovim make it easy to pop plugins into your configuration and try them out for an hour or two. If you like them you can stick, but you don’t have to marry anything.

### vim-plug

In order to install plugins we need a plugin manager. Enter [vim-plug](https://github.com/junegunn/vim-plug). There are other options that I have my eye on (looking at you [`packer.nvim`](https://github.com/wbthomason/packer.nvim)), but for now we will use `vim-plug`. It has 25k stars on GitHub, so I think we’re in good hands.

To install it you can [checkout the docs in the GitHub repository](https://github.com/junegunn/vim-plug). Once installed, we’ll add a few lines to our `init.vim`:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" plugins will go here

call plug#end()
```

We call `plug#begin()`, passing in the directory where we want to install our plugins, and `plug#end()`, which signifies the end of our plugin declarations.

But we haven’t declared any…yet!

### A color scheme

Most colors schemes in Neovim can be installed as a plugin. A color scheme is 100% preference, and there are a lot of really good ones. Here’s a list of some of my favorites:

- [Gruvbox](https://github.com/gruvbox-community)
- [Dracula](https://draculatheme.com/vim)
- [vim-snazzy](https://github.com/connorholyday/vim-snazzy)
- [vim-one](https://github.com/rakr/vim-one)

Inside of `plug#begin()` and `plug#end()` , let’s declare our color scheme. I will use Gruvbox to illustrate:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

Plug 'gruvbox-community/gruvbox'

call plug#end()
```

The above example illustrates how to declare plugins you wish to install with `vim-plug`. It is shorthand notation for:

```vim
Plug 'https://github.com/gruvbox-community/gruvbox'
```

You can see more examples of declaring plugins with `vim-plug` [here](https://github.com/junegunn/vim-plug#example).

Once you declare your plugin in `init.vim`, you need to take a few more steps to install it:

1. Save your `init.vim` with `:w`
2. Source your `init.vim` with `:so %`
3. Run `:PlugInstall`

The commands above need to be entered in `NORMAL` mode. See here for an explanation on [Vim’s “modes”](https://www.freecodecamp.org/news/vim-editor-modes-explained/).

Step one saves your `init.vim` file without closing it.

[Step two “sources” your `init.vim`](https://unix.stackexchange.com/questions/466305/what-does-source-mean). The command is shorthand for `:source CURRENT_FILEPATH`.

Step three calls a global function named `PlugInstall` which is made available to you by `vim-plug`. It will open [a vertical buffer](https://vim.fandom.com/wiki/Buffers) to the left of your `init.vim`. In this buffer you will be able to see a list of all the plugins you’ve installed along with the most recent installation’s progress. Once the plugin has finished installing, you can close the `vim-plug` buffer by typing `:q!`.

After installing your color scheme, we’ll need to tell Neovim to use it. Inside of your `init.vim` and below your plugin declarations, add the following lines of code:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

Plug 'gruvbox-community/gruvbox'

call plug#end()

" declare your color scheme
colorscheme gruvbox
" Use this for dark color schemes
set background=dark
```

If you aren’t using `gruvbox` then make sure to refer to your color scheme’s documentation for clear instructions on how to declare it.

Save and exit with `:wq`. Next time you reopen Neovim you should see a crisp new paint job!

### Telescope

The first function-focused plugin on the list is [Telescope](https://github.com/nvim-telescope/telescope.nvim), created by one of Neovim’s core team members - [TJ Devries](https://twitter.com/teejdevries).

Side note, TJ puts out a lot of Neovim-related content on [his YouTube channel](https://www.youtube.com/c/TJDeVries) and [his Twitch streams](https://twitter.com/teejdevries). His YouTube channel in particular has been a great resource. Check him out!

Telescope is a must-have plugin in my opinion. It is a [fuzzy finder](https://en.wikipedia.org/wiki/Fuzzy_finder) built in Lua that can be configured every which way. It adds a ton of functionality to your Neovim setup. I haven’t realized its true potential so far, but for finding files and searching for text it’s amazing.

Let’s install it:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" Telescope requires plenary to function
Plug 'nvim-lua/plenary.nvim'
" The main Telescope plugin
Plug 'nvim-telescope/telescope.nvim'
" An optional plugin recommended by Telescope docs
Plug 'nvim-telescope/telescope-fzf-native.nvim', {'do': 'make' }

call plug#end()
```

Note that we are installing more than one plugin here. Telescope requires another plugin named [plenary](https://github.com/nvim-lua/plenary.nvim) to function. And the other plugin is a [Telescope Sorter](https://github.com/nvim-telescope/telescope.nvim#sorters) that supports [fzf](https://github.com/junegunn/fzf) syntax. [Telescope recommends installing it for performance improvements](https://github.com/nvim-telescope/telescope.nvim#suggested-dependencies). We like the gains, so we’ll take the gains.

Make sure to perform these three steps to complete installation:

1. Save `init.vim` with `:w`
2. Source `init.vim` with `:so %`
3. Install the plugins with `:PlugInstall`.

Next we need to configure Telescope. This is a common practice amongst Vim plugins, and in my opinion the most challenging aspect of Vim. If you’re working with a Vimscript-based plugin it can be even more so. This is why I’ve tried to stick to Lua-based plugins when possible.

Telescope happens to be a Lua-based plugin, which is awesome! Since it’s the first plugin on the list, we’ll need to do some basic setup.

First, we will need to create [a new directory in `~/.config/nvim` named `lua`](https://github.com/nanotee/nvim-lua-guide#modules):

```shell
cd ~/.config/nvim
mkdir lua
```

Inside of the `lua` directory we want to create a subdirectory to namespace our plugin configurations. To understand why this is necessary requires explaining how Vim handles file lookup. In short, creating a namespace subdirectory inside the `lua` directory [prevents unwanted filename collisions](https://github.com/nanotee/nvim-lua-guide#tips).

I’ve opted to use my GitHub handle as the name of my subdirectory:

```shell
cd ~/.config/nvim/lua
mkdir jakewies
```

You can make it whatever you like. It really doesn’t matter. Once you’ve created it, you’ll want to cd into it:

```shell
cd ~/.config/nvim/lua/YOUR_NAMESPACE
```

Note: `YOUR_NAMESPACE` is being used as a placeholder. Replace it with the name of your namespace directory.

In your namespace directory you will create a `.lua` file for every plugin you want/need to configure. Telescope is the first one:

```shell
nvim telescope.lua
```

A helpful Vim tip is that you can open files before they exist. If you close the file without saving it, it won’t be created! In the example above, we open `telescope.lua` in Neovim. As long as we save the file it will be created.

Our configuration for Telescope will be short and sweet:

```lua
local telescope = require('telescope')

telescope.setup {}

-- To get fzf loaded and working with telescope,
-- you need to call load_extension, somewhere after
-- the setup function.
telescope.load_extension('fzf')
```

The above example gives us our first taste of Lua. See? Not that intimidating! I’ve found that, at a minimum, a lot of plugins need:

```lua
require('PLUGIN_NAME').setup {}
```

I won’t be diving deep into Telescope configuration in this post. If you want to see some of the available setup options and other customizations, [check the docs](https://github.com/nvim-telescope/telescope.nvim#customization).

Make sure to save `telescope.lua` and exit Neovim with `:wq`.

Next, we need to tell our `init.vim` that it exists:

```vim
" ...general settings
" ...plugins

lua require('YOUR_NAMESPACE/telescope')
```

In my case I would put:

```vim
lua require('jakewies/telescope')
```

A couple of notes:

1. Put this command below the `vim-plug` functions!
2. You don’t need to specify the `.lua` filetype

What does this do? It seems a little magical at first, and understanding requires Vim lookup knowledge, but essentially this command will look for a `lua` folder in your Vim runtime, and source the specific `.lua` file based on the path. So in this example the following file will be sourced:

```markup
~/.config/nvim/lua/YOUR_NAMESPACE/telescope.lua
```

This is great! But we can certainly improve on it.

Instead of requiring each configuration file in our `init.vim`, we can just require the namespace directory. In order for this to work, we’ll need to add a new file to our namespace directory called `init.lua`:

```shell
cd ~/.config/nvim/lua/YOUR_NAMESPACE
touch init.lua
```

The `init.lua` file can be thought of as an index file, similar to `index.js` for you JavaScript friends. Inside `init.lua` we can require all of our configuration files:

```lua:init.lua
require('YOUR_NAMESPACE/telescope')
```

Now, inside of our `init.vim` all we need to do is require the namespace:

```vim
" ...general settings
" ...plugins

lua require('YOUR_NAMESPACE')
```

In my case it would be:

```vim
lua require('jakewies')
```

From this point on, any new configuration files in `lua/YOUR_NAMESPACE` can be required within `lua/YOUR_NAMESPACE/init.lua` and they will be sourced in your `init.vim`!

Ok, back to Telescope.

Telescope exposes [a number of commands](https://github.com/nvim-telescope/telescope.nvim#vim-commands) that can be accessed using `:Telescope` in `NORMAL` mode. A quick way to see all of the available commands is to enter `:Telescope` followed by a space, and then hit `<Tab>`. The tab button will open an autocomplete list full of available Telescope commands. You can navigate the list using `<Tab>` and `<Shift-Tab>` to go backwards.

Below are a list of Telescope commands that I have found extremely useful thus far:

1.  `:Telescope find_files`

    - This command will search your current directory for filenames matching your query. It will not show hidden files by default, and it will also ignore files in `.ignore` files like `.gitignore`.

    - This command will also try to identify if you are in a larger project using special directories like `.git`. This gives you access to subdirectories and parent directories if you have moved away from the project root.

2.  `:Telescope buffers`

    - Search for files that have been opened in a buffer in your current Neovim instance. Really helpful when you are working on a few files at a time, and you want to “narrow your world”.

3.  `:Telescope live_grep`

    - Similar to `find_files`, but searches for a string match rather than a file name. [Note, this command will only work if you have `ripgrep` installed](https://github.com/nvim-telescope/telescope.nvim#suggested-dependencies). `ripgrep` is a command-line search utility that makes searching for text really fast. It can be installed like any other command-line tool, and Telescope will use it for its `live_grep` command.

The commands above are just a few I’ve been leaning on lately, and there's many more. Another helpful tip is to think about mapping the commands above to a keybinding. For instance, I have the `find_files` command mapped to `<Ctrl-p>`.

You can view documentation on Telescope within Neovim using:

```markup
:h telescope
```

### Lightline

[Lightline](https://github.com/itchyny/lightline.vim) is a status line plugin for Vim. The status line exists at the bottom of your Vim window, and exposes a few helpful pieces of information, such as [the current MODE](https://www.freecodecamp.org/news/vim-editor-modes-explained/) (normal, insert, visual, etc.), and what type of file you are in.

I enjoy developer tools that do a small list of things well without trying to support every use case under the sun. Lightline fits that mold. It is relatively “light” in configuration options when compared to other popular alternatives like [vim-airline](https://github.com/vim-airline/vim-airline).

To install Lightline, let’s open up our `init.vim` again:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" Lightline
Plug 'itchyny/lightline.vim'

call plug#end()
```

Remember, each time you declare a new plugin you have to take 3 steps to get it installed:

1. Save your `init.vim` with `:w`
2. Source your `init.vim` with `:so %`
3. Run `:PlugInstall`

We will preemptively create a configuration file for Lightline now, because shortly [we will install a plugin that integrates with the status line](#vim-fugitive) in a unique way.

In our namespace directory, let’s create a `lightline.lua` file:

```shell
cd ~/.config/nvim/lua/YOUR_NAMESPACE
nvim lightline.lua
```

Inside the file, we’ll add a placeholder config object:

```lua
vim.g.lightline = {}
```

The config above looks different from that of Telescope’s. The reason for this is that Lightline is not written in Lua, and so to hook into its configuration requires using [the Vim namespace](https://github.com/nanotee/nvim-lua-guide#the-vim-namespace) within the Lua standard library:

> Neovim exposes a global `vim` variable which serves as an entry point to interact with its APIs from Lua. It provides users with an extended “standard library” of functions as well as various sub-modules.

`vim.g` is a [meta-accessor](https://github.com/nanotee/nvim-lua-guide#using-meta-accessors-1) that lets you interact with Vim’s global variable. The config above sets a new global variable named `lightline` to an empty object.

Go ahead and save your `lightline.lua` file before sourcing it within your namespace directory’s `init.lua` file:

```lua
require('YOUR_NAMESPACE/telescope')
require('YOUR_NAMESPACE/lightline')
```

My `init.lua` currently reads:

```lua
require('jakewies/telescope')
require('jakewies/lightline')
```

Save `init.lua` with `:w` and source it with `:so %`.

One last thing we want to do is hide the Vim Mode text at the bottom left of our window. This is no longer necessary since Lightline is showing us the mode too.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yice7n7naogbibzcfod5.png)

In your `init.vim` file, append the following setting to your General Settings:

```vim
" ...general settings
set noshowmode
```

Excellent! That wraps up the Lightline section for now. If you want to view documentation on Lightline in the future, you can do so within Neovim using:

```markup
:h lightline
```

### vim-fugitive

If ever there was a “blue chip” plugin in the Vim ecosystem, [`vim-fugitive`](https://github.com/tpope/vim-fugitive) would probably be it:

> A Git wrapper so awesome, it should be illegal.

It has taken some getting used to, but the integrations provided by `vim-fugitive` are powerful. Let’s install it. You know the drill by now:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" vim-fugitive
Plug 'tpope/vim-fugitive'

call plug#end()
```

There’s no configuration required for this plugin. Just save, source, install and you’re off to the races.

I have a very familiar workflow with Git via the command line. Trying to re-train my brain to utilize `vim-fugitive` is going to take some time. You might be in a similar boat, so let me give you some helpful tips.

`:Git`

This is the “crown jewel” (straight from the Docs!) of `vim-fugitive`. As you may have come to recognize with these types of commands, you can use `<Tab>` autocomplete to see what else it can do:

```vim
:Git <Tab>
```

And there is a lot you can do! In fact, it’s a little overwhelming, but if you’re familiar with Git, then you will find `vim-fugitive` is pretty intuitive:

```vim
" view status of current branch
:Git status

" view available branches
:Git branch

" stash work
:Git stash

" stage changes for commit
:Git add

" commit staged changes
:Git commit

" push committed changes
:Git push
```

Entering `:Git` with no command will bring up [the `fugitive-summary` window](https://github.com/tpope/vim-fugitive/blob/69f5fcbd459c113181d18a0ea2641cd47c9e0318/doc/fugitive.txt#L50). I actually like this feature a lot. You will see a list of untracked, staged and unstaged files along with the current branch name. Placing your cursor over these files will let you run certain “maps” like staging/unstaging, discarding changes, toggling inline diffs etc.

For more info on what maps are available within the `fugitive-summary` window, enter:

```markup
:h fugitive-maps
```

Or [check out this link](https://github.com/tpope/vim-fugitive/blob/69f5fcbd459c113181d18a0ea2641cd47c9e0318/doc/fugitive.txt#L258).

`vim-fugitive` can integrate with Lightline to display the current working branch. Let’s set that up. In our placeholder `lightline.lua` file from before, let’s add some configuration:

```lua
vim.g.lightline = {
	active = {
		left = {
			{ 'mode', 'past' },
			{ 'gitbranch', 'filename', 'modified' }
		}
	},
	component_function = {
		gitbranch = 'fugitive#head'
	}
}
```

There’s a lot going on here, but know that this comes straight out of Lightline’s `README` in the [Advanced Configuration](https://github.com/itchyny/lightline.vim#advanced-configuration) section. In short, we define a component named `gitbranch` and set it equal to `fugitive#head`, and then add the component to the left side of the status line.

Save and source the file. If you are working in a `git` project (pssst. [you should!](https://www.jakewiesler.com/blog/managing-dotfiles)) , you will see the branch name on the bottom left!

For more information on `vim-fugitive` enter the following command in Neovim:

```markup
:h fugitive
```

### gitsigns

One thing I sorely miss from VSCode is the ability to view new, modified and deleted lines in a git project. Neovim doesn’t support this by default, so we’ll use [`gitsigns`](https://github.com/lewis6991/gitsigns.nvim):

> Super fast git decorations implemented purely in lua/teal.

Yes please. In our `init.vim`:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" gitsigns
Plug 'lewis6991/gitsigns.nvim'

call plug#end()
```

Note: `gitsigns` requires `nvim-lua/plenary.nvim` as well. Since we’ve installed this with Telescope (see above), we’re covered.

You know what to do - Save, source and `:PlugInstall`.

Gitsigns requires calling a `setup` function in your configuration for it to work. Let’s create a `gitsigns.lua` file in our namespaced directory:

```shell
nvim ~/.config/nvim/lua/YOUR_NAMESPACE/gitsigns.lua
```

And inside of this file we’ll get things started:

```lua
require('gitsigns').setup {}
```

Passing no options will init the plugin using the default settings, which can be found [here](https://github.com/lewis6991/gitsigns.nvim#usage).

Now let’s add `gitsigns.lua` to our namespace directory’s `init.lua` file:

```lua
require('YOUR_NAMESPACE/telescope')
require('YOUR_NAMESPACE/lightline')
require('YOUR_NAMESPACE/gitsigns')
```

Mine looks like this:

```lua
require('jakewies/telescope')
require('jakewies/lightline')
require('jakewies/gitsigns')
```

Source this file and you’re good to go! You can see gitsigns in action by navigating to a project on your machine that is version controlled with git. Any tracked file that has been modified will display that modification with a colored pipe on the left hand side of Vim.

Although this is technically what we want, the placing of the sign is a little off. This is because we need to tell Vim to show diagnostics signs in the number column, rather than to the left of the number. Open up your `init.vim` file and add a new `set` option to your general settings:

```vim
" ... general settings
set signcolumn=number
```

Save and source your `init.vim` file and view the changes. Now your signs should be displayed in the number column, rather than to the left. To learn more about the `signcolumn` option, run the following command in Neovim:

```vim
:h signcolumn
```

Git signs also supports a [Git blame feature](https://git-scm.com/docs/git-blame) very similar to VSCode, and it’s a feature I desperately miss. When you are in a project that uses Git, keeping your cursor over a line for a certain period of time will cause some virtual text to display in a dim color. The text will read the name of the developer that last modified the line, the modification date and the commit message.

I love this feature, but it’s not a requirement. `vim-fugitive` has a `:Git blame` command that solves the same use case, but I prefer the virtual text. To enable it, simply add the following to your Git signs configuration:

```lua
require('gitsigns').setup {
	-- signs config
	current_line_blame = true,
	current_line_blame_opts = {
		virt_text_pos = 'right_align'
	}
}
```

And that’s it! Saving and sourcing the file will result in some inline Git blame goodness.

## Language support

At this point we have some general settings applied that make Neovim easier to work with. We have some plugins installed that improve our productivity. But we still haven’t done anything about language support.

In text editors like VSCode, this isn’t a huge concern. Some languages like JavaScript and HTML are supported out of the box. Others, like Python and C++, have dedicated extensions that make your life a lot easier. These extensions provide [Intellisense](https://code.visualstudio.com/docs/editor/intellisense), code completion, formatting, linting and more.

Extensions like this are possible because VSCode supports the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/), a Microsoft-created standard for defining communications between language tooling and text editors.

Before the Language Server Protocol (LSP), language tools and text editors had no uniform way of talking with each other. Each language tool needed a separate implementation for each editor, and each editor needed to adapt to each tool.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ry42dm7bkd0m522rpi05.png)

LSP provides a reliable standard so language tools can be built once, and editors that support LSP will be able to utilize them.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lh3441ubs6k64oh9e1ri.png)

Neovim supports LSP natively as of `v0.5.0`, meaning we can get the same benefits VSCode provides, albeit with a “little” configuration.

There is a second option to Neovim’s native LSP called [`coc.nvim`](https://github.com/neoclide/coc.nvim). It has been around for a while and a lot of people use it with success. I showed up to the Neovim party around the time that native LSP became a thing, so I never really tried `coc`. I’ve heard that it is much easier to configure, but comes with significant features that you may or may not need.

I like the idea of opting for the native solution when possible, and even though it can be a pain to configure at the moment, the fact that the core team is supporting LSP means that in the future things should improve. For this reason, we’ll opt for native LSP, but feel free to check out `coc` if things get dicey.

Note: In order for the rest of this section to work for you, make sure you are on `v.0.5.0` of Neovim or above.

### Lspconfig

To get started with Neovim’s native LSP, we need to install a few plugins. The first is [`nvim-lspconfig`](https://github.com/neovim/nvim-lspconfig). Per the README, this plugin:

> Handles automatically launching and initializing language servers that are installed on your system.

We can install it the same we install any other plugin in our `init.vim` file:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" LSP
Plug 'neovim/nvim-lspconfig'

call plug#end()
```

Remember to save, source and `:PlugInstall`!

### Install a language server

The next step is installing the necessary language server(s) for your development environment. This is equivalent to installing a VSCode language extension. [Neovim lists available language servers here](https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md). Find one that supports your language of choice, and **install it on your machine**.

For example, I work with Typescript every day, so I need to install a Typescript language server. The list of available servers tells me that I should install [tsserver](https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md#tsserver). It can be installed via `npm` globally like so:

```shell
npm install -g typescript typescript-language-server
```

A few things to note here:

1. You are not installing the language server as a Neovim plugin. You are installing the server as a global package on your computer.
2. Not all language servers are installed the same. `tsserver` can be installed via `npm`, but others may require different installation methods.
3. Always refer to [the list of available language servers](https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md) to find the one you need. If installation instructions are not provided, look at the server’s official `README`.

### Configure the language server

After you have successfully installed your language server(s), it’s time to tell Neovim about them. This is going to be the most challenging aspect of setting up native LSP. I am still trying to understand all the moving parts!

Let’s create a new `.lua` file in our namespace directory called `lsp.lua`:

```markup
nvim ~/.config/nvim/lua/YOUR_NAMESPACE/lsp.lua
```

This file will hold all of our LSP-related configuration. Inside, we need to add some boilerplate code. Note that the code we are adding here is not only [documented on the lspconfig README](https://github.com/neovim/nvim-lspconfig#keybindings-and-completion), but it’s also [the recommended configuration](https://github.com/mjlbach/defaults.nvim/blob/a203b26da856d190809bf1addf66455488335739/init.lua#L202) by [Michael Lingelbach](https://github.com/mjlbach), one of Neovim’s core team members, who has worked heavily on the native LSP implementation.

I will walk through setting up this file step-by-step using `tsserver` is an example.

```lua
-- lspconfig
require'lspconfig'.tsserver.setup {}
```

The code above is a minimal representation of how we can get Neovim’s LSP working with `tsserver`. This works just fine, however we can improve on this:

```lua
-- lspconfig
local nvim_lsp = require('lspconfig')
local servers = { 'tsserver' }

for _, lsp in ipairs(servers) do
	nvim_lsp[lsp].setup {}
end
```

The code above is now capable of setting up any arbitrary amount of servers defined in the `servers` variable. Installing more than one server? This will effectively loop through each one and wire it up to `nvim_lsp`.

For example, maybe you added a few more language servers to your setup:

```lua
local servers = {'clangd', 'pyright', 'tsserver'}
```

The only thing that has to change in your code is the `servers` variable. Everything else can stay the same!

Note: `ipairs` is a `lua` function that returns index-value pairs. We don’t need the index, hence the `_`.

Each server’s `setup` function is capable of accepting a special `on_attach` function that runs when a server attaches to your current Neovim buffer. Think of the current buffer as the current file you are editing in Neovim. Based on the filetype, you can have LSP configured to do certain things.

```lua
-- lspconfig
local nvim_lsp = require('lspconfig')
local servers = { 'tsserver' }

local on_attach = function(client, bufnr)
	-- Do things when language server attaches to
	-- the current buffer
end

for _, lsp in ipairs(servers) do
	nvim_lsp[lsp].setup {
		on_attach = on_attach
	}
end
```

The code above creates a new `on_attach` function and passes it to each server in our list of `servers`. But what to put in it?

The [Keybindings and completion](https://github.com/neovim/nvim-lspconfig#keybindings-and-completion) section of the lspconfig `README` illustrates adding some basic remaps that will work across every language server. Let’s add these:

```lua
-- lspconfig
local nvim_lsp = require('lspconfig')
local servers = { 'tsserver' }

local on_attach = function(client, bufnr)
  local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
  local opts = { noremap=true, silent=true }

  -- our first remap
  buf_set_keymap('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
end

for _, lsp in ipairs(servers) do
    nvim_lsp[lsp].setup {
        on_attach = on_attach
    }
end
```

This is a lot to digest. Let’s walk through it one step at a time.

1. We define a function named `buf_set_keymap` that accepts a variable number of arguments, denoted by the `…`.

   - `buf_set_keymap` is an abstraction over the `vim.api.nvim_buf_set_keymap` function.

2. We define a table named `opts` that holds some basic options for all of our remaps.

   - `opts.noremap` means “no recursive mapping”
   - `opts.silent` means the command will not be echoed in the command line

3. We call `buf_set_keymap` for the first time, passing in 4 parameters.
   - `'n'` tells LSP that this remap can run in NORMAL MODE
   - `'gD'` is the remap itself. The letters that you will type to perform some action.
   - `'<cmd>lua vim.lsp.buf.declaration()<CR>'` is the actual command you want to perform. Without the remap, you would have to write this every time. `<cmd>` is like typing `:`. `<CR>` denotes a carriage return, such as hitting `ENTER`.
   - `opts` is the options table we declared earlier

Our first remap is `'gD'`. Typing it will be equivalent to typing `:lua vim.lsp.buf.declaration()` followed by `ENTER`. This remap will jump to the definition of the symbol under your cursor while in Neovim. It’s a really helpful remap that I use all the time.

Now that you have the basics down, let’s add the rest of the remaps defined in the lspconfig `README`:

```lua
-- lspconfig
local nvim_lsp = require('lspconfig')
local servers = { 'tsserver' }

local on_attach = function(client, bufnr)
    local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
    local opts = { noremap=true, silent=true }

    buf_set_keymap('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
    buf_set_keymap('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts)
    buf_set_keymap('n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
    buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
    buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
    buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
    buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
    buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
    buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
    buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
    buf_set_keymap('n', '<space>e', '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
    buf_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
    buf_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
    buf_set_keymap('n', '<space>q', '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)
    buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
end

for _, lsp in ipairs(servers) do
	nvim_lsp[lsp].setup {
		on_attach = on_attach
	}
end
```

Now your language server(s) will be able to run these remaps whenever you please. There’s a lot here, and there’s a good chance you won’t use them all. But it’s helpful to have them in case you do. I like to keep my `lsp.lua` file close while I work, referencing these remaps in case I want to perform some actions. Here are a few that I like to use:

1. `gD` - see the declaration of a symbol (function, variable, etc.)
2. `K` - see some info about a symbol in a hover window
3. `gr` - show a list of references to a symbol
4. `<space>e` - show any issues on the current line

Once the rest of our remaps have been added to our `on_attach` function, let’s save the file and require it inside of `init.lua`:

```lua
require('YOUR_NAMESPACE/telescope')
require('YOUR_NAMESPACE/lightline')
require('YOUR_NAMESPACE/gitsigns')
require('YOUR_NAMESPACE/lsp')
```

To give your remaps a test drive, make sure to save and source this file. Hop into a project that utilizes one of your language servers and you’re all set! If you're not sure that LSP is working in a particular file, type the command `:LspInfo`.

You can read more about Neovim's built-in LSP using the command:

```markup
:h lsp
```

### Autocompletion

Next on the list of language-related items to configure is autocompletion. Autocompletion is a huge productivity improvement for developers, and it does not come baked into Neovim. We need to install a few plugins to make it happen.

In our `init.vim`:

```vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" Autocompletion
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'L3MON4D3/LuaSnip'
Plug 'saadparwaiz1/cmp_luasnip'
Plug 'onsails/lspkind-nvim'

call plug#end()
```

Yes we just installed 5 plugins for autocompletion. Don’t freak out. They each have their purpose:

1. `nvim-cmp` -[The main plugin](https://github.com/hrsh7th/nvim-cmp)
2. `cmp-nvim-lsp` - [A dependency of nvim-cmp](https://github.com/hrsh7th/cmp-nvim-lsp)
3. `LuaSnip` - [Autocomplete your snippets](https://github.com/L3MON4D3/LuaSnip).
4. `cmp_luasnip` - [Snippet completion source](https://github.com/saadparwaiz1/cmp_luasnip). Not quite sure what it does? But it’s recommended in [`defaults.nvim`](https://github.com/mjlbach/defaults.nvim/blob/cddcfb7821f4799df8767f362fc3d024be4fd7d1/init.lua#L40).
5. `lspkind-nvim` - [Autocompletion icons](https://github.com/onsails/lspkind-nvim)

Save, source and `:PlugInstall`.

Now that all of these plugins are installed, let’s glue them together. But, instead of configuring it within its own `.lua` file, we’ll want to colocate it with our LSP config. You'll see why.

Ino our `lsp.lua` file:

```lua
-- ...lspconfig

-- nvim-cmp
local cmp = require('cmp')
local lspkind = require('lspkind')
local luasnip = require('luasnip')

-- better autocompletion experience
vim.o.completeopt = 'menuone,noselect'

cmp.setup {
	-- Format the autocomplete menu
	formatting = {
		format = lspkind.cmp_format()
	},
	mapping = {
        -- Use Tab and shift-Tab to navigate autocomplete menu
        ['<Tab>'] = function(fallback)
            if cmp.visible() then
              cmp.select_next_item()
            elseif luasnip.expand_or_jumpable() then
              luasnip.expand_or_jump()
            else
              fallback()
            end
          end,
        ['<S-Tab>'] = function(fallback)
            if cmp.visible() then
              cmp.select_prev_item()
            elseif luasnip.jumpable(-1) then
              luasnip.jump(-1)
            else
              fallback()
            end
        end,
        ['<CR>'] = cmp.mapping.confirm {
            behavior = cmp.ConfirmBehavior.Replace,
            select = true,
        },
    },
    snippet = {
        expand = function(args)
            luasnip.lsp_expand(args.body)
        end
    },
    sources = {
        { name = 'nvim_lsp' },
        { name = 'luasnip' },
    },
}
```

A lot to take in. Let’s walk it out.

1. [`formatting.format`](https://github.com/hrsh7th/nvim-cmp#formattingformat-type-funentry-cmpentry-vim_item-vimcompleteditem-vimcompleteditem) - Formats the autocomplete menu using `lspkind`, a plugin we installed earlier. This adds icons to keywords and variables to autocomplete options.
2. [`mapping`](https://github.com/hrsh7th/nvim-cmp#mapping-type-tablestring-funfallback-function) - Custom keybindings for better autocomplete menu navigation. Press `<Tab>` to navigate forward, and `<Shift-Tab>` to navigate backwards. Press `<CR>` (Enter) to confirm selection.
3. `snippet` - Enables snippet autocompletion via `LuaSnip`
4. [`sources`](https://github.com/hrsh7th/nvim-cmp#sources-type-tablecmpsourceconfig) - Declare autocompletion sources.

After configuring the autocompletion plugins, we need to wire it all up with our language servers:

```lua
-- lspconfig
local on_attach = function(client, bufnr)
  -- ...
end

local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)

local servers = { 'tsserver' }
for _, lsp in ipairs(servers) do
    nvim_lsp[lsp].setup {
        capabilities = capabilities,
        on_attach = on_attach,
    }
end

-- ...nvim-cmp
```

The above code adds a new property named `capabilities` to each language server. All together, `lsp.lua` now looks like this:

```lua
-- lspconfig
local nvim_lsp = require('lspconfig')
local servers = { 'tsserver' }

local on_attach = function(client, bufnr)
    local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
    local opts = { noremap=true, silent=true }

    buf_set_keymap('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
    buf_set_keymap('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts)
    buf_set_keymap('n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
    buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
    buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
    buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
    buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
    buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
    buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
    buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
    buf_set_keymap('n', '<space>e', '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
    buf_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
    buf_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
    buf_set_keymap('n', '<space>q', '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)
    buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
end

local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)

local servers = { 'tsserver' }
for _, lsp in ipairs(servers) do
    nvim_lsp[lsp].setup {
        capabilities = capabilities,
        on_attach = on_attach,
    }
end

-- nvim-cmp
local cmp = require('cmp')
local lspkind = require('lspkind')
local luasnip = require('luasnip')

-- better autocompletion experience
vim.o.completeopt = 'menuone,noselect'

cmp.setup {
	-- Format the autocomplete menu
	formatting = {
		format = lspkind.cmp_format()
	},
	mapping = {
        -- Use Tab and shift-Tab to navigate autocomplete menu
        ['<Tab>'] = function(fallback)
            if cmp.visible() then
              cmp.select_next_item()
            elseif luasnip.expand_or_jumpable() then
              luasnip.expand_or_jump()
            else
              fallback()
            end
          end,
        ['<S-Tab>'] = function(fallback)
            if cmp.visible() then
              cmp.select_prev_item()
            elseif luasnip.jumpable(-1) then
              luasnip.jump(-1)
            else
              fallback()
            end
        end,
        ['<CR>'] = cmp.mapping.confirm {
            behavior = cmp.ConfirmBehavior.Replace,
            select = true,
        },
    },
    snippet = {
        expand = function(args)
            luasnip.lsp_expand(args.body)
        end
    },
    sources = {
        { name = 'nvim_lsp' },
        { name = 'luasnip' },
    },
}
```

### Treesitter

The last plugin to improve Neovim's language support is `treesitter`. [We will be installing it](https://github.com/nvim-treesitter/nvim-treesitter#installation) for improved syntax highlighting. In your `init.vim`, add the following to you list of declared plugins:

```vim:init.vim
" ...general settings

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" Treesitter
Plug 'nvim-treesitter/nvim-treesitter', { 'do': ':TSUpdate' }

call plug#end()
```

Next, create a `treesitter.lua` file in your namespace directory:

```markup
nvim ~/.config/nvim/lua/YOUR_NAMESPACE/treesitter.lua
```

Inside this file we'll add the following:

```lua
local treesitter = require('nvim-treesitter.configs')

treesitter.setup {
    highlight = {
        enable = true
    }
}
```

Finally, update your `init.lua` to include `treesitter.lua`:

```lua
require('YOUR_NAMESPACE/telescope')
require('YOUR_NAMESPACE/lightline')
require('YOUR_NAMESPACE/gitsigns')
require('YOUR_NAMESPACE/lsp')
require('YOUR_NAMESPACE/treesitter')
```

And that's it! Source your `init.vim` and you should see an improved syntax highlighting experience.

## Remaps

The following will not be a tutorial on remaps themselves, as remaps in Vim are a topic worthy of a separate article. There are also plenty of great resources on the topic:

- [Mapping Keys in Vim](<https://vim.fandom.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_1)>)
- [Understand Vim Mappings and Create Your Own Shortcuts!](https://alldrops.info/posts/vim-drops/2018-05-15_understand-vim-mappings-and-create-your-own-shortcuts/)
- [Basic Mapping - Learn Vimscript the Hard Way](https://learnvimscriptthehardway.stevelosh.com/chapters/03.html)

Instead, I will quickly explain what they are and why you would want to use them, and ultimately hope to inspire you to seek out your own.

Let's begin.

Over the course of this article, we've covered many plugins that introduce their own commands:

```vim
" Telescope
:Telescope find_files
:Telescope buffers
:Telescope live_grep

" vim-fugitive
:Git
:Git add
:Git status
:Git blame

" lspconfig
:LspInfo
```

On top of these plugin-specific commands, Vim has its own set of commands. It can be cumbersome to type out the entire command every time you want to use it. Instead, you can "map" these commands to a pattern of keystrokes. These are remaps.

I declare my remaps inside `init.vim`. Here's an example of one of my favorites:

```vim
" ...general settings

" ...plugins

" remaps
nnoremap <C-p> :Telescope find_files<Cr>
```

The remap above is `<C-p>`, or `CTRL+p`. Each time I type this pattern, the command `:Telescope find_files<Cr>` will run. `<Cr>` must be explicitly declared in order for the command to be "entered", i.e - with `<Enter>`.

Here are a few others:

```vim
" ...general settings

" ...plugins

" remaps
let mapleader = ' '

nnoremap <leader>h :wincmd h<Cr>
nnoremap <leader>j :wincmd j<Cr>
nnoremap <leader>k :wincmd k<Cr>
nnoremap <leader>l :wincmd l<Cr>

nnoremap <C-p> :Telescope find_files<Cr>
```

A `mapleader` is a special character you can appoint to "lead" your remaps. This both simplifies remap keystrokes and prevents unwanted remap collisions. It defaults to `\`, but here I'm setting it to an empty space, `' '`.

`<leader>h` is equivalent to typing the `<Space>` bar followed by the letter `h`. Doing so will run the command `:wincmd h<Cr>`.

I believe remaps are one of those things you'll undoubtedly look for as you notice pain points in your own workflow. It can be helpful to puruse others' Vim configurations and see what they use for inspiration.

## Tips and tricks

1. **Use relative line numbers**

Your Neovim terminal supports line numbers with the following setting:

```vim
set number
```

When you get more advanced with your Vim movements, you'll look for ways to move vertically faster. One of the ways to do this is to explicitly type the line number you want to go to. This is easier with relative line numbers:

```vim
set number
set relativenumber
```

How does it work? The line your cursor is currently on will be denoted "line 0". The rest of the lines above and below will be numbered relative to their distance from line 0. For example, the line directly below your cursor will be line 1. So will the line directly above. To jump to line 1 below, type the number `1` plus the letter `j`. To go to line 1 above, type the number `1` plus the letter `k`.

2. **Alias the `nvim` command**

An alias is a shortcut defined in your terminal shell’s settings that allows you to run regular commands with custom names. An example alias that many people use is:

```bash
alias gc="git checkout"
```

Instead of typing `git checkout` every time I want to check out a branch, I just need to type `gc`. My terminal knows that when I enter `gc`, what I really mean is `git checkout`.

Defining your aliases is dependent on which terminal shell you are using. There are a number of shells that people use these days, like `bash`, `zsh` and `fish`. I use `zsh`, but you may not be. If you don’t know what shell you are using, you can run the following command in your terminal:

```shell
echo $SHELL
```

Aliases are defined in your shell’s configuration file. For bash users, that would be `.bashrc`. For zsh users, `.zshrc`. etc. etc. Your mileage may vary. Locate your specific file, and add the following:

```bash
alias vim ="nvim"
```

Now anytime you run the command `vim`, you will really be running the command `nvim`. If you still want access to the original `vim` command, you can prefix it with a `\` character:

```shell
\vim
```

The command above will use the unaliased `vim` command. The more you know, right?

3. **Save your Neovim configuration in Git**

This is a major key! [I've written a blog post on this very topic](https://jakewiesler.com/blog/managing-dotfiles). I suggest you take a look. Your Neovim configuration is a subset of a larger group of configuration files dubbed "dotfiles". Dotfiles are the life blood of a good development environment. Make sure you keep yours portable!

4. **Experiment!**

Programming is way more fun when you're being efficient. Neovim can get you there over time, but you have to be willing to try new things. New plugins, new remaps, etc.

5. **Keep an eye on the community**

Neovim has an active [subreddit](https://www.reddit.com/r/neovim) and [forum](https://neovim.discourse.group/). These are great places to learn and find new plugins to experiment with.

## Conclusion

Phew! If you've stuck with me to the end, I appreciate you! This was a massive article, one of the biggest I've ever written. We discussed settings, plugins, color schemes, language support, autocompletion and remaps. I hope you learned something!

If you're curious to see my configuration, you can checkout [my dotfiles on GitHub, specifically the `nvim` directory](https://github.com/jakewies/.dotfiles/tree/main/nvim/.config/nvim). And if you're just getting started on your Vim journey, please let me know! It's been a challenging and exciting one for me, and I'm only getting started.