-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Linter for powershell syntax errors (#2413)
* Linter for powershell syntax errors
- Loading branch information
Showing
8 changed files
with
207 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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', | ||
\}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
" Author: zigford <zigford@gmail.com> | ||
" Description: Functions for integrating with Powershell linters. | ||
|
||
" 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#powershell#RunPowerShell(buffer, base_var_name, command) abort | ||
let l:executable = ale#Var(a:buffer, a:base_var_name . '_executable') | ||
let l:tempscript = s:TemporaryPSScript(a:buffer, a:command) | ||
|
||
return ale#Escape(l:executable) | ||
\ . ' -Exe Bypass -NoProfile -File ' | ||
\ . ale#Escape(l:tempscript) | ||
\ . ' %t' | ||
endfunction |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -319,6 +319,7 @@ Notes: | |
* Pony | ||
* `ponyc` | ||
* PowerShell | ||
* `powershell` | ||
* `psscriptanalyzer` | ||
* Prolog | ||
* `swipl` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
Before: | ||
runtime ale_linters/powershell/powershell.vim | ||
|
||
After: | ||
call ale#linter#Reset() | ||
|
||
Execute(The powershell handler should process syntax errors from parsing a powershell script): | ||
AssertEqual | ||
\ [ | ||
\ { | ||
\ 'lnum': 8, | ||
\ 'col': 29, | ||
\ 'type': 'E', | ||
\ 'text': 'Missing closing ''}'' in statement block or type definition.', | ||
\ 'code': 'ParseException', | ||
\ }, | ||
\ ], | ||
\ ale_linters#powershell#powershell#Handle(bufnr(''), [ | ||
\ "At line:8 char:29", | ||
\ "+ Invoke-Command -ScriptBlock {", | ||
\ "+ ~", | ||
\ "Missing closing '}' in statement block or type definition.", | ||
\ "At /home/harrisj/tester.ps1:5 char:5", | ||
\ "+ [void]$ExecutionContext.InvokeCommand.NewScriptBlock($Contents);", | ||
\ "+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", | ||
\ "+ CategoryInfo : NotSpecified: (:) [], ParseException", | ||
\ "+ FullyQualifiedErrorId : ParseException" | ||
\ ]) | ||
|
||
Execute(The powershell handler should process multiple syntax errors from parsing a powershell script): | ||
AssertEqual | ||
\ [ | ||
\ { | ||
\ 'lnum': 11, | ||
\ 'col': 31, | ||
\ 'type': 'E', | ||
\ 'text': 'The string is missing the terminator: ".', | ||
\ 'code': 'ParseException' | ||
\ }, | ||
\ { | ||
\ 'lnum': 3, | ||
\ 'col': 16, | ||
\ 'type': 'E', | ||
\ 'text': 'Missing closing ''}'' in statement block or type definition.', | ||
\ 'code': 'ParseException' | ||
\ }, | ||
\ ], | ||
\ ale_linters#powershell#powershell#Handle(bufnr(''), [ | ||
\ 'At line:11 char:31', | ||
\ '+ write-verbose ''deleted''', | ||
\ '+ ~', | ||
\ 'The string is missing the terminator: ".', | ||
\ 'At line:3 char:16', | ||
\ '+ invoke-command {', | ||
\ '+ ~', | ||
\ 'Missing closing ''}'' in statement block or type definition.', | ||
\ 'At /var/folders/qv/15ybvt050v9cgwrm7c95x4r4zc4qsg/T/vwhzIc8/1/script.ps1:1 char:150', | ||
\ '+ ... ontents); [void]$ExecutionContext.InvokeCommand.NewScriptBlock($Con ...', | ||
\ '+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', | ||
\ '+ CategoryInfo : NotSpecified: (:) [], ParseException', | ||
\ '+ FullyQualifiedErrorId : ParseException' | ||
\ ]) |