diff options
author | Jesse Harris <zigford@gmail.com> | 2019-04-13 21:24:56 +1000 |
---|---|---|
committer | w0rp <w0rp@users.noreply.github.com> | 2019-04-13 12:24:56 +0100 |
commit | 2ed53108c4f000e0a36a79c4317dad4fdfb545fe (patch) | |
tree | 85c7e046cab44b4e4a776b215c1378aaeec528a0 /ale_linters/powershell | |
parent | d7395906ba0f9aca2fb1a6c32df236bfd4ab32f6 (diff) | |
download | ale-2ed53108c4f000e0a36a79c4317dad4fdfb545fe.zip |
Linter for powershell syntax errors (#2413)
* Linter for powershell syntax errors
Diffstat (limited to 'ale_linters/powershell')
-rwxr-xr-x | ale_linters/powershell/powershell.vim | 91 | ||||
-rw-r--r-- | ale_linters/powershell/psscriptanalyzer.vim | 37 |
2 files changed, 95 insertions, 33 deletions
diff --git a/ale_linters/powershell/powershell.vim b/ale_linters/powershell/powershell.vim new file mode 100755 index 00000000..51ded71d --- /dev/null +++ b/ale_linters/powershell/powershell.vim @@ -0,0 +1,91 @@ +" Author: Jesse Harris - https://github.com/zigford +" Description: This file adds support for powershell scripts synatax errors + +call ale#Set('powershell_powershell_executable', 'pwsh') + +function! ale_linters#powershell#powershell#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'powershell_powershell_executable') +endfunction + +" Some powershell magic to show syntax errors without executing the script +" thanks to keith hill: +" https://rkeithhill.wordpress.com/2007/10/30/powershell-quicktip-preparsing-scripts-to-check-for-syntax-errors/ +function! ale_linters#powershell#powershell#GetCommand(buffer) abort + let l:script = ['Param($Script); + \ trap {$_;continue} & { + \ $Contents = Get-Content -Path $Script; + \ $Contents = [string]::Join([Environment]::NewLine, $Contents); + \ [void]$ExecutionContext.InvokeCommand.NewScriptBlock($Contents); + \ };'] + + return ale#powershell#RunPowerShell( + \ a:buffer, 'powershell_powershell', l:script) +endfunction + +" Parse powershell error output using regex into a list of dicts +function! ale_linters#powershell#powershell#Handle(buffer, lines) abort + let l:output = [] + " Our 3 patterns we need to scrape the data for the dicts + let l:patterns = [ + \ '\v^At line:(\d+) char:(\d+)', + \ '\v^(At|\+| )@!.*', + \ '\vFullyQualifiedErrorId : (\w+)', + \] + + let l:matchcount = 0 + + for l:match in ale#util#GetMatches(a:lines, l:patterns) + " We want to work with 3 matches per syntax error + let l:matchcount = l:matchcount + 1 + + if l:matchcount == 1 || str2nr(l:match[1]) + " First match consists of 2 capture groups, and + " can capture the line and col + if exists('l:item') + " We may be here because the last syntax + " didn't emit a code, and so only had 2 + " matches + call add(l:output, l:item) + let l:matchcount = 1 + endif + + let l:item = { + \ 'lnum': str2nr(l:match[1]), + \ 'col': str2nr(l:match[2]), + \ 'type': 'E', + \} + elseif l:matchcount == 2 + " Second match[0] grabs the full line in order + " to handles the text + let l:item['text'] = l:match[0] + else + " Final match handles the code, however + " powershell only emits 1 code for all errors + " so, we get the final code on the last error + " and loop over the previously added items to + " append the code we now know + call add(l:output, l:item) + unlet l:item + + if len(l:match[1]) > 0 + for l:i in l:output + let l:i['code'] = l:match[1] + endfor + endif + + " Reset the matchcount so we can begin gathering + " matches for the next syntax error + let l:matchcount = 0 + endif + endfor + + return l:output +endfunction + +call ale#linter#Define('powershell', { +\ 'name': 'powershell', +\ 'executable_callback': 'ale_linters#powershell#powershell#GetExecutable', +\ 'command_callback': 'ale_linters#powershell#powershell#GetCommand', +\ 'output_stream': 'stdout', +\ 'callback': 'ale_linters#powershell#powershell#Handle', +\}) diff --git a/ale_linters/powershell/psscriptanalyzer.vim b/ale_linters/powershell/psscriptanalyzer.vim index 8440d771..4794d9d8 100644 --- a/ale_linters/powershell/psscriptanalyzer.vim +++ b/ale_linters/powershell/psscriptanalyzer.vim @@ -13,37 +13,6 @@ function! ale_linters#powershell#psscriptanalyzer#GetExecutable(buffer) abort return ale#Var(a:buffer, 'powershell_psscriptanalyzer_executable') endfunction -" Write a powershell script to a temp file for execution -" return the command used to execute it -function! s:TemporaryPSScript(buffer, input) abort - let l:filename = 'script.ps1' - " Create a temp dir to house our temp .ps1 script - " a temp dir is needed as powershell needs the .ps1 - " extension - let l:tempdir = ale#util#Tempname() . (has('win32') ? '\' : '/') - let l:tempscript = l:tempdir . l:filename - " Create the temporary directory for the file, unreadable by 'other' - " users. - call mkdir(l:tempdir, '', 0750) - " Automatically delete the directory later. - call ale#command#ManageDirectory(a:buffer, l:tempdir) - " Write the script input out to a file. - call ale#util#Writefile(a:buffer, a:input, l:tempscript) - - return l:tempscript -endfunction - -function! ale_linters#powershell#psscriptanalyzer#RunPowerShell(buffer, command) abort - let l:executable = ale_linters#powershell#psscriptanalyzer#GetExecutable( - \ a:buffer) - let l:tempscript = s:TemporaryPSScript(a:buffer, a:command) - - return ale#Escape(l:executable) - \ . ' -Exe Bypass -NoProfile -File ' - \ . ale#Escape(l:tempscript) - \ . ' %t' -endfunction - " Run Invoke-ScriptAnalyzer and output each linting message as 4 seperate lines " for each parsing function! ale_linters#powershell#psscriptanalyzer#GetCommand(buffer) abort @@ -60,8 +29,10 @@ function! ale_linters#powershell#psscriptanalyzer#GetCommand(buffer) abort \ $_.Message; \ $_.RuleName}'] - return ale_linters#powershell#psscriptanalyzer#RunPowerShell( - \ a:buffer, l:script) + return ale#powershell#RunPowerShell( + \ a:buffer, + \ 'powershell_psscriptanalyzer', + \ l:script) endfunction " add every 4 lines to an item(Dict) and every item to a list |