summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xale_linters/powershell/powershell.vim91
-rw-r--r--ale_linters/powershell/psscriptanalyzer.vim37
-rw-r--r--autoload/ale/powershell.vim32
-rw-r--r--doc/ale-powershell.txt15
-rw-r--r--doc/ale-supported-languages-and-tools.txt1
-rw-r--r--doc/ale.txt1
-rw-r--r--supported-tools.md1
-rwxr-xr-xtest/handler/test_powershell_handler.vader62
8 files changed, 207 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
diff --git a/autoload/ale/powershell.vim b/autoload/ale/powershell.vim
new file mode 100644
index 00000000..8c163206
--- /dev/null
+++ b/autoload/ale/powershell.vim
@@ -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
diff --git a/doc/ale-powershell.txt b/doc/ale-powershell.txt
index 743b5ae3..c28ef9ea 100644
--- a/doc/ale-powershell.txt
+++ b/doc/ale-powershell.txt
@@ -3,6 +3,21 @@ ALE PowerShell Integration *ale-powershell-options*
===============================================================================
+powershell *ale-powershell-powershell*
+
+g:ale_powershell_powershell_executable *g:ale_powershell_powershell_executable*
+ *b:ale_powershell_powershell_executable*
+ Type: String
+ Default: `'pwsh'`
+
+ This variable can be changed to use a different executable for powershell.
+
+>
+ " Use powershell.exe rather than the default pwsh
+ let g:ale_powershell_powershell_executable = 'powershell.exe'
+>
+
+===============================================================================
psscriptanalyzer *ale-powershell-psscriptanalyzer*
Installation
diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt
index eff2e607..70b86a03 100644
--- a/doc/ale-supported-languages-and-tools.txt
+++ b/doc/ale-supported-languages-and-tools.txt
@@ -319,6 +319,7 @@ Notes:
* Pony
* `ponyc`
* PowerShell
+ * `powershell`
* `psscriptanalyzer`
* Prolog
* `swipl`
diff --git a/doc/ale.txt b/doc/ale.txt
index 6d78fd89..54e3c455 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -2070,6 +2070,7 @@ documented in additional help files.
pony....................................|ale-pony-options|
ponyc.................................|ale-pony-ponyc|
powershell............................|ale-powershell-options|
+ powershell..........................|ale-powershell-powershell|
psscriptanalyzer....................|ale-powershell-psscriptanalyzer|
prolog..................................|ale-prolog-options|
swipl.................................|ale-prolog-swipl|
diff --git a/supported-tools.md b/supported-tools.md
index 74cea65c..108e08f2 100644
--- a/supported-tools.md
+++ b/supported-tools.md
@@ -328,6 +328,7 @@ formatting.
* Pony
* [ponyc](https://github.com/ponylang/ponyc)
* PowerShell
+ * [powershell](https://github.com/PowerShell/PowerShell) :floppy_disk
* [psscriptanalyzer](https://github.com/PowerShell/PSScriptAnalyzer) :floppy_disk
* Prolog
* [swipl](https://github.com/SWI-Prolog/swipl-devel)
diff --git a/test/handler/test_powershell_handler.vader b/test/handler/test_powershell_handler.vader
new file mode 100755
index 00000000..635bcd20
--- /dev/null
+++ b/test/handler/test_powershell_handler.vader
@@ -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'
+ \ ])