From 88fa0b9294c6e907e8e2e8bee2ca593dff91b473 Mon Sep 17 00:00:00 2001 From: Keith Maxwell Date: Thu, 23 May 2019 14:12:36 +0100 Subject: [PATCH] Add a terraform linter This linter uses the check functionality built into terraform. ALE already has a fixer using `terraform fmt` but this doesn't provide error messages. ALE already has a linter using `tflint` but this requires an extra application to be installed. For example this linter will give a warning that ! is an illegal character in the line below: variable "example" !{} This linter runs the buffer through the command below and parses the output: terraform fmt -no-color -check=true - This commit includes a basic implementation, documentation and tests. The only option is to control which executable is run. Tested with: $ terraform -version Terraform v0.11.13 --- ale_linters/terraform/terraform.vim | 49 +++++++++++++++++++ doc/ale-hcl.txt | 2 +- doc/ale-terraform.txt | 14 +++++- doc/ale.txt | 3 +- ...terraform_terraform_command_callback.vader | 9 ++++ test/handler/test_terraform_handler.vader | 34 +++++++++++++ 6 files changed, 108 insertions(+), 3 deletions(-) create mode 100755 ale_linters/terraform/terraform.vim create mode 100644 test/command_callback/test_terraform_terraform_command_callback.vader create mode 100755 test/handler/test_terraform_handler.vader diff --git a/ale_linters/terraform/terraform.vim b/ale_linters/terraform/terraform.vim new file mode 100755 index 00000000..0429cb7a --- /dev/null +++ b/ale_linters/terraform/terraform.vim @@ -0,0 +1,49 @@ +" Author: Keith Maxwell +" Description: terraform fmt to check for errors + +call ale#Set('terraform_terraform_executable', 'terraform') + +function! ale_linters#terraform#terraform#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'terraform_terraform_executable') +endfunction + +function! ale_linters#terraform#terraform#GetCommand(buffer) abort + return ale#Escape(ale_linters#terraform#terraform#GetExecutable(a:buffer)) + \ . ' fmt -no-color --check=true -' +endfunction + +function! ale_linters#terraform#terraform#Handle(buffer, lines) abort + let l:head = '^Error running fmt: In : ' + let l:output = [] + let l:patterns = [ + \ l:head.'At \(\d\+\):\(\d\+\): \(.*\)$', + \ l:head.'\(.*\)$' + \] + + for l:match in ale#util#GetMatches(a:lines, l:patterns) + if len(l:match[2]) > 0 + call add(l:output, { + \ 'lnum': str2nr(l:match[1]), + \ 'col': str2nr(l:match[2]), + \ 'text': l:match[3], + \ 'type': 'E', + \}) + else + call add(l:output, { + \ 'lnum': line('$'), + \ 'text': l:match[1], + \ 'type': 'E', + \}) + endif + endfor + + return l:output +endfunction + +call ale#linter#Define('terraform', { +\ 'name': 'terraform', +\ 'output_stream': 'stderr', +\ 'executable': function('ale_linters#terraform#terraform#GetExecutable'), +\ 'command': function('ale_linters#terraform#terraform#GetCommand'), +\ 'callback': 'ale_linters#terraform#terraform#Handle', +\}) diff --git a/doc/ale-hcl.txt b/doc/ale-hcl.txt index 8060ac44..59b0a9da 100644 --- a/doc/ale-hcl.txt +++ b/doc/ale-hcl.txt @@ -5,7 +5,7 @@ ALE HCL Integration *ale-hcl-options* =============================================================================== terraform-fmt *ale-hcl-terraform-fmt* -See |ale-terraform-fmt| for information about the available options. +See |ale-terraform-fmt-fixer| for information about the available options. =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-terraform.txt b/doc/ale-terraform.txt index 49a55028..387fd732 100644 --- a/doc/ale-terraform.txt +++ b/doc/ale-terraform.txt @@ -3,7 +3,7 @@ ALE Terraform Integration *ale-terraform-options* =============================================================================== -fmt *ale-terraform-fmt* +terraform-fmt-fixer *ale-terraform-fmt-fixer* g:ale_terraform_fmt_executable *g:ale_terraform_fmt_executable* *b:ale_terraform_fmt_executable* @@ -20,6 +20,18 @@ g:ale_terraform_fmt_options *g:ale_terraform_fmt_options* Default: `''` +=============================================================================== +terraform *ale-terraform-terraform* + +g:ale_terraform_terraform_executable *g:ale_terraform_terraform_executable* + *b:ale_terraform_terraform_executable* + + Type: |String| + Default: `'terraform'` + + This variable can be changed to use a different executable for terraform. + + =============================================================================== tflint *ale-terraform-tflint* diff --git a/doc/ale.txt b/doc/ale.txt index 4bb34947..6b051d3c 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2246,7 +2246,8 @@ documented in additional help files. tcl.....................................|ale-tcl-options| nagelfar..............................|ale-tcl-nagelfar| terraform...............................|ale-terraform-options| - fmt...................................|ale-terraform-fmt| + terraform-fmt-fixer...................|ale-terraform-fmt-fixer| + terraform.............................|ale-terraform-terraform| tflint................................|ale-terraform-tflint| tex.....................................|ale-tex-options| chktex................................|ale-tex-chktex| diff --git a/test/command_callback/test_terraform_terraform_command_callback.vader b/test/command_callback/test_terraform_terraform_command_callback.vader new file mode 100644 index 00000000..fabd902d --- /dev/null +++ b/test/command_callback/test_terraform_terraform_command_callback.vader @@ -0,0 +1,9 @@ +" Based upon :help ale-development +Before: + call ale#assert#SetUpLinterTest('terraform', 'terraform') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'terraform', ale#Escape('terraform') . ' fmt -no-color --check=true -' diff --git a/test/handler/test_terraform_handler.vader b/test/handler/test_terraform_handler.vader new file mode 100755 index 00000000..976ce12a --- /dev/null +++ b/test/handler/test_terraform_handler.vader @@ -0,0 +1,34 @@ +Before: + " Load the file which defines the linter. + runtime ale_linters/terraform/terraform.vim + +After: + " Unload all linters again. + call ale#linter#Reset() + +Execute(The output should be correct): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 20, + \ 'type': 'E', + \ 'text': 'illegal char', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 14, + \ 'type': 'E', + \ 'text': 'literal not terminated', + \ }, + \ { + \ 'lnum': 1, + \ 'type': 'E', + \ 'text': 'object expected closing RBRACE got: EOF', + \ }, + \ ], + \ ale_linters#terraform#terraform#Handle(bufnr(''), [ + \ 'Error running fmt: In : At 1:20: illegal char', + \ 'Error running fmt: In : At 2:14: literal not terminated', + \ 'Error running fmt: In : object expected closing RBRACE got: EOF', + \ ])