Anyone working in the land of systems and infrastructure automation these days will no doubt have heard of Terraform. At work, we use it for pretty much everything, and being a long time vim user and fan, I wanted to work a little bit smarter as I spend a lot of time writing Terraform, Ansible, Shell scripts, Makefiles and Markdown documentation. Having never used ctags before, but knowing what they were, I decided to see if and how I could start using them to my advantage.
Here's what I came up with for the ctags configuration:
» cat ~/.ctags
--recurse
--exclude=.git
--exclude=.terraform
--regex-make=/^([^# \t]*):/\1/t,target/
--langdef=ansible
--langmap=ansible:.yml.yaml
--regex-ansible=/^[ \t]*-[ \t]*name:[ \t]*(.+)/\1/t,tasks/
--regex-ansible=/^[ \t]*-[ \t]*hosts:[ \t]*(.+)/\1/h,hosts/
--langdef=terraform
--langmap=terraform:.tf.tfvars
--regex-terraform=/^resource "(.+)" "(.+)"/\1 \2/r,resources/
--regex-terraform=/^variable "(.+)"/\1/v,variables/
--regex-terraform=/^module "(.+)"/\1/m,modules/
--regex-terraform=/^output "(.+)"/\1/o,outputs/
--regex-terraform=/^([a-z0-9_]+) =/\1/f,tfvars/
--languages=ansible,python,sh,terraform,make
--langdef=markdown
--langmap=markdown:.md
--regex-markdown=/^#[ \t]+(.*)/\1/h,Heading_L1/
--regex-markdown=/^##[ \t]+(.*)/\1/i,Heading_L2/
--regex-markdown=/^###[ \t]+(.*)/\1/k,Heading_L3/
--regex-sh=/readonly (.*)=.*/\1/c,constants/
As you can probably see, setting this up just requires reading the documentation and working out the correct regex for each language.
Next, I just needed to run ctags
in the root of my repo to generate the tags
file. This is just a plain text file you can look at e.g.:
» grep ^aws_instance tags
aws_instance bastion terraform/layers/support/modules/bastion/bastion.tf /^resource "aws_instance" "bastion" {$/;" r
aws_instance cm-controlnode terraform/layers/cloudera/modules/cm-controlnode/cm-controlnode.tf /^resource "aws_instance" "cm-controlnode" {$/;" r
aws_instance cm-datanode terraform/layers/cloudera/modules/cm-datanode/cm-datanode.tf /^resource "aws_instance" "cm-datanode" {$/;" r
aws_instance cm-edgenode terraform/layers/cloudera/modules/cm-edgenode/cm-edgenode.tf /^resource "aws_instance" "cm-edgenode" {$/;" r
I then wanted to know how to use these, and a quick google pointed me at the majutsushi/tagbar plugin. After reading the docs and a some testing I came up with the following vim configuration:
" CtrlP
nnoremap <silent> <leader>o :CtrlP<CR>
nnoremap <silent> <leader>t :CtrlPTag<cr>
nnoremap <silent> <leader>b :CtrlPBuffer<cr>
nnoremap <silent> <leader>l :CtrlPLine<cr>
nnoremap <silent> <leader>b :TagbarToggle<CR>
nnoremap <silent> ; :CtrlPBuffer<CR>
" ctags/tagbar
nnoremap <leader>f :ta<space>
" Auto open the TagBar when file is supported
autocmd FileType * nested :call tagbar#autoopen(0)
let g:tagbar_compact = 1
let g:tagbar_type_ansible = {
\ 'ctagstype' : 'ansible',
\ 'kinds' : [
\ 't:tasks',
\ 'h:hosts'
\ ],
\ 'sort' : 0
\ }
let g:tagbar_type_terraform = {
\ 'ctagstype' : 'terraform',
\ 'kinds' : [
\ 'r:resources',
\ 'm:modules',
\ 'o:outputs',
\ 'v:variables',
\ 'f:tfvars'
\ ],
\ 'sort' : 0
\ }
let g:tagbar_type_make = {
\ 'kinds':[
\ 'm:macros',
\ 't:targets'
\ ]
\}
let g:tagbar_type_sh = {
\ 'kinds':[
\ 'f:functions',
\ 'c:constants'
\ ]
\}
let g:tagbar_type_markdown = {
\ 'ctagstype' : 'markdown',
\ 'kinds' : [
\ 'h:Heading_L1',
\ 'i:Heading_L2',
\ 'k:Heading_L3'
\ ]
\ }
With the above configuration, I can use the CtrlP plugin with <leader>t
to fuzzy find tags, and <leader>b
to toggle the tagbar on the right open and closed. With the configuration above, the tagbar will open when a supported file is opened. Also, I can use the ta
vim command to jump to a tag. For example, to go to my bastion instance resource, I can use:
:ta aws_instance bast<tab>
or
<leader>f aws_instance bast<tab>
To fuzzy search the tags, I use:
<leader>t aws_inst bast
To find the bastion host instance resource.
The end result:
Here's a quick demo:
As an additional bonus, if you have the hashivim/vim-terraform plugin you can have automatically run terraform fmt
on the file you are working on when you save it by adding the following to your ~/.vimrc
:
let g:terraform_fmt_on_save = 1
I'm sure this isn't the final configuration I'll use, but as always, my dotfiles can be viewed on GitHub.
If you have any comments or suggestions on how I can improve this, please shout at me on twitter: @z0mbix