diff options
57 files changed, 1069 insertions, 58 deletions
@@ -1,4 +1,4 @@ -Copyright (c) 2016-2019, w0rp <devw0rp@gmail.com> +Copyright (c) 2016-2020, w0rp <devw0rp@gmail.com> All rights reserved. Redistribution and use in source and binary forms, with or without @@ -102,7 +102,7 @@ programs for checking the syntax and semantics of your programs. By default, linters will be re-run in the background to check your syntax when you open new buffers or as you make edits to your files. -The behaviour of linting can be configured with a variety of options, +The behavior of linting can be configured with a variety of options, documented in [the Vim help file](doc/ale.txt). For more information on the options ALE offers, consult `:help ale-options` for global options and `:help ale-integration-options` for options specified to particular linters. @@ -231,6 +231,9 @@ ALE supports "hover" information for printing brief information about symbols at the cursor taken from Language Server Protocol linters and `tsserver` with the `ALEHover` command. +Truncated information will be displayed when the cursor rests on a symbol by +default, as long as there are no problems on the same line. + The information can be displayed in a `balloon` tooltip in Vim or GVim by hovering your mouse over symbols. Mouse hovering is enabled by default in GVim, and needs to be configured for Vim 8.1+ in terminals. @@ -735,7 +738,7 @@ while you type. ALE uses a timeout which is cancelled and reset every time you type, and this delay can be increased so linters are run less often. See `:help g:ale_lint_delay` for more information. -If you don't wish to run linters while you type, you can disable that behaviour. +If you don't wish to run linters while you type, you can disable that behavior. Set `g:ale_lint_on_text_changed` to `never`. You won't get as frequent error checking, but ALE shouldn't block your ability to edit a document after you save a file, so the asynchronous nature of the plugin will still be an advantage. diff --git a/ale_linters/c/ccls.vim b/ale_linters/c/ccls.vim index 9e3dafe9..9f105712 100644 --- a/ale_linters/c/ccls.vim +++ b/ale_linters/c/ccls.vim @@ -3,6 +3,7 @@ call ale#Set('c_ccls_executable', 'ccls') call ale#Set('c_ccls_init_options', {}) +call ale#Set('c_build_dir', '') call ale#linter#Define('c', { \ 'name': 'ccls', @@ -10,5 +11,5 @@ call ale#linter#Define('c', { \ 'executable': {b -> ale#Var(b, 'c_ccls_executable')}, \ 'command': '%e', \ 'project_root': function('ale#handlers#ccls#GetProjectRoot'), -\ 'initialization_options': {b -> ale#Var(b, 'c_ccls_init_options')}, +\ 'initialization_options': {b -> ale#handlers#ccls#GetInitOpts(b, 'c_ccls_init_options')}, \}) diff --git a/ale_linters/c/cppcheck.vim b/ale_linters/c/cppcheck.vim index 309b2851..b671fc8b 100644 --- a/ale_linters/c/cppcheck.vim +++ b/ale_linters/c/cppcheck.vim @@ -10,9 +10,11 @@ function! ale_linters#c#cppcheck#GetCommand(buffer) abort let l:buffer_path_include = empty(l:compile_commands_option) \ ? ale#handlers#cppcheck#GetBufferPathIncludeOptions(a:buffer) \ : '' + let l:template = ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' return l:cd_command \ . '%e -q --language=c' + \ . l:template \ . ale#Pad(l:compile_commands_option) \ . ale#Pad(ale#Var(a:buffer, 'c_cppcheck_options')) \ . l:buffer_path_include diff --git a/ale_linters/cpp/ccls.vim b/ale_linters/cpp/ccls.vim index b265ff70..38f8df9c 100644 --- a/ale_linters/cpp/ccls.vim +++ b/ale_linters/cpp/ccls.vim @@ -3,6 +3,7 @@ call ale#Set('cpp_ccls_executable', 'ccls') call ale#Set('cpp_ccls_init_options', {}) +call ale#Set('c_build_dir', '') call ale#linter#Define('cpp', { \ 'name': 'ccls', @@ -10,5 +11,5 @@ call ale#linter#Define('cpp', { \ 'executable': {b -> ale#Var(b, 'cpp_ccls_executable')}, \ 'command': '%e', \ 'project_root': function('ale#handlers#ccls#GetProjectRoot'), -\ 'initialization_options': {b -> ale#Var(b, 'cpp_ccls_init_options')}, +\ 'initialization_options': {b -> ale#handlers#ccls#GetInitOpts(b, 'cpp_ccls_init_options')}, \}) diff --git a/ale_linters/cpp/cppcheck.vim b/ale_linters/cpp/cppcheck.vim index 7cd80dbc..2c832246 100644 --- a/ale_linters/cpp/cppcheck.vim +++ b/ale_linters/cpp/cppcheck.vim @@ -10,9 +10,11 @@ function! ale_linters#cpp#cppcheck#GetCommand(buffer) abort let l:buffer_path_include = empty(l:compile_commands_option) \ ? ale#handlers#cppcheck#GetBufferPathIncludeOptions(a:buffer) \ : '' + let l:template = ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' return l:cd_command \ . '%e -q --language=c++' + \ . l:template \ . ale#Pad(l:compile_commands_option) \ . ale#Pad(ale#Var(a:buffer, 'cpp_cppcheck_options')) \ . l:buffer_path_include diff --git a/ale_linters/objc/ccls.vim b/ale_linters/objc/ccls.vim index 51ecf056..7aef5325 100644 --- a/ale_linters/objc/ccls.vim +++ b/ale_linters/objc/ccls.vim @@ -3,6 +3,7 @@ call ale#Set('objc_ccls_executable', 'ccls') call ale#Set('objc_ccls_init_options', {}) +call ale#Set('c_build_dir', '') call ale#linter#Define('objc', { \ 'name': 'ccls', @@ -10,5 +11,5 @@ call ale#linter#Define('objc', { \ 'executable': {b -> ale#Var(b, 'objc_ccls_executable')}, \ 'command': '%e', \ 'project_root': function('ale#handlers#ccls#GetProjectRoot'), -\ 'initialization_options': {b -> ale#Var(b, 'objc_ccls_init_options')}, +\ 'initialization_options': {b -> ale#handlers#ccls#GetInitOpts(b, 'objc_ccls_init_options')}, \}) diff --git a/ale_linters/python/pyright.vim b/ale_linters/python/pyright.vim new file mode 100644 index 00000000..422ecd61 --- /dev/null +++ b/ale_linters/python/pyright.vim @@ -0,0 +1,43 @@ +call ale#Set('python_pyright_executable', 'pyright-langserver') +call ale#Set('python_pyright_config', {}) + +function! ale_linters#python#pyright#GetConfig(buffer) abort + let l:config = deepcopy(ale#Var(a:buffer, 'python_pyright_config')) + + if !has_key(l:config, 'python') + let l:config.python = {} + endif + + if type(l:config.python) is v:t_dict + " Automatically detect the virtualenv path and use it. + if !has_key(l:config.python, 'venvPath') + let l:venv = ale#python#FindVirtualenv(a:buffer) + + if !empty(l:venv) + let l:config.python.venvPath = l:venv + endif + endif + + " Automatically use the version of Python in virtualenv. + if type(get(l:config.python, 'venvPath')) is v:t_string + \&& !empty(l:config.python.venvPath) + \&& !has_key(l:config.python, 'pythonPath') + let l:config.python.pythonPath = ale#path#Simplify( + \ l:config.python.venvPath + \ . (has('win32') ? '/Scripts/python' : '/bin/python') + \) + endif + endif + + return l:config +endfunction + +call ale#linter#Define('python', { +\ 'name': 'pyright', +\ 'lsp': 'stdio', +\ 'executable': {b -> ale#Var(b, 'python_pyright_executable')}, +\ 'command': '%e --stdio', +\ 'project_root': function('ale#python#FindProjectRoot'), +\ 'completion_filter': 'ale#completion#python#CompletionItemFilter', +\ 'lsp_config': function('ale_linters#python#pyright#GetConfig'), +\}) diff --git a/ale_linters/verilog/hdl_checker.vim b/ale_linters/verilog/hdl_checker.vim new file mode 100644 index 00000000..b05d8565 --- /dev/null +++ b/ale_linters/verilog/hdl_checker.vim @@ -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') diff --git a/ale_linters/vhdl/hdl_checker.vim b/ale_linters/vhdl/hdl_checker.vim new file mode 100644 index 00000000..c9d306b3 --- /dev/null +++ b/ale_linters/vhdl/hdl_checker.vim @@ -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') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 2b5756e4..b00fc6a3 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -523,13 +523,46 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:doc = l:doc.value endif - call add(l:results, { + let l:result = { \ 'word': l:word, \ 'kind': ale#completion#GetCompletionSymbols(get(l:item, 'kind', '')), \ 'icase': 1, \ 'menu': get(l:item, 'detail', ''), \ 'info': (type(l:doc) is v:t_string ? l:doc : ''), - \}) + \} + + if has_key(l:item, 'additionalTextEdits') + let l:text_changes = [] + + for l:edit in l:item.additionalTextEdits + let l:range = l:edit.range + call add(l:text_changes, { + \ 'start': { + \ 'line': l:range.start.line + 1, + \ 'offset': l:range.start.character + 1, + \ }, + \ 'end': { + \ 'line': l:range.end.line + 1, + \ 'offset': l:range.end.character + 1, + \ }, + \ 'newText': l:edit.newText, + \}) + endfor + + let l:changes = [{ + \ 'fileName': expand('#' . l:buffer . ':p'), + \ 'textChanges': l:text_changes, + \}] + \ + let l:result.user_data = json_encode({ + \ 'codeActions': [{ + \ 'description': 'completion', + \ 'changes': l:changes, + \ }], + \ }) + endif + + call add(l:results, l:result) endfor if has_key(l:info, 'prefix') diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim index da554ef9..731e36f2 100644 --- a/autoload/ale/events.vim +++ b/autoload/ale/events.vim @@ -147,6 +147,10 @@ function! ale#events#Init() abort autocmd InsertLeave * if exists('*ale#engine#Cleanup') | call ale#virtualtext#ShowCursorWarning() | endif endif + if g:ale_hover_cursor + autocmd CursorHold * if exists('*ale#lsp#Send') | call ale#hover#ShowTruncatedMessageAtCursor() | endif + endif + if g:ale_close_preview_on_insert autocmd InsertEnter * if exists('*ale#preview#CloseIfTypeMatches') | call ale#preview#CloseIfTypeMatches('ale-preview') | endif endif diff --git a/autoload/ale/fixers/astyle.vim b/autoload/ale/fixers/astyle.vim index 04e4b69a..3a5a70a1 100644 --- a/autoload/ale/fixers/astyle.vim +++ b/autoload/ale/fixers/astyle.vim @@ -49,7 +49,7 @@ endfunction function! ale#fixers#astyle#Fix(buffer) abort let l:executable = ale#fixers#astyle#Var(a:buffer, 'executable') let l:proj_options = ale#fixers#astyle#FindProjectOptions(a:buffer) - let l:command = ' --stdin=' + let l:command = ' --stdin=' . ale#Escape(expand('#' . a:buffer)) return { \ 'command': ale#Escape(l:executable) diff --git a/autoload/ale/handlers/ccls.vim b/autoload/ale/handlers/ccls.vim index 1e2aa318..290f5852 100644 --- a/autoload/ale/handlers/ccls.vim +++ b/autoload/ale/handlers/ccls.vim @@ -17,3 +17,10 @@ function! ale#handlers#ccls#GetProjectRoot(buffer) abort " Fall back on default project root detection. return ale#c#FindProjectRoot(a:buffer) endfunction + +function! ale#handlers#ccls#GetInitOpts(buffer, init_options_var) abort + let l:build_dir = ale#c#GetBuildDirectory(a:buffer) + let l:init_options = empty(l:build_dir) ? {} : {'compilationDatabaseDirectory': l:build_dir} + + return extend(l:init_options, ale#Var(a:buffer, a:init_options_var)) +endfunction diff --git a/autoload/ale/handlers/cppcheck.vim b/autoload/ale/handlers/cppcheck.vim index 6d8fa15d..7f68ba67 100644 --- a/autoload/ale/handlers/cppcheck.vim +++ b/autoload/ale/handlers/cppcheck.vim @@ -44,16 +44,21 @@ endfunction function! ale#handlers#cppcheck#HandleCppCheckFormat(buffer, lines) abort " Look for lines like the following. " - " [test.cpp:5]: (error) Array 'a[10]' accessed at index 10, which is out of bounds - let l:pattern = '\v^\[(.+):(\d+)\]: \(([a-z]+)\) (.+)$' + "test.cpp:974:6: error: Array 'n[3]' accessed at index 3, which is out of bounds. [arrayIndexOutOfBounds]\ + " n[3]=3; + " ^ + let l:pattern = '\v^(\f+):(\d+):(\d+): (\w+): (.*) \[(\w+)\]\' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) if ale#path#IsBufferPath(a:buffer, l:match[1]) call add(l:output, { - \ 'lnum': str2nr(l:match[2]), - \ 'type': l:match[3] is# 'error' ? 'E' : 'W', - \ 'text': l:match[4], + \ 'lnum': str2nr(l:match[2]), + \ 'col': str2nr(l:match[3]), + \ 'type': l:match[4] is# 'error' ? 'E' : 'W', + \ 'sub_type': l:match[4] is# 'style' ? 'style' : '', + \ 'text': l:match[5], + \ 'code': l:match[6] \}) endif endfor diff --git a/autoload/ale/handlers/go.vim b/autoload/ale/handlers/go.vim index f17cd862..c969669d 100644 --- a/autoload/ale/handlers/go.vim +++ b/autoload/ale/handlers/go.vim @@ -6,9 +6,12 @@ " " Author: Ben Paxton <ben@gn32.uk> " Description: moved to generic Golang file from govet +" +" Author: mostfunkyduck <mostfunkyduck@protonmail.com> +" Description: updated to work with go 1.14 function! ale#handlers#go#Handler(buffer, lines) abort - let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? ?(.+)$' + let l:pattern = '\v^%(vet: )?([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? ?(.+)$' let l:output = [] let l:dir = expand('#' . a:buffer . ':p:h') diff --git a/autoload/ale/handlers/hdl_checker.vim b/autoload/ale/handlers/hdl_checker.vim new file mode 100644 index 00000000..36dbd259 --- /dev/null +++ b/autoload/ale/handlers/hdl_checker.vim @@ -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 + diff --git a/autoload/ale/handlers/markdownlint.vim b/autoload/ale/handlers/markdownlint.vim index 5cef20ee..6c273bd0 100644 --- a/autoload/ale/handlers/markdownlint.vim +++ b/autoload/ale/handlers/markdownlint.vim @@ -2,7 +2,7 @@ " Description: Adds support for markdownlint function! ale#handlers#markdownlint#Handle(buffer, lines) abort - let l:pattern=': \?\(\d\+\)\(:\(\d\+\)\?\)\? \(MD\d\{3}/[A-Za-z0-9-]\+\) \(.*\)$' + let l:pattern=': \?\(\d\+\)\(:\(\d\+\)\?\)\? \(MD\d\{3}/[A-Za-z0-9-/]\+\) \(.*\)$' let l:output=[] for l:match in ale#util#GetMatches(a:lines, l:pattern) diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim index 8fdd288c..a6462227 100644 --- a/autoload/ale/hover.vim +++ b/autoload/ale/hover.vim @@ -42,6 +42,8 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort \&& exists('*balloon_show') \&& ale#Var(l:options.buffer, 'set_balloons') call balloon_show(a:response.body.displayString) + elseif get(l:options, 'truncated_echo', 0) + call ale#cursor#TruncatedEcho(split(a:response.body.displayString, "\n")[0]) elseif g:ale_hover_to_preview call ale#preview#Show(split(a:response.body.displayString, "\n"), { \ 'filetype': 'ale-preview.message', @@ -89,11 +91,12 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort if type(l:result) is v:t_dict " If the result is an object, then it's markup content. - let l:result = [l:result.value] + let l:result = has_key(l:result, 'value') ? [l:result.value] : [] endif if type(l:result) is v:t_list " Replace objects with text values. + call filter(l:result, '!(type(v:val) is v:t_dict && !has_key(v:val, ''value''))') call map(l:result, 'type(v:val) is v:t_string ? v:val : v:val.value') let l:str = join(l:result, "\n") let l:str = substitute(l:str, '^\s*\(.\{-}\)\s*$', '\1', '') @@ -103,6 +106,8 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort \&& exists('*balloon_show') \&& ale#Var(l:options.buffer, 'set_balloons') call balloon_show(l:str) + elseif get(l:options, 'truncated_echo', 0) + call ale#cursor#TruncatedEcho(split(l:str, "\n")[0]) elseif g:ale_hover_to_preview call ale#preview#Show(split(l:str, "\n"), { \ 'filetype': 'ale-preview.message', @@ -156,6 +161,7 @@ function! s:OnReady(line, column, opt, linter, lsp_details) abort \ 'column': l:column, \ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0), \ 'show_documentation': get(a:opt, 'show_documentation', 0), + \ 'truncated_echo': get(a:opt, 'truncated_echo', 0), \} endfunction @@ -189,6 +195,16 @@ function! ale#hover#ShowAtCursor() abort call ale#hover#Show(l:buffer, l:pos[1], l:pos[2], {}) endfunction +function! ale#hover#ShowTruncatedMessageAtCursor() abort + let l:buffer = bufnr('') + let [l:info, l:loc] = ale#util#FindItemAtCursor(l:buffer) + + if empty(l:loc) + let l:pos = getpos('.') + call ale#hover#Show(l:buffer, l:pos[1], l:pos[2], {'truncated_echo': 1}) + endif +endfunction + " This function implements the :ALEDocumentation command. function! ale#hover#ShowDocumentationAtCursor() abort let l:buffer = bufnr('') diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index a85f06e2..f81b62be 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -44,7 +44,7 @@ let s:default_ale_linters = { \ 'help': [], \ 'perl': ['perlcritic'], \ 'perl6': [], -\ 'python': ['flake8', 'mypy', 'pylint'], +\ 'python': ['flake8', 'mypy', 'pylint', 'pyright'], \ 'rust': ['cargo'], \ 'spec': [], \ 'text': [], diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index 2509174e..ae8fd51d 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -196,14 +196,26 @@ function! s:UpdateCapabilities(conn, capabilities) abort let a:conn.capabilities.hover = 1 endif + if type(get(a:capabilities, 'hoverProvider')) is v:t_dict + let a:conn.capabilities.hover = 1 + endif + if get(a:capabilities, 'referencesProvider') is v:true let a:conn.capabilities.references = 1 endif + if type(get(a:capabilities, 'referencesProvider')) is v:t_dict + let a:conn.capabilities.references = 1 + endif + if get(a:capabilities, 'renameProvider') is v:true let a:conn.capabilities.rename = 1 endif + if type(get(a:capabilities, 'renameProvider')) is v:t_dict + let a:conn.capabilities.rename = 1 + endif + if !empty(get(a:capabilities, 'completionProvider')) let a:conn.capabilities.completion = 1 endif @@ -220,13 +232,25 @@ function! s:UpdateCapabilities(conn, capabilities) abort let a:conn.capabilities.definition = 1 endif + if type(get(a:capabilities, 'definitionProvider')) is v:t_dict + let a:conn.capabilities.definition = 1 + endif + if get(a:capabilities, 'typeDefinitionProvider') is v:true let a:conn.capabilities.typeDefinition = 1 endif + if type(get(a:capabilities, 'typeDefinitionProvider')) is v:t_dict + let a:conn.capabilities.typeDefinition = 1 + endif + if get(a:capabilities, 'workspaceSymbolProvider') is v:true let a:conn.capabilities.symbol_search = 1 endif + + if type(get(a:capabilities, 'workspaceSymbolProvider')) is v:t_dict + let a:conn.capabilities.symbol_search = 1 + endif endfunction " Update a connection's configuration dictionary and notify LSP servers diff --git a/autoload/ale/rename.vim b/autoload/ale/rename.vim index fbd6c2ad..64952e63 100644 --- a/autoload/ale/rename.vim +++ b/autoload/ale/rename.vim @@ -83,6 +83,31 @@ function! ale#rename#HandleTSServerResponse(conn_id, response) abort \}, v:true) endfunction +function! s:getChanges(workspace_edit) abort + let l:changes = {} + + if has_key(a:workspace_edit, 'changes') && !empty(a:workspace_edit.changes) + return a:workspace_edit.changes + elseif has_key(a:workspace_edit, 'documentChanges') + let l:document_changes = [] + + if type(a:workspace_edit.documentChanges) is v:t_dict + \ && has_key(a:workspace_edit.documentChanges, 'edits') + call add(l:document_changes, a:workspace_edit.documentChanges) + elseif type(a:workspace_edit.documentChanges) is v:t_list + let l:document_changes = a:workspace_edit.documentChanges + endif + + for l:text_document_edit in l:document_changes + let l:filename = l:text_document_edit.textDocument.uri + let l:edits = l:text_document_edit.edits + let l:changes[l:filename] = l:edits + endfor + endif + + return l:changes +endfunction + function! ale#rename#HandleLSPResponse(conn_id, response) abort if has_key(a:response, 'id') \&& has_key(s:rename_map, a:response.id) @@ -94,9 +119,9 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort return endif - let l:workspace_edit = a:response.result + let l:changes_map = s:getChanges(a:response.result) - if !has_key(l:workspace_edit, 'changes') || empty(l:workspace_edit.changes) + if empty(l:changes_map) call s:message('No changes received from server') return @@ -104,8 +129,8 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort let l:changes = [] - for l:file_name in keys(l:workspace_edit.changes) - let l:text_edits = l:workspace_edit.changes[l:file_name] + for l:file_name in keys(l:changes_map) + let l:text_edits = l:changes_map[l:file_name] let l:text_changes = [] for l:edit in l:text_edits diff --git a/doc/ale-javascript.txt b/doc/ale-javascript.txt index ea0a7089..13059eaa 100644 --- a/doc/ale-javascript.txt +++ b/doc/ale-javascript.txt @@ -138,7 +138,7 @@ g:ale_javascript_flow_use_respect_pragma By default, ALE will use the `--respect-pragma` option for `flow`, so only files with the `@flow` pragma are checked by ALE. This option can be set to - `0` to disable that behaviour, so all files can be checked by `flow`. + `0` to disable that behavior, so all files can be checked by `flow`. =============================================================================== diff --git a/doc/ale-python.txt b/doc/ale-python.txt index 93f1d668..60b0771d 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -598,6 +598,7 @@ g:ale_python_pylint_use_msg_id *g:ale_python_pylint_use_msg_id* Use message for output (e.g. I0011) instead of symbolic name of the message (e.g. locally-disabled). + =============================================================================== pyls *ale-python-pyls* @@ -683,6 +684,65 @@ g:ale_python_pyre_auto_pipenv *g:ale_python_pyre_auto_pipenv* =============================================================================== +pyright *ale-python-pyright* + +The `pyrlight` linter requires a recent version of `pyright` which includes +the `pyright-langserver` executable. You can install `pyright` on your system +through `npm` with `sudo npm install -g pyright` or similar. + +Refer to their README for installation instructions: +https://github.com/Microsoft/pyright + +`pyright` needs to know the path to your Python executable and probably a +virtualenv to run. ALE will try to detect these automatically. +See |g:ale_python_pyright_config|. + + +g:ale_python_pyright_executable *g:ale_python_pyright_executable* + *b:ale_python_pyright_executable* + Type: |String| + Default: `'pyright-langserver'` + + The executable for running `pyright`, which is typically installed globally. + + +g:ale_python_pyright_config *g:ale_python_pyright_config* + *b:ale_python_pyright_config* + Type: |Dictionary| + Default: `{}` + + Settings for configuring the `pyright` language server. + + See pyright's documentation for a full list of options: + https://github.com/microsoft/pyright/blob/master/docs/settings.md + + ALE will automatically try to set defaults for `venvPath` and `pythonPath` + so your project can automatically be checked with the right libraries. + You can override these settings with whatever you want in your ftplugin + file like so: > + + let b:ale_python_pyright_config = { + \ 'python': { + \ 'pythonPath': '/bin/python', + \ 'venvPath': '/other/dir', + \ }, + \} +< + If `venvPath` is set, but `pythonPath` is not, + ALE will use `venvPath . '/bin/python'` or similar as `pythonPath`. + + A commonly used setting for `pyright` is disabling language services + apart from type checking and "hover" (|ale-hover|), you can set this + setting like so, or use whatever other settings you want: > + + let b:ale_python_pyright_config = { + \ 'pyright': { + \ 'disableLanguageServices': v:true, + \ }, + \} +< + +=============================================================================== reorder-python-imports *ale-python-reorder_python_imports* g:ale_python_reorder_python_imports_executable diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index cfb1b6e2..6d495fa9 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -373,6 +373,7 @@ Notes: * `pylint`!! * `pyls` * `pyre` + * `pyright` * `reorder-python-imports` * `vulture`!! * `yapf` @@ -484,6 +485,7 @@ Notes: * VALA * `uncrustify` * Verilog + * `hdl-checker` * `iverilog` * `verilator` * `vlog` diff --git a/doc/ale-verilog.txt b/doc/ale-verilog.txt index 94b820b8..01af63c2 100644 --- a/doc/ale-verilog.txt +++ b/doc/ale-verilog.txt @@ -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: Using `iverilog -t null -Wall` @@ -26,6 +29,9 @@ defining 'g:ale_linters' variable: \ let g:ale_linters = {'systemverilog' : ['verilator'],} < +=============================================================================== +General notes + Linters/compilers that utilize a "work" directory for analyzing designs- such 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 @@ -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 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* diff --git a/doc/ale-vhdl.txt b/doc/ale-vhdl.txt index 3fea947d..c2870240 100644 --- a/doc/ale-vhdl.txt +++ b/doc/ale-vhdl.txt @@ -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: - Using `iverilog -t null -Wall` + ghdl: + Using `ghdl --std=08` ModelSim/Questa Using `vcom -2008 -quiet -lint` @@ -14,8 +14,15 @@ ALE can use three different linters for VHDL: Vivado Using `xvhdl --2008` -Note all linters default to VHDL-2008 support. This, and other options, can be -changed with each linter's respective option variable. + HDL Checker + 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 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 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* @@ -51,6 +62,60 @@ g:ale_vhdl_ghdl_options *g:ale_vhdl_ghdl_options* =============================================================================== +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* g:ale_vhdl_vcom_executable *g:ale_vhdl_vcom_executable* diff --git a/doc/ale.txt b/doc/ale.txt index 71f69ac0..a19c6060 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -127,7 +127,7 @@ their relevant options. * By showing balloons for your mouse cursor - |g:ale_set_balloons| Please consult the documentation for each option, which can reveal some other -ways of tweaking the behaviour of each way of displaying problems. You can +ways of tweaking the behavior of each way of displaying problems. You can disable or enable whichever options you prefer. Most settings can be configured for each buffer. (|b:| instead of |g:|), @@ -516,6 +516,10 @@ at the cursor taken from LSP linters. The following commands are supported: |ALEHover| - Print information about the symbol at the cursor. +Truncated information will be displayed when the cursor rests on a symbol by +default, as long as there are no problems on the same line. You can disable +this behavior by setting |g:ale_hover_cursor| to `0`. + If |g:ale_set_balloons| is set to `1` and your version of Vim supports the |balloon_show()| function, then "hover" information also show up when you move the mouse over a symbol in a buffer. Diagnostic information will take priority @@ -1048,9 +1052,27 @@ g:ale_history_log_output *g:ale_history_log_output* if you want to save on some memory usage. +g:ale_hover_cursor *g:ale_hover_cursor* + + Type: |Number| + Default: `1` + + If set to `1`, ALE will show truncated information in the echo line about + the symbol at the cursor automatically when the |CursorHold| event is fired. + The delay before requesting hover information is based on 'updatetime', as + with all |CursorHold| events. + + If there's a problem on the line where the cursor is resting, ALE will not + show any hover information. + + See |ale-hover| for more information on hover information. + + This setting must be set to `1` before ALE is loaded for this behavior + to be enabled. See |ale-lint-settings-on-startup|. + + g:ale_hover_to_preview *g:ale_hover_to_preview* *b:ale_hover_to_preview* - Type: |Number| Default: `0` @@ -1268,7 +1290,7 @@ g:ale_linters *g:ale_linters* \ 'help': [], \ 'perl': ['perlcritic'], \ 'perl6': [], - \ 'python': ['flake8', 'mypy', 'pylint'], + \ 'python': ['flake8', 'mypy', 'pylint', 'pyright'], \ 'rust': ['cargo'], \ 'spec': [], \ 'text': [], @@ -2557,6 +2579,7 @@ documented in additional help files. pylint................................|ale-python-pylint| pyls..................................|ale-python-pyls| pyre..................................|ale-python-pyre| + pyright...............................|ale-python-pyright| reorder-python-imports................|ale-python-reorder_python_imports| vulture...............................|ale-python-vulture| yapf..................................|ale-python-yapf| @@ -2654,12 +2677,14 @@ documented in additional help files. vala....................................|ale-vala-options| uncrustify............................|ale-vala-uncrustify| verilog/systemverilog...................|ale-verilog-options| + hdl-checker...........................|ale-verilog-hdl-checker| iverilog..............................|ale-verilog-iverilog| verilator.............................|ale-verilog-verilator| vlog..................................|ale-verilog-vlog| xvlog.................................|ale-verilog-xvlog| vhdl....................................|ale-vhdl-options| ghdl..................................|ale-vhdl-ghdl| + hdl-checker...........................|ale-vhdl-hdl-checker| vcom..................................|ale-vhdl-vcom| xvhdl.................................|ale-vhdl-xvhdl| vim.....................................|ale-vim-options| @@ -2864,7 +2889,7 @@ ALELast *ALELast* the last or first warning or error in the file, respectively. `ALEPrevious` and `ALENext` take optional flags arguments to custom their - behaviour : + behavior : `-wrap` enable wrapping around the file `-error`, `-warning` and `-info` enable jumping to errors, warnings or infos respectively, ignoring anything else. They are mutually exclusive and if @@ -3506,7 +3531,7 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* contents of the buffer being checked. All occurrences of `%t` in command strings will reference the one temporary file. The temporary file will be created inside a temporary directory, and the entire temporary directory - will be automatically deleted, following the behaviour of + will be automatically deleted, following the behavior of |ale#command#ManageDirectory|. This option can be used for some linters which do not support reading from stdin. @@ -3531,7 +3556,6 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* be used to replace those characters to avoid formatting issues. *ale-linter-loading-behavior* - *ale-linter-loading-behaviour* Linters for ALE will be loaded by searching |runtimepath| in the following format: > diff --git a/plugin/ale.vim b/plugin/ale.vim index e1ddf7b7..65c5a77c 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -121,6 +121,9 @@ let g:ale_cursor_detail = get(g:, 'ale_cursor_detail', 0) " This flag can be set to 1 to enable virtual text when the cursor moves. let g:ale_virtualtext_cursor = get(g:, 'ale_virtualtext_cursor', 0) +" This flag can be set to 1 to enable LSP hover messages at the cursor. +let g:ale_hover_cursor = get(g:, 'ale_hover_cursor', 1) + " This flag can be set to 1 to automatically close the preview window upon " entering Insert Mode. let g:ale_close_preview_on_insert = get(g:, 'ale_close_preview_on_insert', 0) diff --git a/supported-tools.md b/supported-tools.md index 595391cc..7b884ff5 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -382,6 +382,7 @@ formatting. * [pylint](https://www.pylint.org/) :floppy_disk: * [pyls](https://github.com/palantir/python-language-server) :warning: * [pyre](https://github.com/facebook/pyre-check) :warning: + * [pyright](https://github.com/microsoft/pyright) * [reorder-python-imports](https://github.com/asottile/reorder_python_imports) * [vulture](https://github.com/jendrikseipp/vulture) :warning: :floppy_disk: * [yapf](https://github.com/google/yapf) @@ -493,6 +494,7 @@ formatting. * VALA * [uncrustify](https://github.com/uncrustify/uncrustify) * Verilog + * [hdl-checker](https://pypi.org/project/hdl-checker) * [iverilog](https://github.com/steveicarus/iverilog) * [verilator](http://www.veripool.org/projects/verilator/wiki/Intro) * [vlog](https://www.mentor.com/products/fv/questa/) diff --git a/test/command_callback/ccls_paths/with_build_dir/unusual_build_dir_name/compile_commands.json b/test/command_callback/ccls_paths/with_build_dir/unusual_build_dir_name/compile_commands.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/ccls_paths/with_build_dir/unusual_build_dir_name/compile_commands.json diff --git a/test/command_callback/hdl_server/foo.vhd b/test/command_callback/hdl_server/foo.vhd new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/hdl_server/foo.vhd diff --git a/test/command_callback/hdl_server/with_config_file/.hdl_checker.config b/test/command_callback/hdl_server/with_config_file/.hdl_checker.config new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/hdl_server/with_config_file/.hdl_checker.config diff --git a/test/command_callback/hdl_server/with_config_file/_hdl_checker.config b/test/command_callback/hdl_server/with_config_file/_hdl_checker.config new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/hdl_server/with_config_file/_hdl_checker.config diff --git a/test/command_callback/hdl_server/with_config_file/foo.vhd b/test/command_callback/hdl_server/with_config_file/foo.vhd new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/hdl_server/with_config_file/foo.vhd diff --git a/test/command_callback/hdl_server/with_git/files/foo.vhd b/test/command_callback/hdl_server/with_git/files/foo.vhd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/command_callback/hdl_server/with_git/files/foo.vhd @@ -0,0 +1 @@ + diff --git a/test/command_callback/test_c_ccls_command_callbacks.vader b/test/command_callback/test_c_ccls_command_callbacks.vader index 43fdb366..04643d02 100644 --- a/test/command_callback/test_c_ccls_command_callbacks.vader +++ b/test/command_callback/test_c_ccls_command_callbacks.vader @@ -4,6 +4,10 @@ Before: call ale#assert#SetUpLinterTest('c', 'ccls') + Save b:ale_c_build_dir_names + Save b:ale_c_ccls_executable + Save b:ale_c_ccls_init_options + After: call ale#assert#TearDownLinterTest() @@ -47,3 +51,19 @@ Execute(The initialization options should be configurable): let b:ale_c_ccls_init_options = { 'cacheDirectory': '/tmp/ccls' } AssertLSPOptions { 'cacheDirectory': '/tmp/ccls' } + +Execute(The compile command database should be detected correctly): + call ale#test#SetFilename('ccls_paths/with_ccls/dummy.c') + + AssertLSPOptions {} + + call ale#test#SetFilename('ccls_paths/with_compile_commands_json/dummy.c') + + AssertLSPOptions { 'compilationDatabaseDirectory': + \ ale#path#Simplify(g:dir . '/ccls_paths/with_compile_commands_json') } + + call ale#test#SetFilename('ccls_paths/with_build_dir/dummy.c') + let b:ale_c_build_dir_names = ['unusual_build_dir_name'] + + AssertLSPOptions { 'compilationDatabaseDirectory': + \ ale#path#Simplify(g:dir . '/ccls_paths/with_build_dir/unusual_build_dir_name') } diff --git a/test/command_callback/test_c_cppcheck_command_callbacks.vader b/test/command_callback/test_c_cppcheck_command_callbacks.vader index 3d487a31..b6ea3179 100644 --- a/test/command_callback/test_c_cppcheck_command_callbacks.vader +++ b/test/command_callback/test_c_cppcheck_command_callbacks.vader @@ -1,7 +1,6 @@ Before: call ale#assert#SetUpLinterTest('c', 'cppcheck') - - let b:command_tail = ' -q --language=c --enable=style -I' . ale#Escape(ale#path#Simplify(g:dir)) .' %t' + let b:command_tail = ' -q --language=c --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}'' --enable=style -I' . ale#Escape(ale#path#Simplify(g:dir)) .' %t' After: " Remove a test file we might open for some tests. @@ -10,9 +9,8 @@ After: set buftype=nofile endif - call ale#assert#TearDownLinterTest() - unlet! b:command_tail + call ale#assert#TearDownLinterTest() Execute(The executable should be configurable): AssertLinter 'cppcheck', ale#Escape('cppcheck') . b:command_tail @@ -28,6 +26,7 @@ Execute(cppcheck for C should detect compile_commands.json files): \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ale#Escape('cppcheck') \ . ' -q --language=c' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --project=' . ale#Escape('compile_commands.json') \ . ' --enable=style %t' @@ -38,6 +37,7 @@ Execute(cppcheck for C should detect compile_commands.json files in build direct \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/with_build_dir')) \ . ale#Escape('cppcheck') \ . ' -q --language=c' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --project=' . ale#Escape(ale#path#Simplify('build/compile_commands.json')) \ . ' --enable=style %t' @@ -47,6 +47,7 @@ Execute(cppcheck for C should include file dir if compile_commands.json file is AssertLinter 'cppcheck', \ ale#Escape('cppcheck') \ . ' -q --language=c' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --enable=style' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/cppcheck_paths')) \ . ' %t' @@ -61,6 +62,7 @@ Execute(cppcheck for C should ignore compile_commands.json file if buffer is mod \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ale#Escape('cppcheck') \ . ' -q --language=c' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --enable=style' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ' %t' diff --git a/test/command_callback/test_cpp_ccls_command_callbacks.vader b/test/command_callback/test_cpp_ccls_command_callbacks.vader index eece42bc..f603ac07 100644 --- a/test/command_callback/test_cpp_ccls_command_callbacks.vader +++ b/test/command_callback/test_cpp_ccls_command_callbacks.vader @@ -4,6 +4,10 @@ Before: call ale#assert#SetUpLinterTest('cpp', 'ccls') + Save b:ale_c_build_dir_names + Save b:ale_cpp_ccls_executable + Save b:ale_cpp_ccls_init_options + After: call ale#assert#TearDownLinterTest() @@ -47,3 +51,19 @@ Execute(The initialization options should be configurable): let b:ale_cpp_ccls_init_options = { 'cacheDirectory': '/tmp/ccls' } AssertLSPOptions { 'cacheDirectory': '/tmp/ccls' } + +Execute(The compile command database should be detected correctly): + call ale#test#SetFilename('ccls_paths/with_ccls/dummy.c') + + AssertLSPOptions {} + + call ale#test#SetFilename('ccls_paths/with_compile_commands_json/dummy.c') + + AssertLSPOptions { 'compilationDatabaseDirectory': + \ ale#path#Simplify(g:dir . '/ccls_paths/with_compile_commands_json') } + + call ale#test#SetFilename('ccls_paths/with_build_dir/dummy.c') + let b:ale_c_build_dir_names = ['unusual_build_dir_name'] + + AssertLSPOptions { 'compilationDatabaseDirectory': + \ ale#path#Simplify(g:dir . '/ccls_paths/with_build_dir/unusual_build_dir_name') } diff --git a/test/command_callback/test_cpp_cppcheck_command_callbacks.vader b/test/command_callback/test_cpp_cppcheck_command_callbacks.vader index 02bdf748..b19c09b1 100644 --- a/test/command_callback/test_cpp_cppcheck_command_callbacks.vader +++ b/test/command_callback/test_cpp_cppcheck_command_callbacks.vader @@ -1,6 +1,6 @@ Before: call ale#assert#SetUpLinterTest('cpp', 'cppcheck') - let b:command_tail = ' -q --language=c++ --enable=style -I' . ale#Escape(ale#path#Simplify(g:dir)) .' %t' + let b:command_tail = ' -q --language=c++ --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}'' --enable=style -I' . ale#Escape(ale#path#Simplify(g:dir)) .' %t' After: " Remove a test file we might open for some tests. @@ -26,6 +26,7 @@ Execute(cppcheck for C++ should detect compile_commands.json files): \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ale#Escape('cppcheck') \ . ' -q --language=c++' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --project=' . ale#Escape('compile_commands.json') \ . ' --enable=style %t' @@ -36,6 +37,7 @@ Execute(cppcheck for C++ should detect compile_commands.json files in build dire \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/with_build_dir')) \ . ale#Escape('cppcheck') \ . ' -q --language=c++' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --project=' . ale#Escape(ale#path#Simplify('build/compile_commands.json')) \ . ' --enable=style %t' @@ -45,6 +47,7 @@ Execute(cppcheck for C++ should include file dir if compile_commands.json file i AssertLinter 'cppcheck', \ ale#Escape('cppcheck') \ . ' -q --language=c++' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --enable=style' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/cppcheck_paths')) \ . ' %t' @@ -59,6 +62,7 @@ Execute(cppcheck for C++ should ignore compile_commands.json file if buffer is m \ ale#path#CdString(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ale#Escape('cppcheck') \ . ' -q --language=c++' + \ . ' --template=''{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}''' \ . ' --enable=style' \ . ' -I' . ale#Escape(ale#path#Simplify(g:dir . '/cppcheck_paths/one')) \ . ' %t' diff --git a/test/command_callback/test_gopls_command_callback.vader b/test/command_callback/test_gopls_command_callback.vader index 92b20b18..73fcbf03 100644 --- a/test/command_callback/test_gopls_command_callback.vader +++ b/test/command_callback/test_gopls_command_callback.vader @@ -49,7 +49,10 @@ Execute(Should return directory for 'go.mod' if found in parent directory): Execute(Should return nearest directory with '.git' if found in parent directory): call ale#test#SetFilename('test.go') - call mkdir(g:dir . '/.git') + + if !isdirectory(g:dir . '/.git') + call mkdir(g:dir . '/.git') + endif AssertLSPProject g:dir diff --git a/test/command_callback/test_objc_ccls_command_callbacks.vader b/test/command_callback/test_objc_ccls_command_callbacks.vader index 5aa69d6a..34b8539e 100644 --- a/test/command_callback/test_objc_ccls_command_callbacks.vader +++ b/test/command_callback/test_objc_ccls_command_callbacks.vader @@ -1,6 +1,10 @@ Before: call ale#assert#SetUpLinterTest('objc', 'ccls') + Save b:ale_c_build_dir_names + Save b:ale_objc_ccls_executable + Save b:ale_objc_ccls_init_options + After: call ale#assert#TearDownLinterTest() @@ -44,3 +48,19 @@ Execute(The initialization options should be configurable): let b:ale_objc_ccls_init_options = { 'cacheDirectory': '/tmp/ccls' } AssertLSPOptions { 'cacheDirectory': '/tmp/ccls' } + +Execute(The compile command database should be detected correctly): + call ale#test#SetFilename('ccls_paths/with_ccls/dummy.c') + + AssertLSPOptions {} + + call ale#test#SetFilename('ccls_paths/with_compile_commands_json/dummy.c') + + AssertLSPOptions { 'compilationDatabaseDirectory': + \ ale#path#Simplify(g:dir . '/ccls_paths/with_compile_commands_json') } + + call ale#test#SetFilename('ccls_paths/with_build_dir/dummy.c') + let b:ale_c_build_dir_names = ['unusual_build_dir_name'] + + AssertLSPOptions { 'compilationDatabaseDirectory': + \ ale#path#Simplify(g:dir . '/ccls_paths/with_build_dir/unusual_build_dir_name') } diff --git a/test/command_callback/test_pyright_command_callback.vader b/test/command_callback/test_pyright_command_callback.vader new file mode 100644 index 00000000..3e421bd9 --- /dev/null +++ b/test/command_callback/test_pyright_command_callback.vader @@ -0,0 +1,116 @@ +Before: + call ale#assert#SetUpLinterTest('python', 'pyright') + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + +After: + unlet! b:bin_dir + unlet! b:executable + + call ale#assert#TearDownLinterTest() + +Execute(The command callback should return the correct default string): + AssertLinter + \ 'pyright-langserver', + \ ale#Escape('pyright-langserver') . ' --stdio' + +Execute(The executable should be configurable): + let g:ale_python_pyright_executable = '/bin/foo-bar' + + AssertLinter + \ '/bin/foo-bar', + \ ale#Escape('/bin/foo-bar') . ' --stdio' + +Execute(The default configuration should be mostly empty): + " The default configuration needs to have at least one key in it, + " or the server won't start up properly. + AssertLSPConfig {'python': {}} + + let b:ale_python_pyright_config = {} + + AssertLSPConfig {'python': {}} + +Execute(virtualenv paths should be set in configuration by default): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + AssertLSPConfig { + \ 'python': { + \ 'pythonPath': ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/python'), + \ 'venvPath': ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env'), + \ }, + \} + +Execute(The pythonPath should be set based on whatever the ovveride for the venvPath is set to): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + " This overrides the default detection of the path. + let b:ale_python_pyright_config = { + \ 'python': { + \ 'venvPath': '/foo/bar', + \ }, + \} + + AssertLSPConfig { + \ 'python': { + \ 'pythonPath': ale#path#Simplify('/foo/bar/' . b:bin_dir . '/python'), + \ 'venvPath': '/foo/bar', + \ }, + \} + +Execute(You should be able to override pythonPath when venvPath is detected): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + " This overrides the default detection of the path. + let b:ale_python_pyright_config = { + \ 'python': { + \ 'pythonPath': '/bin/python', + \ }, + \} + + AssertLSPConfig { + \ 'python': { + \ 'pythonPath': '/bin/python', + \ 'venvPath': ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env'), + \ }, + \} + +Execute(You should be able to override both pythonPath and venvPath): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + " This overrides the default detection of the path. + let b:ale_python_pyright_config = { + \ 'python': { + \ 'pythonPath': '/bin/python', + \ 'venvPath': '/other/dir', + \ }, + \} + + AssertLSPConfig { + \ 'python': { + \ 'pythonPath': '/bin/python', + \ 'venvPath': '/other/dir', + \ }, + \} + +Execute(You should be able to define other settings): + call ale#test#SetFilename('python_paths/with_virtualenv/subdir/foo/bar.py') + + let b:ale_python_pyright_config = { + \ 'python': { + \ 'analysis': {'logLevel': 'warning'}, + \ }, + \ 'pyright': { + \ 'disableLanguageServices': v:true, + \ }, + \} + + AssertLSPConfig { + \ 'python': { + \ 'analysis': {'logLevel': 'warning'}, + \ 'pythonPath': ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/python'), + \ 'venvPath': ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env'), + \ }, + \ 'pyright': { + \ 'disableLanguageServices': v:true, + \ }, + \} diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader index 1fdbbd96..e233c955 100644 --- a/test/completion/test_lsp_completion_parsing.vader +++ b/test/completion/test_lsp_completion_parsing.vader @@ -526,3 +526,73 @@ Execute(Should handle completion messages with the deprecated insertText attribu \ ], \ }, \ }) + +Execute(Should handle completion messages with additionalTextEdits): + AssertEqual + \ [ + \ { + \ 'word': 'next_callback', + \ 'menu': 'PlayTimeCallback', + \ 'info': '', + \ 'kind': 'v', + \ 'icase': 1, + \ 'user_data': json_encode({ + \ 'codeActions': [ + \ { + \ 'description': 'completion', + \ 'changes': [ + \ { + \ 'fileName': expand('#' . bufnr('') . ':p'), + \ 'textChanges': [ + \ { + \ 'start': { + \ 'line': 11, + \ 'offset': 2, + \ }, + \ 'end': { + \ 'line': 13, + \ 'offset': 4, + \ }, + \ 'newText': 'from "module" import next_callback', + \ }, + \ ], + \ }, + \ ], + \ }, + \ ], + \ }), + \ }, + \ ], + \ ale#completion#ParseLSPCompletions({ + \ 'id': 226, + \ 'jsonrpc': '2.0', + \ 'result': { + \ 'isIncomplete': v:false, + \ 'items': [ + \ { + \ 'detail': 'PlayTimeCallback', + \ 'filterText': 'next_callback', + \ 'insertText': 'next_callback', + \ 'insertTextFormat': 1, + \ 'kind': 6, + \ 'label': ' next_callback', + \ 'sortText': '3ee19999next_callback', + \ 'additionalTextEdits': [ + \ { + \ 'range': { + \ 'start': { + \ 'line': 10, + \ 'character': 1, + \ }, + \ 'end': { + \ 'line': 12, + \ 'character': 3, + \ }, + \ }, + \ 'newText': 'from "module" import next_callback', + \ }, + \ ], + \ }, + \ ], + \ }, + \ }) diff --git a/test/completion/test_public_completion_api.vader b/test/completion/test_public_completion_api.vader index c3cd42b8..f26fdc12 100644 --- a/test/completion/test_public_completion_api.vader +++ b/test/completion/test_public_completion_api.vader @@ -37,6 +37,7 @@ Execute(ale#completion#GetCompletionPositionForDeoplete() should return the posi AssertEqual 4, ale#completion#GetCompletionPositionForDeoplete('foo bar') Execute(ale#completion#CanProvideCompletions should return 0 when no completion sources are available): + let b:ale_linters = ['flake8'] AssertEqual 0, ale#completion#CanProvideCompletions() Execute(ale#completion#CanProvideCompletions should return 1 when at least one completion source is available): diff --git a/test/fixers/test_astyle_fixer_callback.vader b/test/fixers/test_astyle_fixer_callback.vader index cbec4493..ac756870 100644 --- a/test/fixers/test_astyle_fixer_callback.vader +++ b/test/fixers/test_astyle_fixer_callback.vader @@ -20,11 +20,12 @@ Execute(The astyle callback should return the correct default values): " Because this file doesn't exist, no astylrc config " exists near it. Therefore, project_options is empty. call ale#test#SetFilename('../c_files/testfile.c') + let targetfile = bufname(bufnr('%')) AssertEqual \ { \ 'command': ale#Escape(g:ale_c_astyle_executable) - \ . ' --stdin=' + \ . ' --stdin=' . ale#Escape(targetfile) \ }, \ ale#fixers#astyle#Fix(bufnr('')) @@ -33,46 +34,63 @@ Execute(The astyle callback should support cpp files): " exists near it. Therefore, project_options is empty. call ale#test#SetFilename('../cpp_files/dummy.cpp') set filetype=cpp " The test fails without this + let targetfile = bufname(bufnr('%')) AssertEqual \ { \ 'command': ale#Escape(g:ale_cpp_astyle_executable) - \ . ' --stdin=' + \ . ' --stdin=' . ale#Escape(targetfile) \ }, \ ale#fixers#astyle#Fix(bufnr('')) Execute(The astyle callback should support cpp files with option file set): call ale#test#SetFilename('../cpp_files/dummy.cpp') let g:ale_cpp_astyle_project_options = '.astylerc_cpp' + let targetfile = bufname(bufnr('%')) set filetype=cpp " The test fails without this AssertEqual \ { \ 'command': ale#Escape('invalidpp') \ . ' --project=' . g:ale_cpp_astyle_project_options - \ . ' --stdin=' + \ . ' --stdin=' . ale#Escape(targetfile) \ }, \ ale#fixers#astyle#Fix(bufnr('')) -Execute(The astyle callback should return the correct default values with an option file set): +Execute(The astyle callback should return the correct default values with a specified option file): call ale#test#SetFilename('../c_files/testfile.c') let g:ale_c_astyle_project_options = '.astylerc_c' + let targetfile = bufname(bufnr('%')) AssertEqual \ { \ 'command': ale#Escape('xxxinvalid') \ . ' --project=' . g:ale_c_astyle_project_options - \ . ' --stdin=' + \ . ' --stdin=' . ale#Escape(targetfile) \ }, \ ale#fixers#astyle#Fix(bufnr('')) Execute(The astyle callback should find nearest default option file _astylrc): call ale#test#SetFilename('../test_c_projects/makefile_project/subdir/file.c') + let targetfile = bufname(bufnr('%')) AssertEqual \ { \ 'command': ale#Escape('xxxinvalid') \ . ' --project=_astylerc' - \ . ' --stdin=' + \ . ' --stdin=' . ale#Escape(targetfile) + \ }, + \ ale#fixers#astyle#Fix(bufnr('')) + +Execute(The astyle callback should find .astylrc in the same directory as src): + call ale#test#SetFilename('../test_cpp_project/dummy.cpp') + set filetype=cpp " The test fails without this + let targetfile = bufname(bufnr('%')) + + AssertEqual + \ { + \ 'command': ale#Escape('invalidpp') + \ . ' --project=.astylerc' + \ . ' --stdin=' . ale#Escape(targetfile) \ }, \ ale#fixers#astyle#Fix(bufnr('')) diff --git a/test/handler/test_cppcheck_handler.vader b/test/handler/test_cppcheck_handler.vader index f153b9b5..55a5d29b 100644 --- a/test/handler/test_cppcheck_handler.vader +++ b/test/handler/test_cppcheck_handler.vader @@ -10,19 +10,29 @@ Execute(Basic errors should be handled by cppcheck): AssertEqual \ [ \ { - \ 'lnum': 5, + \ 'lnum': 974, + \ 'col' : 6, \ 'type': 'E', - \ 'text': 'Array ''a[10]'' accessed at index 10, which is out of bounds', + \ 'sub_type': '', + \ 'text': 'Array ''n[3]'' accessed at index 3, which is out of bounds.', + \ 'code': 'arrayIndexOutOfBounds' \ }, \ { - \ 'lnum': 7, + \ 'lnum': 1185, + \ 'col' : 10, \ 'type': 'W', - \ 'text': 'Some other problem', + \ 'sub_type': 'style', + \ 'text': 'The scope of the variable ''indxStr'' can be reduced.', + \ 'code': 'variableScope' \ }, \ ], \ ale#handlers#cppcheck#HandleCppCheckFormat(bufnr(''), [ - \ '[test.cpp:5]: (error) Array ''a[10]'' accessed at index 10, which is out of bounds', - \ '[test.cpp:7]: (warning) Some other problem', + \ 'test.cpp:974:6: error: Array ''n[3]'' accessed at index 3, which is out of bounds. [arrayIndexOutOfBounds]\', + \ ' n[3]=3;', + \ ' ^', + \ 'test.cpp:1185:10: style: The scope of the variable ''indxStr'' can be reduced. [variableScope]\', + \ ' char indxStr[16];', + \ ' ^', \ ]) Execute(Problems from other files should be ignored by cppcheck): @@ -32,5 +42,7 @@ Execute(Problems from other files should be ignored by cppcheck): \ [ \ ], \ ale#handlers#cppcheck#HandleCppCheckFormat(bufnr(''), [ - \ '[bar.cpp:5]: (error) Array ''a[10]'' accessed at index 10, which is out of bounds', + \ 'bar.cpp:974:6: error: Array ''n[3]'' accessed at index 3, which is out of bounds. [arrayIndexOutOfBounds]\', + \ ' n[3]=3;', + \ ' ^', \ ]) diff --git a/test/handler/test_go_generic_handler.vader b/test/handler/test_go_generic_handler.vader index 624e56c1..2b17fdcb 100644 --- a/test/handler/test_go_generic_handler.vader +++ b/test/handler/test_go_generic_handler.vader @@ -15,8 +15,24 @@ Execute(The golang handler should return the correct filenames): \ 'type': 'E', \ 'filename': ale#path#Simplify(expand('%:p:h') . '/other.go'), \ }, + \ { + \ 'lnum': 18, + \ 'col': 0, + \ 'text': 'random error', + \ 'type': 'E', + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/go1.14.go'), + \ }, + \ { + \ 'lnum': 36, + \ 'col': 2, + \ 'text': 'another random error', + \ 'type': 'E', + \ 'filename': ale#path#Simplify(expand('%:p:h') . '/anothergo1.14.go'), + \ }, \ ], \ ale#handlers#go#Handler(bufnr(''), [ \ 'test.go:27: some error', \ 'other.go:27:5: some error with a column', + \ 'vet: go1.14.go:18:0: random error', + \ 'vet: anothergo1.14.go:36:2: another random error', \ ]) diff --git a/test/handler/test_markdownlint_handler.vader b/test/handler/test_markdownlint_handler.vader index 8e9f77a0..f2e6e328 100644 --- a/test/handler/test_markdownlint_handler.vader +++ b/test/handler/test_markdownlint_handler.vader @@ -75,3 +75,17 @@ Execute(The Markdownlint handler should parse post v0.22.0 output with column co \ ale#handlers#markdownlint#Handle(0, [ \ 'README.md:10:20 MD013/line-length Line length [Expected: 80; Actual: 114]' \ ]) + +Execute(The Markdownlint handler should parse output with multiple slashes in rule name correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 10, + \ 'code': 'MD022/blanks-around-headings/blanks-around-headers', + \ 'text': 'Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Above] [Context: "### something"]', + \ 'type': 'W' + \ } + \ ], + \ ale#handlers#markdownlint#Handle(0, [ + \ 'README.md:10 MD022/blanks-around-headings/blanks-around-headers Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Above] [Context: "### something"]' + \ ]) diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader index 6473e283..b6ef852a 100644 --- a/test/lsp/test_other_initialize_message_handling.vader +++ b/test/lsp/test_other_initialize_message_handling.vader @@ -131,7 +131,7 @@ Execute(Disabled capabilities should be recognised correctly): \ }, \ 'definitionProvider': v:false, \ 'experimental': {}, - \ 'documentHighlightProvider': v:true + \ 'documentHighlightProvider': v:true, \ }, \ }, \}) @@ -150,6 +150,57 @@ Execute(Disabled capabilities should be recognised correctly): \ b:conn.capabilities AssertEqual [[1, 'initialized', {}]], g:message_list +Execute(Capabilities should be enabled when send as Dictionaries): + call ale#lsp#HandleInitResponse(b:conn, { + \ 'jsonrpc': '2.0', + \ 'id': 1, + \ 'result': { + \ 'capabilities': { + \ 'renameProvider': {}, + \ 'executeCommandProvider': { + \ 'commands': [], + \ }, + \ 'hoverProvider': {}, + \ 'documentSymbolProvider': v:true, + \ 'documentRangeFormattingProvider': v:true, + \ 'codeLensProvider': { + \ 'resolveProvider': v:false + \ }, + \ 'completionProvider': { + \ 'triggerCharacters': ['.'], + \ 'resolveProvider': v:false + \ }, + \ 'referencesProvider': {}, + \ 'textDocumentSync': 2, + \ 'documentFormattingProvider': v:true, + \ 'codeActionProvider': v:true, + \ 'signatureHelpProvider': { + \ 'triggerCharacters': ['(', ','], + \ }, + \ 'definitionProvider': {}, + \ 'typeDefinitionProvider': {}, + \ 'experimental': {}, + \ 'documentHighlightProvider': v:true, + \ 'workspaceSymbolProvider': {} + \ }, + \ }, + \}) + + AssertEqual 1, b:conn.initialized + AssertEqual + \ { + \ 'completion_trigger_characters': ['.'], + \ 'completion': 1, + \ 'references': 1, + \ 'hover': 1, + \ 'definition': 1, + \ 'typeDefinition': 1, + \ 'symbol_search': 1, + \ 'rename': 1, + \ }, + \ b:conn.capabilities + AssertEqual [[1, 'initialized', {}]], g:message_list + Execute(Results that are not dictionaries should be handled correctly): call ale#lsp#HandleInitResponse(b:conn, { \ 'jsonrpc': '2.0', diff --git a/test/test_autocmd_commands.vader b/test/test_autocmd_commands.vader index 355b4c77..a69333d4 100644 --- a/test/test_autocmd_commands.vader +++ b/test/test_autocmd_commands.vader @@ -49,6 +49,7 @@ Before: Save g:ale_lint_on_save Save g:ale_lint_on_text_changed Save g:ale_pattern_options_enabled + Save g:ale_hover_cursor " Turn everything on by defaul for these tests. let g:ale_completion_enabled = 1 @@ -61,6 +62,7 @@ Before: let g:ale_lint_on_save = 1 let g:ale_lint_on_text_changed = 1 let g:ale_pattern_options_enabled = 1 + let g:ale_hover_cursor = 1 After: delfunction CheckAutocmd @@ -84,6 +86,7 @@ Execute (All events should be set up when everything is on): \ 'BufWinEnter * call ale#events#LintOnEnter(str2nr(expand(''<abuf>'')))', \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand(''<abuf>'')))', \ 'CursorHold * if exists(''*ale#engine#Cleanup'') | call ale#cursor#EchoCursorWarningWithDelay() | endif', + \ 'CursorHold if exists(''*ale#lsp#Send'') | call ale#hover#ShowTruncatedMessageAtCursor() | endif', \ 'CursorMoved * if exists(''*ale#engine#Cleanup'') | call ale#cursor#EchoCursorWarningWithDelay() | endif', \ 'FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand(''<abuf>'')))', \ 'FileType * call ale#events#FileTypeEvent( str2nr(expand(''<abuf>'')), expand(''<amatch>''))', @@ -95,9 +98,30 @@ Execute (All events should be set up when everything is on): \ CheckAutocmd('ALEEvents') Execute (Only the required events should be bound even if various settings are off): + let g:ale_enabled = 1 + let g:ale_completion_enabled = 0 + let g:ale_echo_cursor = 0 + let g:ale_fix_on_save = 0 + let g:ale_lint_on_enter = 0 + let g:ale_lint_on_filetype_changed = 0 + let g:ale_lint_on_insert_leave = 0 + let g:ale_lint_on_save = 0 + let g:ale_lint_on_text_changed = 0 + let g:ale_pattern_options_enabled = 0 + let g:ale_hover_cursor = 0 + + AssertEqual + \ [ + \ 'BufEnter * call ale#events#ReadOrEnterEvent(str2nr(expand(''<abuf>'')))', + \ 'BufReadPost * call ale#events#ReadOrEnterEvent(str2nr(expand(''<abuf>'')))', + \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand(''<abuf>'')))', + \ ], + \ CheckAutocmd('ALEEvents') + +Execute (The cursor hoever event should be enabled with g:ale_hover_cursor = 1): + let g:ale_enabled = 1 let g:ale_completion_enabled = 0 let g:ale_echo_cursor = 0 - let g:ale_enabled = 0 let g:ale_fix_on_save = 0 let g:ale_lint_on_enter = 0 let g:ale_lint_on_filetype_changed = 0 @@ -105,12 +129,14 @@ Execute (Only the required events should be bound even if various settings are o let g:ale_lint_on_save = 0 let g:ale_lint_on_text_changed = 0 let g:ale_pattern_options_enabled = 0 + let g:ale_hover_cursor = 1 AssertEqual \ [ \ 'BufEnter * call ale#events#ReadOrEnterEvent(str2nr(expand(''<abuf>'')))', \ 'BufReadPost * call ale#events#ReadOrEnterEvent(str2nr(expand(''<abuf>'')))', \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand(''<abuf>'')))', + \ 'CursorHold * if exists(''*ale#lsp#Send'') | call ale#hover#ShowTruncatedMessageAtCursor() | endif', \ ], \ CheckAutocmd('ALEEvents') diff --git a/test/test_cpp_project/.astylerc b/test/test_cpp_project/.astylerc new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test_cpp_project/.astylerc diff --git a/test/test_cpp_project/dummy.cpp b/test/test_cpp_project/dummy.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test_cpp_project/dummy.cpp diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader index 9c40cb23..842cc394 100644 --- a/test/test_filetype_linter_defaults.vader +++ b/test/test_filetype_linter_defaults.vader @@ -32,7 +32,7 @@ Execute(The defaults for the help filetype should be correct): AssertEqual [], GetLinterNames('help') Execute(The defaults for the python filetype should be correct): - AssertEqual ['flake8', 'mypy', 'pylint'], GetLinterNames('python') + AssertEqual ['flake8', 'mypy', 'pylint', 'pyright'], GetLinterNames('python') let g:ale_linters_explicit = 1 @@ -61,7 +61,7 @@ Execute(The defaults for the zsh 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 " 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 diff --git a/test/test_hdl_checker_options.vader b/test/test_hdl_checker_options.vader new file mode 100644 index 00000000..4bee0f55 --- /dev/null +++ b/test/test_hdl_checker_options.vader @@ -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 diff --git a/test/test_hover.vader b/test/test_hover.vader index 917694a2..8b4cf7bd 100644 --- a/test/test_hover.vader +++ b/test/test_hover.vader @@ -133,6 +133,12 @@ Execute(LSP hover responses with markup content should be handled): AssertEqual ['markup'], g:echo_list AssertEqual {}, ale#hover#GetMap() +Execute(LSP hover responses with markup content missing values should be handled): + call HandleValidLSPResult({'contents': {'kind': 'something'}}) + + AssertEqual [], g:echo_list + AssertEqual {}, ale#hover#GetMap() + Execute(LSP hover response with lists of strings should be handled): call HandleValidLSPResult({'contents': [ \ "foo\n", @@ -145,6 +151,7 @@ Execute(LSP hover response with lists of strings should be handled): Execute(LSP hover response with lists of strings and marked strings should be handled): call HandleValidLSPResult({'contents': [ \ {'language': 'rust', 'value': 'foo'}, + \ {'language': 'foobar'}, \ "bar\n", \]}) diff --git a/test/test_rename.vader b/test/test_rename.vader index 3600df59..34d9e32e 100644 --- a/test/test_rename.vader +++ b/test/test_rename.vader @@ -327,6 +327,115 @@ Execute(Code actions from LSP should be handled): \ ], \ g:code_actions +Execute(DocumentChanges from LSP should be handled): + call ale#rename#HandleLSPResponse(1, { + \ 'id': 3, + \ 'result': { + \ 'documentChanges': [ + \ { + \ 'textDocument': { + \ 'version': 1.0, + \ 'uri': 'file:///foo/bar/file1.ts', + \ }, + \ 'edits': [ + \ { + \ 'range': { + \ 'start': { + \ 'line': 1, + \ 'character': 2, + \ }, + \ 'end': { + \ 'line': 3, + \ 'character': 4, + \ }, + \ }, + \ 'newText': 'bla123', + \ }, + \ ], + \ }, + \ ], + \ }, + \}) + + AssertEqual + \ [ + \ { + \ 'description': 'rename', + \ 'changes': [ + \ { + \ 'fileName': '/foo/bar/file1.ts', + \ 'textChanges': [ + \ { + \ 'start': { + \ 'line': 2, + \ 'offset': 3, + \ }, + \ 'end': { + \ 'line': 4, + \ 'offset': 5, + \ }, + \ 'newText': 'bla123', + \ }, + \ ], + \ }, + \ ], + \ } + \ ], + \ g:code_actions + +Execute(Single DocumentChange from LSP should be handled): + call ale#rename#HandleLSPResponse(1, { + \ 'id': 3, + \ 'result': { + \ 'documentChanges': { + \ 'textDocument': { + \ 'version': 1.0, + \ 'uri': 'file:///foo/bar/file1.ts', + \ }, + \ 'edits': [ + \ { + \ 'range': { + \ 'start': { + \ 'line': 1, + \ 'character': 2, + \ }, + \ 'end': { + \ 'line': 3, + \ 'character': 4, + \ }, + \ }, + \ 'newText': 'bla123', + \ }, + \ ], + \ }, + \ }, + \}) + + AssertEqual + \ [ + \ { + \ 'description': 'rename', + \ 'changes': [ + \ { + \ 'fileName': '/foo/bar/file1.ts', + \ 'textChanges': [ + \ { + \ 'start': { + \ 'line': 2, + \ 'offset': 3, + \ }, + \ 'end': { + \ 'line': 4, + \ 'offset': 5, + \ }, + \ 'newText': 'bla123', + \ }, + \ ], + \ }, + \ ], + \ } + \ ], + \ g:code_actions Execute(LSP should perform no action when no result): call ale#rename#HandleLSPResponse(1, { \ 'id': 3, |