Adds hdl_checker LSP support (#2804)

* Added hdl_checker support
* Added hdl_checker tests

HDL Checker searches for files when no config file is found, which could lead to very long searches when the user is not really on a project setting
This commit is contained in:
Andre Souto 2020-08-06 13:20:54 +01:00 committed by GitHub
parent 711c90c523
commit 5b3da60cea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 250 additions and 7 deletions

View file

@ -0,0 +1,5 @@
" Author: suoto <andre820@gmail.com>
" Description: Adds support for HDL Code Checker, which wraps vcom/vlog, ghdl
" or xvhdl. More info on https://github.com/suoto/hdl_checker
call ale#handlers#hdl_checker#DefineLinter('verilog')

View file

@ -0,0 +1,5 @@
" Author: suoto <andre820@gmail.com>
" Description: Adds support for HDL Code Checker, which wraps vcom/vlog, ghdl
" or xvhdl. More info on https://github.com/suoto/hdl_checker
call ale#handlers#hdl_checker#DefineLinter('vhdl')

View file

@ -0,0 +1,71 @@
" Author: suoto <andre820@gmail.com>
" Description: Adds support for HDL Code Checker, which wraps vcom/vlog, ghdl
" or xvhdl. More info on https://github.com/suoto/hdl_checker
call ale#Set('hdl_checker_executable', 'hdl_checker')
call ale#Set('hdl_checker_config_file', has('unix') ? '.hdl_checker.config' : '_hdl_checker.config')
call ale#Set('hdl_checker_options', '')
" Use this as a function so we can mock it on testing. Need to do this because
" test files are inside /testplugin (which refers to the ale repo), which will
" always have a .git folder
function! ale#handlers#hdl_checker#IsDotGit(path) abort
return ! empty(a:path) && isdirectory(a:path)
endfunction
" Sould return (in order of preference)
" 1. Nearest config file
" 2. Nearest .git directory
" 3. The current path
function! ale#handlers#hdl_checker#GetProjectRoot(buffer) abort
let l:project_root = ale#path#FindNearestFile(
\ a:buffer,
\ ale#Var(a:buffer, 'hdl_checker_config_file'))
if !empty(l:project_root)
return fnamemodify(l:project_root, ':h')
endif
" Search for .git to use as root
let l:project_root = ale#path#FindNearestDirectory(a:buffer, '.git')
if ale#handlers#hdl_checker#IsDotGit(l:project_root)
return fnamemodify(l:project_root, ':h:h')
endif
endfunction
function! ale#handlers#hdl_checker#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'hdl_checker_executable')
endfunction
function! ale#handlers#hdl_checker#GetCommand(buffer) abort
let l:command = ale#Escape(ale#handlers#hdl_checker#GetExecutable(a:buffer)) . ' --lsp'
" Add extra parameters only if config has been set
let l:options = ale#Var(a:buffer, 'hdl_checker_options')
if ! empty(l:options)
let l:command = l:command . ' ' . l:options
endif
return l:command
endfunction
" To allow testing
function! ale#handlers#hdl_checker#GetInitOptions(buffer) abort
return {'project_file': ale#Var(a:buffer, 'hdl_checker_config_file')}
endfunction
" Define the hdl_checker linter for a given filetype.
function! ale#handlers#hdl_checker#DefineLinter(filetype) abort
call ale#linter#Define(a:filetype, {
\ 'name': 'hdl-checker',
\ 'lsp': 'stdio',
\ 'language': a:filetype,
\ 'executable': function('ale#handlers#hdl_checker#GetExecutable'),
\ 'command': function('ale#handlers#hdl_checker#GetCommand'),
\ 'project_root': function('ale#handlers#hdl_checker#GetProjectRoot'),
\ 'initialization_options': function('ale#handlers#hdl_checker#GetInitOptions'),
\ })
endfunction

View file

@ -3,7 +3,10 @@ ALE Verilog/SystemVerilog Integration *ale-verilog-options*
=============================================================================== ===============================================================================
ALE can use four different linters for Verilog HDL: ALE can use five different linters for Verilog HDL:
HDL Checker
Using `hdl_checker --lsp`
iverilog: iverilog:
Using `iverilog -t null -Wall` Using `iverilog -t null -Wall`
@ -26,6 +29,9 @@ defining 'g:ale_linters' variable:
\ let g:ale_linters = {'systemverilog' : ['verilator'],} \ let g:ale_linters = {'systemverilog' : ['verilator'],}
< <
===============================================================================
General notes
Linters/compilers that utilize a "work" directory for analyzing designs- such Linters/compilers that utilize a "work" directory for analyzing designs- such
as ModelSim and Vivado- can be passed the location of these directories as as ModelSim and Vivado- can be passed the location of these directories as
part of their respective option strings listed below. This is useful for part of their respective option strings listed below. This is useful for
@ -40,6 +46,16 @@ changing. This can happen in the form of hangs or crashes. To help prevent
this when using these linters, it may help to run linting less frequently; for this when using these linters, it may help to run linting less frequently; for
example, only when a file is saved. example, only when a file is saved.
HDL Checker is an alternative for some of the issues described above. It wraps
around ghdl, Vivado and ModelSim/Questa and, when using the latter, it can
handle mixed language (VHDL, Verilog, SystemVerilog) designs.
===============================================================================
hdl-checker *ale-verilog-hdl-checker*
See |ale-vhdl-hdl-checker|
=============================================================================== ===============================================================================
iverilog *ale-verilog-iverilog* iverilog *ale-verilog-iverilog*

View file

@ -3,10 +3,10 @@ ALE VHDL Integration *ale-vhdl-options*
=============================================================================== ===============================================================================
ALE can use three different linters for VHDL: ALE can use four different linters for VHDL:
iverilog: ghdl:
Using `iverilog -t null -Wall` Using `ghdl --std=08`
ModelSim/Questa ModelSim/Questa
Using `vcom -2008 -quiet -lint` Using `vcom -2008 -quiet -lint`
@ -14,8 +14,15 @@ ALE can use three different linters for VHDL:
Vivado Vivado
Using `xvhdl --2008` Using `xvhdl --2008`
Note all linters default to VHDL-2008 support. This, and other options, can be HDL Checker
changed with each linter's respective option variable. Using `hdl_checker --lsp`
===============================================================================
General notes
ghdl, ModelSim/Questa and Vivado linters default to VHDL-2008 support. This,
and other options, can be changed with each linter's respective option
variable.
Linters/compilers that utilize a "work" directory for analyzing designs- such Linters/compilers that utilize a "work" directory for analyzing designs- such
as ModelSim and Vivado- can be passed the location of these directories as as ModelSim and Vivado- can be passed the location of these directories as
@ -31,6 +38,10 @@ changing. This can happen in the form of hangs or crashes. To help prevent
this when using these linters, it may help to run linting less frequently; for this when using these linters, it may help to run linting less frequently; for
example, only when a file is saved. example, only when a file is saved.
HDL Checker is an alternative for some of the issues described above. It wraps
around ghdl, Vivado and ModelSim/Questa and, when using the latter, it can
handle mixed language (VHDL, Verilog, SystemVerilog) designs.
=============================================================================== ===============================================================================
ghdl *ale-vhdl-ghdl* ghdl *ale-vhdl-ghdl*
@ -50,6 +61,60 @@ g:ale_vhdl_ghdl_options *g:ale_vhdl_ghdl_options*
This variable can be changed to modify the flags/options passed to 'ghdl'. This variable can be changed to modify the flags/options passed to 'ghdl'.
===============================================================================
hdl-checker *ale-vhdl-hdl-checker*
HDL Checker is a wrapper for VHDL/Verilg/SystemVerilog tools that aims to
reduce the boilerplate code needed to set things up. It can automatically
infer libraries for VHDL sources, determine the compilation order and provide
some static checks.
You can install it using pip:
>
$ pip install hdl-checker
`hdl-checker` will be run from a detected project root, determined by the
following methods, in order:
1. Find the first directory containing a configuration file (see
|g:ale_hdl_checker_config_file|)
2. If no configuration file can be found, find the first directory containing
a folder named `'.git'
3. If no such folder is found, use the directory of the current buffer
g:ale_hdl_checker_executable
*g:ale_hdl_checker_executable*
*b:ale_hdl_checker_executable*
Type: |String|
Default: `'hdl_checker'`
This variable can be changed to the path to the 'hdl_checker' executable.
g:ale_hdl_checker_options *g:ale_hdl_checker_options*
*b:ale_hdl_checker_options*
Type: |String|
Default: `''`
This variable can be changed to modify the flags/options passed to the
'hdl_checker' server startup command.
g:ale_hdl_checker_config_file *g:ale_hdl_checker_config_file*
*b:ale_hdl_checker_config_file*
Type: |String|
Default: `'.hdl_checker.config'` (Unix),
`'_hdl_checker.config'` (Windows)
This variable can be changed to modify the config file HDL Checker will try
to look for. It will also affect how the project's root directory is
determined (see |ale-vhdl-hdl-checker|).
More info on the configuration file format can be found at:
https://github.com/suoto/hdl_checker/wiki/Setting-up-a-project
=============================================================================== ===============================================================================
vcom *ale-vhdl-vcom* vcom *ale-vhdl-vcom*

View file

@ -2675,12 +2675,14 @@ documented in additional help files.
vala....................................|ale-vala-options| vala....................................|ale-vala-options|
uncrustify............................|ale-vala-uncrustify| uncrustify............................|ale-vala-uncrustify|
verilog/systemverilog...................|ale-verilog-options| verilog/systemverilog...................|ale-verilog-options|
hdl-checker...........................|ale-verilog-hdl-checker|
iverilog..............................|ale-verilog-iverilog| iverilog..............................|ale-verilog-iverilog|
verilator.............................|ale-verilog-verilator| verilator.............................|ale-verilog-verilator|
vlog..................................|ale-verilog-vlog| vlog..................................|ale-verilog-vlog|
xvlog.................................|ale-verilog-xvlog| xvlog.................................|ale-verilog-xvlog|
vhdl....................................|ale-vhdl-options| vhdl....................................|ale-vhdl-options|
ghdl..................................|ale-vhdl-ghdl| ghdl..................................|ale-vhdl-ghdl|
hdl-checker...........................|ale-vhdl-hdl-checker|
vcom..................................|ale-vhdl-vcom| vcom..................................|ale-vhdl-vcom|
xvhdl.................................|ale-vhdl-xvhdl| xvhdl.................................|ale-vhdl-xvhdl|
vim.....................................|ale-vim-options| vim.....................................|ale-vim-options|

View file

View file

@ -0,0 +1 @@

View file

@ -61,7 +61,7 @@ Execute(The defaults for the zsh filetype should be correct):
Execute(The defaults for the verilog filetype should be correct): Execute(The defaults for the verilog filetype should be correct):
" This filetype isn't configured with default, so we can test loading all " This filetype isn't configured with default, so we can test loading all
" available linters with this. " available linters with this.
AssertEqual ['iverilog', 'verilator', 'vlog', 'xvlog'], GetLinterNames('verilog') AssertEqual ['hdl-checker', 'iverilog', 'verilator', 'vlog', 'xvlog'], GetLinterNames('verilog')
let g:ale_linters_explicit = 1 let g:ale_linters_explicit = 1

View file

@ -0,0 +1,78 @@
Before:
call ale#assert#SetUpLinterTest('vhdl', 'hdl_checker')
Save g:ale_hdl_checker_config_file
Save g:ale_hdl_checker_options
let g:default_config_file = has('unix') ? '.hdl_checker.config' : '_hdl_checker.config'
After:
Restore
call ale#assert#TearDownLinterTest()
unlet! g:default_config_file
Execute(Get default initialization dict):
AssertEqual
\ {'project_file': g:default_config_file},
\ ale#handlers#hdl_checker#GetInitOptions(bufnr(''))
Execute(Get custom initialization dict):
let g:ale_hdl_checker_config_file = 'some_file_name'
AssertEqual
\ {'project_file': 'some_file_name'},
\ ale#handlers#hdl_checker#GetInitOptions(bufnr(''))
Execute(Get the checker command without extra user parameters):
AssertEqual
\ ale#Escape('hdl_checker') . ' --lsp',
\ ale#handlers#hdl_checker#GetCommand(bufnr(''))
Execute(Get the checker command with user configured parameters):
let g:ale_hdl_checker_options = '--log-level DEBUG'
AssertEqual
\ ale#Escape('hdl_checker') . ' --lsp --log-level DEBUG',
\ ale#handlers#hdl_checker#GetCommand(bufnr(''))
Execute(Customize executable):
let g:ale_hdl_checker_executable = '/some/other/path'
AssertEqual
\ ale#Escape('/some/other/path') . ' --lsp',
\ ale#handlers#hdl_checker#GetCommand(bufnr(''))
Execute(Get project root based on .git):
call ale#test#SetFilename('hdl_server/with_git/files/foo.vhd')
" Create .git file
silent! call mkdir(g:dir . '/hdl_server/with_git/.git')
AssertNotEqual '', glob(g:dir . '/hdl_server/with_git/.git')
AssertEqual
\ ale#path#Simplify(g:dir . '/hdl_server/with_git'),
\ ale#handlers#hdl_checker#GetProjectRoot(bufnr(''))
Execute(Get project root based on config file):
call ale#test#SetFilename('hdl_server/with_config_file/foo.vhd')
AssertEqual
\ ale#path#Simplify(g:dir . '/hdl_server/with_config_file'),
\ ale#handlers#hdl_checker#GetProjectRoot(bufnr(''))
Execute(Return no project root if neither .git or config file are found):
let g:call_count = 0
" Mock this command to avoid the test to find ale's own .git folder
function! ale#handlers#hdl_checker#IsDotGit(path) abort
let g:call_count += 1
return 0
endfunction
call ale#test#SetFilename('hdl_server/foo.vhd')
AssertEqual
\ '',
\ ale#handlers#hdl_checker#GetProjectRoot(bufnr(''))
AssertEqual g:call_count, 1
unlet! g:call_count