Boost Your Productivity with Terraform Autocompletion in Vim
Tags: vim, terraform, devops, tutorial
Published: 2024-01-02 06:19:58

## Pure Vim 

Every so often, when the struggle of jumping between IDE windows, tabs, and extensions catches up to me, I step back and settle into a terminal window with a beautiful, yet simple Vim configuration for my daily DevOps development. In this guide, I will illustrate the process of configuring autocompletion for Terraform code in Vim. This walkthrough uses [Vim Plug](https://github.com/junegunn/vim-plug) for plugin installations, so ensure this is downloaded before continuing (or use another plugin manager).

## Configuring Terraform's Language Server

### What is an LSP?

For us to take advantage of Terraform's auto-completion features, we need to make use of a Language Server Protocol (LSP), which defines the protocol used between an editor or IDE and a language server. This provides language features like auto-complete, go to the definition, find all references, etc. By harnessing a few plugins, we can streamline our Terraform development efforts in pure Vim.

### Installing the Terraform LSP

To install the [Terraform LSP](https://github.com/hashicorp/terraform-ls), we can either utilize Homebrew to download the releases or reference Hashicorp's [Official Packaging Guide](https://www.hashicorp.com/official-packaging-guide) for further installation instructions. If you choose to use Homebrew, here is the command!

```sh
brew install hashicorp/tap/terraform-ls
```

### Installing Vim Plugins

Now that we have the Terraform LSP installed, we need another LSP for language-specific autocompletion. This is where Conquer of Completion (CoC) shines. Before installing CoC, you will need to make sure that you have the necessary dependencies on your system, namely Vim 8 and above and node version 14.14 and above.

 To install CoC, simply include the following line in your `vimrc` and give the command `:PlugInstall`

```sh
Plug 'neoclide/coc.nvim'
```

For CoC's autocompletion to work, there are numerous configurations. While discussing all of them is beyond this guide's scope, my preferred `vimrc` is provided below:

```sh
"CoC Settings
" Use tab for trigger completion with characters ahead and navigate.
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config.
inoremap <silent><expr> <TAB>
      \ pumvisible() ? "\<C-n>" :
      \ <SID>check_back_space() ? "\<TAB>" :
      \ coc#refresh()
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>"
 
function! s:check_back_space() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction
 
" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice.
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
                              \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
"Ultisnips Settings
let g:UltiSnipsExpandTrigger="<tab>"
let g:UltiSnipsJumpForwardTrigger="<c-b>"
let g:UltiSnipsJumpBackwardTrigger="<c-z>"
 
" If you want :UltiSnipsEdit to split your window.
let g:UltiSnipsEditSplit="vertical"
 
"coc-snippets Settings
"inoremap <silent><expr> <TAB>
"      \ coc#pum#visible() ? coc#_select_confirm() :
"      \ coc#expandableOrJumpable() ? "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand-jump',''])\<CR>" :
"      \ CheckBackspace() ? "\<TAB>" :
"      \ coc#refresh()
"
"function! CheckBackspace() abort
"  let col = col('.') - 1
"  return !col || getline('.')[col - 1]  =~# '\s'
"endfunction
"
"let g:coc_snippet_next = '<tab>'
```

To edit CoC's settings after installation, enter Vim and type the command `:CoCConfig` or configure ~/.vim/coc-settings.json as follows:

```json
{
	"languageserver": {
		"terraform": {
			"command": "terraform-ls",
			"args": ["serve"],
			"filetypes": [
				"terraform",
				"tf"
			],
			"initializationOptions": {},
			"settings": {}
		}
	}
}
```

With Terraform and CoC LSPs installed, let's move on to Terraform's plugins: [vim-terraform](https://github.com/hashivim/vim-terraform) for file types, [syntactic](https://github.com/vim-syntastic/syntastic) for linting, and [vim-terraform-completion](https://github.com/juliosueiras/vim-terraform-completion) for auto-completion.

To install these plugins, add the following lines to your `vimrc` before running `:PlugInstall`:

```sh
Plug 'hashivim/vim-terraform'
Plug 'vim-syntastic/syntastic'
Plug 'juliosueiras/vim-terraform-completion'
```

## Final `vimrc`

Now that our plugins are installed, let's add the following to create our final `vimrc`:

```sh
""""""""""""""""""""""""""""""
" => Terraform 
""""""""""""""""""""""""""""""
" Set filetype to HCL for files with .hcl extension
autocmd BufRead,BufNewFile *.hcl set filetype=hcl

" Set filetype to Terraform for files with .tf and .tfvars extensions
autocmd BufRead,BufNewFile *.tf,*.tfvars set filetype=terraform

" Set filetype to JSON for files with .tfstate and .tfstate.backup extensions
autocmd BufRead,BufNewFile *.tfstate,*.tfstate.backup set filetype=json

" Allow vim-terraform to automatically format *.tf and *.tfvars files with terraform fmt
let g:terraform_fmt_on_save=1

" Allow vim-terraform to align settings automatically with Tabularize
let g:terraform_align=1

" Install plugins
call plug#begin('~/.vim/plugged')
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'hashivim/vim-terraform'
Plug 'vim-syntastic/syntastic'
Plug 'juliosueiras/vim-terraform-completion'
call plug#end()

" Syntastic Config
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*

let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_open = 1
let g:syntastic_check_on_wq = 0

" (Optional)Remove Info(Preview) window
set completeopt-=preview

" (Optional)Hide Info(Preview) window after completions
autocmd CursorMovedI * if pumvisible() == 0|pclose|endif
autocmd InsertLeave * if pumvisible() == 0|pclose|endif

" (Optional) Enable terraform plan to be include in filter
let g:syntastic_terraform_tffilter_plan = 1

" (Optional) Default: 0, enable(1)/disable(0) plugin's keymapping
let g:terraform_completion_keys = 1

" (Optional) Default: 1, enable(1)/disable(0) terraform module registry completion
let g:terraform_registry_module_completion = 0

"CoC Settings
" Use tab for trigger completion with characters ahead and navigate.
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config.
inoremap <silent><expr> <TAB>
      \ pumvisible() ? "\<C-n>" :
      \ <SID>check_back_space() ? "\<TAB>" :
      \ coc#refresh()
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>"

function! s:check_back_space() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice.
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
                              \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
"Ultisnips Settings
let g:UltiSnipsExpandTrigger="<tab>"
let g:UltiSnipsJumpForwardTrigger="<c-b>"
let g:UltiSnipsJumpBackwardTrigger="<c-z>"

" If you want :UltiSnipsEdit to split your window.
let g:UltiSnipsEditSplit="vertical"

"coc-snippets Settings
"inoremap <silent><expr> <TAB>
"      \ coc#pum#visible() ? coc#_select_confirm() :
"      \ coc#expandableOrJumpable() ? "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand-jump',''])\<CR>" :
"      \ CheckBackspace() ? "\<TAB>" :
"      \ coc#refresh()
"
"function! CheckBackspace() abort
"  let col = col('.') - 1
"  return !col || getline('.')[col - 1]  =~# '\s'
"endfunction
"
"let g:coc_snippet_next = '<tab>'
```

### Test it Out

<video src="https://github.com/RoseSecurity/DNS-Fender/assets/72598486/6cdbace0-62c0-4ae3-9649-045bb84d73ca" width="320" height="240" controls></video>

## Conclusion

I hope that this guide was an informative journey to improving your DevOps development workflow. If you have any questions, enjoyed the content, or would like to check out more of my work, feel free to drop a comment below and visit my [GitHub](https://github.com/RoseSecurity).

--- 

{% github RoseSecurity/RoseSecurity %}