diff options
38 files changed, 1046 insertions, 198 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 54113953..aca83191 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -5,6 +5,11 @@ clone_depth: 10 # Use the directory C:\testplugin so test directories will mostly work. clone_folder: C:\testplugin +branches: + only: + - master + - /v\d+\.\d+\.(x|\d+)/ + # Cache the vim and vader directories between builds. cache: - C:\vim -> .appveyor.yml diff --git a/.travis.yml b/.travis.yml index d48c1e08..00baff1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,10 @@ sudo: required services: - docker language: generic +branches: + only: + - master + - /^v\d+\.\d+\.(x|\d+)$/ env: - OPTIONS=--vim-80-only - OPTIONS=--vim-81-only diff --git a/ale_linters/python/mypy.vim b/ale_linters/python/mypy.vim index c4c6507f..dc4044e6 100644 --- a/ale_linters/python/mypy.vim +++ b/ale_linters/python/mypy.vim @@ -78,4 +78,5 @@ call ale#linter#Define('python', { \ 'executable': function('ale_linters#python#mypy#GetExecutable'), \ 'command': function('ale_linters#python#mypy#GetCommand'), \ 'callback': 'ale_linters#python#mypy#Handle', +\ 'output_stream': 'both' \}) diff --git a/ale_linters/sh/shell.vim b/ale_linters/sh/shell.vim index 189dc21d..171fe64e 100644 --- a/ale_linters/sh/shell.vim +++ b/ale_linters/sh/shell.vim @@ -34,8 +34,10 @@ function! ale_linters#sh#shell#Handle(buffer, lines) abort " Matches patterns line the following: " " bash: line 13: syntax error near unexpected token `d' + " bash:行0: 未预期的符号“done”附近有语法错误 + " bash: 列 90: 尋找匹配的「"」時遇到了未預期的檔案結束符 " sh: 11: Syntax error: "(" unexpected - let l:pattern = '\v(line |: ?)(\d+): (.+)$' + let l:pattern = '\v([^:]+:\D*)(\d+): (.+)$' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 2d2083da..5540ec14 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -28,76 +28,107 @@ function! ale#c#GetBuildDirectory(buffer) abort return ale#path#Dirname(l:json_file) endfunction -function! ale#c#AreSpecialCharsBalanced(option) abort - " Escape \" - let l:option_escaped = substitute(a:option, '\\"', '', 'g') - - " Retain special chars only - let l:special_chars = substitute(l:option_escaped, '[^"''()`]', '', 'g') - let l:special_chars = split(l:special_chars, '\zs') - - " Check if they are balanced +function! ale#c#ShellSplit(line) abort let l:stack = [] + let l:args = [''] + let l:prev = '' - for l:char in l:special_chars - if l:char is# ')' - if len(l:stack) == 0 || get(l:stack, -1) isnot# '(' - return 0 + for l:char in split(a:line, '\zs') + if l:char is# '''' + if len(l:stack) > 0 && get(l:stack, -1) is# '''' + call remove(l:stack, -1) + elseif (len(l:stack) == 0 || get(l:stack, -1) isnot# '"') && l:prev isnot# '\' + call add(l:stack, l:char) endif - - call remove(l:stack, -1) - elseif l:char is# '(' - call add(l:stack, l:char) - else + elseif (l:char is# '"' || l:char is# '`') && l:prev isnot# '\' if len(l:stack) > 0 && get(l:stack, -1) is# l:char call remove(l:stack, -1) - else + elseif len(l:stack) == 0 || get(l:stack, -1) isnot# '''' call add(l:stack, l:char) endif + elseif (l:char is# '(' || l:char is# '[' || l:char is# '{') && l:prev isnot# '\' + if len(l:stack) == 0 || get(l:stack, -1) isnot# '''' + call add(l:stack, l:char) + endif + elseif (l:char is# ')' || l:char is# ']' || l:char is# '}') && l:prev isnot# '\' + if len(l:stack) > 0 && get(l:stack, -1) is# {')': '(', ']': '[', '}': '{'}[l:char] + call remove(l:stack, -1) + endif + elseif l:char is# ' ' && len(l:stack) == 0 + if len(get(l:args, -1)) > 0 + call add(l:args, '') + endif + + continue endif + + let l:args[-1] = get(l:args, -1) . l:char endfor - return len(l:stack) == 0 + return l:args endfunction function! ale#c#ParseCFlags(path_prefix, cflag_line) abort - let l:split_lines = split(a:cflag_line) + let l:cflags_list = [] + + let l:split_lines = ale#c#ShellSplit(a:cflag_line) let l:option_index = 0 while l:option_index < len(l:split_lines) - let l:next_option_index = l:option_index + 1 - - " Join space-separated option - while l:next_option_index < len(l:split_lines) - \&& stridx(l:split_lines[l:next_option_index], '-') != 0 - let l:next_option_index += 1 - endwhile - - let l:option = join(l:split_lines[l:option_index : l:next_option_index-1], ' ') - call remove(l:split_lines, l:option_index, l:next_option_index-1) - call insert(l:split_lines, l:option, l:option_index) - - " Ignore invalid or conflicting options - if stridx(l:option, '-') != 0 - \|| stridx(l:option, '-o') == 0 - \|| stridx(l:option, '-c') == 0 - call remove(l:split_lines, l:option_index) - let l:option_index = l:option_index - 1 - " Fix relative path - elseif stridx(l:option, '-I') == 0 - if !(stridx(l:option, ':') == 2+1 || stridx(l:option, '/') == 2+0) - let l:option = '-I' . a:path_prefix . s:sep . l:option[2:] - call remove(l:split_lines, l:option_index) - call insert(l:split_lines, l:option, l:option_index) + let l:option = l:split_lines[l:option_index] + let l:option_index = l:option_index + 1 + + " Include options, that may need relative path fix + if stridx(l:option, '-I') == 0 + \ || stridx(l:option, '-iquote') == 0 + \ || stridx(l:option, '-isystem') == 0 + \ || stridx(l:option, '-idirafter') == 0 + if stridx(l:option, '-I') == 0 && l:option isnot# '-I' + let l:arg = join(split(l:option, '\zs')[2:], '') + let l:option = '-I' + else + let l:arg = l:split_lines[l:option_index] + let l:option_index = l:option_index + 1 endif - endif - let l:option_index = l:option_index + 1 - endwhile + " Fix relative paths if needed + if stridx(l:arg, s:sep) != 0 && stridx(l:arg, '/') != 0 + let l:rel_path = substitute(l:arg, '"', '', 'g') + let l:rel_path = substitute(l:rel_path, '''', '', 'g') + let l:arg = ale#Escape(a:path_prefix . s:sep . l:rel_path) + endif + + call add(l:cflags_list, l:option) + call add(l:cflags_list, l:arg) + " Options with arg that can be grouped with the option or separate + elseif stridx(l:option, '-D') == 0 || stridx(l:option, '-B') == 0 + call add(l:cflags_list, l:option) - call uniq(l:split_lines) + if l:option is# '-D' || l:option is# '-B' + call add(l:cflags_list, l:split_lines[l:option_index]) + let l:option_index = l:option_index + 1 + endif + " Options that have an argument (always separate) + elseif l:option is# '-iprefix' || stridx(l:option, '-iwithprefix') == 0 + \ || l:option is# '-isysroot' || l:option is# '-imultilib' + call add(l:cflags_list, l:option) + call add(l:cflags_list, l:split_lines[l:option_index]) + let l:option_index = l:option_index + 1 + " Options without argument + elseif (stridx(l:option, '-W') == 0 && stridx(l:option, '-Wa,') != 0 && stridx(l:option, '-Wl,') != 0 && stridx(l:option, '-Wp,') != 0) + \ || l:option is# '-w' || stridx(l:option, '-pedantic') == 0 + \ || l:option is# '-ansi' || stridx(l:option, '-std=') == 0 + \ || (stridx(l:option, '-f') == 0 && stridx(l:option, '-fdump') != 0 && stridx(l:option, '-fdiagnostics') != 0 && stridx(l:option, '-fno-show-column') != 0) + \ || stridx(l:option, '-O') == 0 + \ || l:option is# '-C' || l:option is# '-CC' || l:option is# '-trigraphs' + \ || stridx(l:option, '-nostdinc') == 0 || stridx(l:option, '-iplugindir=') == 0 + \ || stridx(l:option, '--sysroot=') == 0 || l:option is# '--no-sysroot-suffix' + \ || stridx(l:option, '-m') == 0 + call add(l:cflags_list, l:option) + endif + endwhile - return join(l:split_lines, ' ') + return join(l:cflags_list, ' ') endfunction function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index c0e8abd9..ebf32909 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -267,6 +267,14 @@ function! ale#completion#Show(result) abort \ {-> ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")} \) endif + + if l:source is# 'ale-callback' + call b:CompleteCallback(b:ale_completion_result) + endif +endfunction + +function! ale#completion#GetAllTriggers() abort + return deepcopy(s:trigger_character_map) endfunction function! s:CompletionStillValid(request_id) abort @@ -280,6 +288,7 @@ function! s:CompletionStillValid(request_id) abort \ b:ale_completion_info.column == l:column \ || b:ale_completion_info.source is# 'deoplete' \ || b:ale_completion_info.source is# 'ale-omnifunc' + \ || b:ale_completion_info.source is# 'ale-callback' \) endfunction @@ -560,12 +569,25 @@ endfunction " This function can be used to manually trigger autocomplete, even when " g:ale_completion_enabled is set to false -function! ale#completion#GetCompletions(source) abort +function! ale#completion#GetCompletions(...) abort + let l:source = get(a:000, 0, '') + let l:options = get(a:000, 1, {}) + + if len(a:000) > 2 + throw 'Too many arguments!' + endif + + let l:CompleteCallback = get(l:options, 'callback', v:null) + + if l:CompleteCallback isnot v:null + let b:CompleteCallback = l:CompleteCallback + endif + let [l:line, l:column] = getpos('.')[1:2] let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column) - if a:source is# 'ale-automatic' && empty(l:prefix) + if l:source is# 'ale-automatic' && empty(l:prefix) return 0 endif @@ -578,7 +600,7 @@ function! ale#completion#GetCompletions(source) abort \ 'prefix': l:prefix, \ 'conn_id': 0, \ 'request_id': 0, - \ 'source': a:source, + \ 'source': l:source, \} unlet! b:ale_completion_result diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim index 379c0d73..4e134f8c 100644 --- a/autoload/ale/debugging.vim +++ b/autoload/ale/debugging.vim @@ -50,6 +50,7 @@ let s:global_variable_list = [ \ 'ale_sign_style_error', \ 'ale_sign_style_warning', \ 'ale_sign_warning', +\ 'ale_sign_highlight_linenrs', \ 'ale_statusline_format', \ 'ale_type_map', \ 'ale_use_global_executables', diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index c5ef1544..d21cb227 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -27,6 +27,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['python'], \ 'description': 'Fix PEP8 issues with black.', \ }, +\ 'dfmt': { +\ 'function': 'ale#fixers#dfmt#Fix', +\ 'suggested_filetypes': ['d'], +\ 'description': 'Fix D files with dfmt.', +\ }, \ 'fecs': { \ 'function': 'ale#fixers#fecs#Fix', \ 'suggested_filetypes': ['javascript', 'css', 'html'], @@ -215,6 +220,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['haskell'], \ 'description': 'Fix Haskell files with brittany.', \ }, +\ 'hindent': { +\ 'function': 'ale#fixers#hindent#Fix', +\ 'suggested_filetypes': ['haskell'], +\ 'description': 'Fix Haskell files with hindent.', +\ }, \ 'hlint': { \ 'function': 'ale#fixers#hlint#Fix', \ 'suggested_filetypes': ['haskell'], @@ -325,6 +335,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['python'], \ 'description': 'Sort Python imports with reorder-python-imports.', \ }, +\ 'gnatpp': { +\ 'function': 'ale#fixers#gnatpp#Fix', +\ 'suggested_filetypes': ['ada'], +\ 'description': 'Format Ada files with gnatpp.', +\ }, \} " Reset the function registry to the default entries. diff --git a/autoload/ale/fixers/black.vim b/autoload/ale/fixers/black.vim index 367b8d52..fba6c3b4 100644 --- a/autoload/ale/fixers/black.vim +++ b/autoload/ale/fixers/black.vim @@ -29,6 +29,10 @@ function! ale#fixers#black#Fix(buffer) abort let l:options = ale#Var(a:buffer, 'python_black_options') + if expand('#' . a:buffer . ':e') is? 'pyi' + let l:options .= '--pyi' + endif + return { \ 'command': l:cd_string . ale#Escape(l:executable) . l:exec_args \ . (!empty(l:options) ? ' ' . l:options : '') diff --git a/autoload/ale/fixers/dfmt.vim b/autoload/ale/fixers/dfmt.vim new file mode 100644 index 00000000..0072e045 --- /dev/null +++ b/autoload/ale/fixers/dfmt.vim @@ -0,0 +1,18 @@ +" Author: theoldmoon0602 +" Description: Integration of dfmt with ALE. + +call ale#Set('d_dfmt_executable', 'dfmt') +call ale#Set('d_dfmt_options', '') + +function! ale#fixers#dfmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'd_dfmt_executable') + let l:options = ale#Var(a:buffer, 'd_dfmt_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . ' -i' + \ . (empty(l:options) ? '' : ' ' . l:options) + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/gnatpp.vim b/autoload/ale/fixers/gnatpp.vim new file mode 100644 index 00000000..bf3d484e --- /dev/null +++ b/autoload/ale/fixers/gnatpp.vim @@ -0,0 +1,17 @@ +" Author: tim <tim@inept.tech> +" Description: Fix files with gnatpp. + +call ale#Set('ada_gnatpp_executable', 'gnatpp') +call ale#Set('ada_gnatpp_options', '') + +function! ale#fixers#gnatpp#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'ada_gnatpp_executable') + let l:options = ale#Var(a:buffer, 'ada_gnatpp_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/fixers/hindent.vim b/autoload/ale/fixers/hindent.vim new file mode 100644 index 00000000..b6009a2c --- /dev/null +++ b/autoload/ale/fixers/hindent.vim @@ -0,0 +1,20 @@ +" Author: AlexeiDrake <drake.alexei@gmail.com> +" Description: Integration of hindent formatting with ALE. +" +call ale#Set('haskell_hindent_executable', 'hindent') + +function! ale#fixers#hindent#GetExecutable(buffer) abort + let l:executable = ale#Var(a:buffer, 'haskell_hindent_executable') + + return ale#handlers#haskell_stack#EscapeExecutable(l:executable, 'hindent') +endfunction + +function! ale#fixers#hindent#Fix(buffer) abort + let l:executable = ale#fixers#hindent#GetExecutable(a:buffer) + + return { + \ 'command': l:executable + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/autoload/ale/handlers/languagetool.vim b/autoload/ale/handlers/languagetool.vim index 10e049df..73974ceb 100644 --- a/autoload/ale/handlers/languagetool.vim +++ b/autoload/ale/handlers/languagetool.vim @@ -2,6 +2,7 @@ " Description: languagetool for markdown files " call ale#Set('languagetool_executable', 'languagetool') +call ale#Set('languagetool_options', '--autoDetect') function! ale#handlers#languagetool#GetExecutable(buffer) abort return ale#Var(a:buffer, 'languagetool_executable') @@ -9,8 +10,10 @@ endfunction function! ale#handlers#languagetool#GetCommand(buffer) abort let l:executable = ale#handlers#languagetool#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'languagetool_options') - return ale#Escape(l:executable) . ' --autoDetect %s' + return ale#Escape(l:executable) + \ . (empty(l:options) ? '' : ' ' . l:options) . ' %s' endfunction function! ale#handlers#languagetool#HandleOutput(buffer, lines) abort diff --git a/autoload/ale/highlight.vim b/autoload/ale/highlight.vim index cb7911e1..82ad57e0 100644 --- a/autoload/ale/highlight.vim +++ b/autoload/ale/highlight.vim @@ -26,6 +26,25 @@ endif let s:MAX_POS_VALUES = 8 let s:MAX_COL_SIZE = 1073741824 " pow(2, 30) +let s:has_nvim_highlight = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace') + +if s:has_nvim_highlight + let s:ns_id = nvim_create_namespace('ale_highlight') +endif + +" Wrappers are necessary to test this functionality by faking the calls in tests. +function! ale#highlight#nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) abort + " Ignore all errors for adding highlights. + try + call nvim_buf_add_highlight(a:buffer, a:ns_id, a:hl_group, a:line, a:col_start, a:col_end) + catch + endtry +endfunction + +function! ale#highlight#nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) abort + call nvim_buf_clear_namespace(a:buffer, a:ns_id, a:line_start, a:line_end) +endfunction + function! ale#highlight#CreatePositions(line, col, end_line, end_col) abort if a:line >= a:end_line " For single lines, just return the one position. @@ -51,15 +70,53 @@ endfunction " except these which have matching loclist item entries. function! ale#highlight#RemoveHighlights() abort - for l:match in getmatches() - if l:match.group =~? '\v^ALE(Style)?(Error|Warning|Info)(Line)?$' - call matchdelete(l:match.id) - endif - endfor + if s:has_nvim_highlight + call ale#highlight#nvim_buf_clear_namespace(bufnr(''), s:ns_id, 0, -1) + else + for l:match in getmatches() + if l:match.group =~? '\v^ALE(Style)?(Error|Warning|Info)(Line)?$' + call matchdelete(l:match.id) + endif + endfor + endif +endfunction + +" Same semantics of matchaddpos but will use nvim_buf_add_highlight if +" available. This involves iterating over the position list, switching from +" 1-based indexing to 0-based indexing, and translating the multiple ways +" that position can be specified for matchaddpos into line + col_start + +" col_end. +function! s:matchaddpos(group, pos_list) abort + if s:has_nvim_highlight + for l:pos in a:pos_list + let l:line = type(l:pos) == v:t_number + \ ? l:pos - 1 + \ : l:pos[0] - 1 + + if type(l:pos) == v:t_number || len(l:pos) == 1 + let l:col_start = 0 + let l:col_end = s:MAX_COL_SIZE + else + let l:col_start = l:pos[1] - 1 + let l:col_end = l:col_start + get(l:pos, 2, 1) + endif + + call ale#highlight#nvim_buf_add_highlight( + \ bufnr(''), + \ s:ns_id, + \ a:group, + \ l:line, + \ l:col_start, + \ l:col_end, + \) + endfor + else + call matchaddpos(a:group, a:pos_list) + endif endfunction function! s:highlight_line(bufnr, lnum, group) abort - call matchaddpos(a:group, [a:lnum]) + call s:matchaddpos(a:group, [a:lnum]) endfunction function! s:highlight_range(bufnr, range, group) abort @@ -72,7 +129,7 @@ function! s:highlight_range(bufnr, range, group) abort \ a:range.end_lnum, \ a:range.end_col \ ), - \ 'matchaddpos(a:group, v:val)' + \ 's:matchaddpos(a:group, v:val)' \) endfunction diff --git a/autoload/ale/list.vim b/autoload/ale/list.vim index e9f3f4ec..4bfe2a7b 100644 --- a/autoload/ale/list.vim +++ b/autoload/ale/list.vim @@ -103,6 +103,9 @@ function! s:SetListsImpl(timer_id, buffer, loclist) abort endfor endif + " Save the current view before opening/closing any window + call setbufvar(a:buffer, 'ale_winview', winsaveview()) + " Open a window to show the problems if we need to. " " We'll check if the current buffer's List is not empty here, so the @@ -141,6 +144,8 @@ function! s:SetListsImpl(timer_id, buffer, loclist) abort normal! "\<c-g>" endif endif + + call s:RestoreViewIfNeeded(a:buffer) endif " If ALE isn't currently checking for more problems, close the window if @@ -151,6 +156,30 @@ function! s:SetListsImpl(timer_id, buffer, loclist) abort endif endfunction +" Try to restore the window view after closing any of the lists to avoid making +" the it moving around, especially useful when on insert mode +function! s:RestoreViewIfNeeded(buffer) abort + let l:saved_view = getbufvar(a:buffer, 'ale_winview', {}) + + " Saved view is empty, can't do anything + if empty(l:saved_view) + return + endif + + " Check wether the cursor has moved since linting was actually requested. If + " the user has indeed moved lines, do nothing + let l:current_view = winsaveview() + + if l:current_view['lnum'] != l:saved_view['lnum'] + return + endif + + " Anchor view by topline if the list is set to open horizontally + if ale#Var(a:buffer, 'list_vertical') == 0 + call winrestview({'topline': l:saved_view['topline']}) + endif +endfunction + function! ale#list#SetLists(buffer, loclist) abort if get(g:, 'ale_set_lists_synchronously') == 1 \|| getbufvar(a:buffer, 'ale_save_event_fired', 0) @@ -174,12 +203,15 @@ function! s:CloseWindowIfNeeded(buffer) abort return endif + let l:did_close_any_list = 0 + try " Only close windows if the quickfix list or loclist is completely empty, " including errors set through other means. if g:ale_set_quickfix if empty(getqflist()) cclose + let l:did_close_any_list = 1 endif else let l:win_ids = s:WinFindBuf(a:buffer) @@ -187,10 +219,15 @@ function! s:CloseWindowIfNeeded(buffer) abort for l:win_id in l:win_ids if g:ale_set_loclist && empty(getloclist(l:win_id)) lclose + let l:did_close_any_list = 1 endif endfor endif " Ignore 'Cannot close last window' errors. catch /E444/ endtry + + if l:did_close_any_list + call s:RestoreViewIfNeeded(a:buffer) + endif endfunction diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim index eb0dd1cd..829de118 100644 --- a/autoload/ale/sign.vim +++ b/autoload/ale/sign.vim @@ -20,6 +20,7 @@ let g:ale_sign_info = get(g:, 'ale_sign_info', g:ale_sign_warning) let g:ale_sign_offset = get(g:, 'ale_sign_offset', 1000000) " This flag can be set to 1 to keep sign gutter always open let g:ale_sign_column_always = get(g:, 'ale_sign_column_always', 0) +let g:ale_sign_highlight_linenrs = get(g:, 'ale_sign_highlight_linenrs', 0) if !hlexists('ALEErrorSign') highlight link ALEErrorSign error @@ -82,7 +83,7 @@ execute 'sign define ALEInfoSign text=' . s:EscapeSignText(g:ale_sign_info) \ . ' texthl=ALEInfoSign linehl=ALEInfoLine' sign define ALEDummySign -if has('nvim-0.3.2') +if g:ale_sign_highlight_linenrs && has('nvim-0.3.2') if !hlexists('ALEErrorSignLineNr') highlight link ALEErrorSignLineNr CursorLineNr endif diff --git a/autoload/asyncomplete/sources/ale.vim b/autoload/asyncomplete/sources/ale.vim new file mode 100644 index 00000000..ce793773 --- /dev/null +++ b/autoload/asyncomplete/sources/ale.vim @@ -0,0 +1,26 @@ +function! asyncomplete#sources#ale#get_source_options(...) abort + let l:default = extend({ + \ 'name': 'ale', + \ 'completor': function('asyncomplete#sources#ale#completor'), + \ 'whitelist': ['*'], + \ 'triggers': asyncomplete#sources#ale#get_triggers(), + \ }, a:0 >= 1 ? a:1 : {}) + + return extend(l:default, {'refresh_pattern': '\k\+$'}) +endfunction + +function! asyncomplete#sources#ale#get_triggers() abort + let l:triggers = ale#completion#GetAllTriggers() + let l:triggers['*'] = l:triggers['<default>'] + + return l:triggers +endfunction + +function! asyncomplete#sources#ale#completor(options, context) abort + let l:keyword = matchstr(a:context.typed, '\w\+$') + let l:startcol = a:context.col - len(l:keyword) + + call ale#completion#GetCompletions('ale-callback', { 'callback': {completions -> + \ asyncomplete#complete(a:options.name, a:context, l:startcol, completions) + \ }}) +endfunction diff --git a/doc/ale-ada.txt b/doc/ale-ada.txt index 93e3261a..2ac30c0a 100644 --- a/doc/ale-ada.txt +++ b/doc/ale-ada.txt @@ -22,4 +22,15 @@ g:ale_ada_gcc_options *g:ale_ada_gcc_options* =============================================================================== +gnatpp *ale-ada-gnatpp* + +g:ale_ada_gnatpp_options *g:ale_ada_gnatpp_options* + *b:ale_ada_gnatpp_options* + Type: |String| + Default: `''` + + This variable can be set to pass extra options to the gnatpp fixer. + + +=============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-d.txt b/doc/ale-d.txt index 55596062..72349a20 100644 --- a/doc/ale-d.txt +++ b/doc/ale-d.txt @@ -1,6 +1,15 @@ =============================================================================== ALE D Integration *ale-d-options* +=============================================================================== +dfmt *ale-d-dfmt* + +g:ale_d_dfmt_options *g:ale_d_dfmt_options* + *b:ale_d_dfmt_options* + Type: |String| + Default: `''` + +This variable can be set to pass additional options to the dfmt fixer. =============================================================================== dls *ale-d-dls* diff --git a/doc/ale-haskell.txt b/doc/ale-haskell.txt index e2073e37..5dd3ec15 100644 --- a/doc/ale-haskell.txt +++ b/doc/ale-haskell.txt @@ -12,6 +12,7 @@ g:ale_haskell_brittany_executable *g:ale_haskell_brittany_executable* This variable can be changed to use a different executable for brittany. + =============================================================================== floskell *ale-haskell-floskell* @@ -22,6 +23,7 @@ g:ale_haskell_floskell_executable *g:ale_haskell_floskell_executable* This variable can be changed to use a different executable for floskell. + =============================================================================== ghc *ale-haskell-ghc* @@ -32,6 +34,7 @@ g:ale_haskell_ghc_options *g:ale_haskell_ghc_options* This variable can be changed to modify flags given to ghc. + =============================================================================== ghc-mod *ale-haskell-ghc-mod* @@ -42,6 +45,7 @@ g:ale_haskell_ghc_mod_executable *g:ale_haskell_ghc_mod_executable* This variable can be changed to use a different executable for ghc-mod. + =============================================================================== cabal-ghc *ale-haskell-cabal-ghc* @@ -53,6 +57,7 @@ g:ale_haskell_cabal_ghc_options *g:ale_haskell_cabal_ghc_options* This variable can be changed to modify flags given to ghc through cabal exec. + =============================================================================== hdevtools *ale-haskell-hdevtools* @@ -87,6 +92,18 @@ g:ale_haskell_hfmt_executable *g:ale_haskell_hfmt_executable* This variable can be changed to use a different executable for hfmt. + +=============================================================================== +hindent *ale-haskell-hindent* + +g:ale_haskell_hindent_executable *g:ale_haskell_hindent_executable* + *b:ale_haskell_hindent_executable* + Type: |String| + Default: `'hindent'` + + This variable can be changed to use a different executable for hindent. + + =============================================================================== hlint *ale-haskell-hlint* @@ -106,6 +123,7 @@ g:ale_haskell_hlint_options g:ale_haskell_hlint_options This variable can be used to pass extra options to the underlying hlint executable. + =============================================================================== stack-build *ale-haskell-stack-build* @@ -117,6 +135,7 @@ g:ale_haskell_stack_build_options *g:ale_haskell_stack_build_options* We default to using `'--fast'`. Since Stack generates binaries, your programs will be slower unless you separately rebuild them outside of ALE. + =============================================================================== stack-ghc *ale-haskell-stack-ghc* @@ -128,6 +147,7 @@ g:ale_haskell_stack_ghc_options *g:ale_haskell_stack_ghc_options* This variable can be changed to modify flags given to ghc through `stack ghc` + =============================================================================== stylish-haskell *ale-haskell-stylish-haskell* @@ -139,6 +159,7 @@ g:ale_haskell_stylish_haskell_executable This variable can be changed to use a different executable for stylish-haskell. + =============================================================================== hie *ale-haskell-hie* @@ -150,5 +171,6 @@ g:ale_haskell_hie_executable *g:ale_haskell_hie_executable* This variable can be changed to use a different executable for the haskell ide engine. i.e. `'hie-wrapper'` + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index d39aaf7e..1b1202b9 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -14,6 +14,7 @@ Notes: * Ada * `gcc` + * `gnatpp` * Ansible * `ansible-lint` * API Blueprint @@ -102,6 +103,7 @@ Notes: * Cython (pyrex filetype) * `cython` * D + * `dfmt` * `dls` * `dmd` * `uncrustify` @@ -182,6 +184,7 @@ Notes: * `hdevtools` * `hfmt` * `hie` + * `hindent` * `hlint` * `stack-build`!! * `stack-ghc` diff --git a/doc/ale.txt b/doc/ale.txt index 5541236f..43e124a2 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -341,6 +341,17 @@ completion source for Deoplete is named `'ale'`, and should enabled automatically if Deoplete is enabled and configured correctly. Deoplete integration should not be combined with ALE's own implementation. + *ale-asyncomplete-integration* + +ALE additionally integrates with asyncomplete.vim for offering automatic +completion data. ALE's asyncomplete source requires registration and should +use the defaults provided by the|asyncomplete#sources#ale#get_source_options| function > + + " Use ALE's function for asyncomplete defaults + au User asyncomplete_setup call asyncomplete#register_source(asyncomplete#sources#ale#get_source_options({ + \ 'priority': 10, " Provide your own overrides here + \ })) +> ALE also offers its own completion implementation, which does not require any other plugins. Suggestions will be made while you type after completion is enabled. ALE's own completion implementation can be enabled by setting @@ -1426,8 +1437,8 @@ g:ale_set_signs *g:ale_set_signs* |ALEWarningLine| - All items with `'type': 'W'` |ALEInfoLine| - All items with `'type': 'I'` - With Neovim 0.3.2 or higher, ALE uses `numhl` option to highlight 'number' - column. It uses the following highlight groups. + With Neovim 0.3.2 or higher, ALE can use the `numhl` option to highlight the + 'number' column. It uses the following highlight groups. |ALEErrorSignLineNr| - Items with `'type': 'E'` |ALEWarningSignLineNr| - Items with `'type': 'W'` @@ -1435,6 +1446,9 @@ g:ale_set_signs *g:ale_set_signs* |ALEStyleErrorSignLineNr| - Items with `'type': 'E'` and `'sub_type': 'style'` |ALEStyleWarningSignLineNr| - Items with `'type': 'W'` and `'sub_type': 'style'` + To enable line number highlighting |g:ale_sign_highlight_linenrs| must be + set to `1` before ALE is loaded. + The markers for the highlights can be customized with the following options: |g:ale_sign_error| @@ -1541,6 +1555,16 @@ g:ale_sign_warning *g:ale_sign_warning* The sign for warnings in the sign gutter. +g:ale_sign_highlight_linenrs *g:ale_sign_highlight_linenrs* + + Type: |Number| + Default: `0` + + When set to `1`, this option enables highlighting problems on the 'number' + column in Vim versions that support `numhl` highlights. This option must be + configured before ALE is loaded. + + g:ale_update_tagstack *g:ale_update_tagstack* *b:ale_update_tagstack* Type: |Number| @@ -1943,6 +1967,14 @@ g:ale_languagetool_executable *g:ale_languagetool_executable* The executable to run for languagetool. +g:ale_languagetool_options *g:ale_languagetool_options* + *b:ale_languagetool_options* + Type: |String| + Default: `'--autoDetect'` + + This variable can be set to pass additional options to languagetool. + + ------------------------------------------------------------------------------- 7.3. Options for write-good *ale-write-good-options* @@ -1981,6 +2013,7 @@ documented in additional help files. ada.....................................|ale-ada-options| gcc...................................|ale-ada-gcc| + gnatpp................................|ale-ada-gnatpp| ansible.................................|ale-ansible-options| ansible-lint..........................|ale-ansible-ansible-lint| asciidoc................................|ale-asciidoc-options| @@ -2041,6 +2074,7 @@ documented in additional help files. nvcc..................................|ale-cuda-nvcc| clang-format..........................|ale-cuda-clangformat| d.......................................|ale-d-options| + dfmt..................................|ale-d-dfmt| dls...................................|ale-d-dls| uncrustify............................|ale-d-uncrustify| dart....................................|ale-dart-options| @@ -2107,6 +2141,7 @@ documented in additional help files. cabal-ghc.............................|ale-haskell-cabal-ghc| hdevtools.............................|ale-haskell-hdevtools| hfmt..................................|ale-haskell-hfmt| + hindent...............................|ale-haskell-hindent| hlint.................................|ale-haskell-hlint| stack-build...........................|ale-haskell-stack-build| stack-ghc.............................|ale-haskell-stack-ghc| diff --git a/supported-tools.md b/supported-tools.md index 6cb9f418..d94da030 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -23,6 +23,7 @@ formatting. * Ada * [gcc](https://gcc.gnu.org) + * [gnatpp](https://docs.adacore.com/gnat_ugn-docs/html/gnat_ugn/gnat_ugn/gnat_utility_programs.html#the-gnat-pretty-printer-gnatpp) :floppy_disk: * Ansible * [ansible-lint](https://github.com/willthames/ansible-lint) * API Blueprint @@ -111,6 +112,7 @@ formatting. * Cython (pyrex filetype) * [cython](http://cython.org/) * D + * [dfmt](https://github.com/dlang-community/dfmt) * [dls](https://github.com/d-language-server/dls) * [dmd](https://dlang.org/dmd-linux.html) * [uncrustify](https://github.com/uncrustify/uncrustify) @@ -125,8 +127,8 @@ formatting. * [hadolint](https://github.com/hadolint/hadolint) * Elixir * [credo](https://github.com/rrrene/credo) - * [dialyxir](https://github.com/jeremyjh/dialyxir) - * [dogma](https://github.com/lpil/dogma) + * [dialyxir](https://github.com/jeremyjh/dialyxir) :floppy_disk: + * [dogma](https://github.com/lpil/dogma) :floppy_disk: * [elixir-ls](https://github.com/JakeBecker/elixir-ls) :warning: * [mix](https://hexdocs.pm/mix/Mix.html) :warning: :floppy_disk: * Elm @@ -191,6 +193,7 @@ formatting. * [hdevtools](https://hackage.haskell.org/package/hdevtools) * [hfmt](https://github.com/danstiner/hfmt) * [hie](https://github.com/haskell/haskell-ide-engine) + * [hindent](https://hackage.haskell.org/package/hindent) * [hlint](https://hackage.haskell.org/package/hlint) * [stack-build](https://haskellstack.org/) :floppy_disk: * [stack-ghc](https://haskellstack.org/) diff --git a/test/ada_files/testfile.adb b/test/ada_files/testfile.adb new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/ada_files/testfile.adb diff --git a/test/command_callback/python_paths/with_virtualenv/subdir/foo/bar.pyi b/test/command_callback/python_paths/with_virtualenv/subdir/foo/bar.pyi new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/python_paths/with_virtualenv/subdir/foo/bar.pyi diff --git a/test/command_callback/test_languagetool_command_callback.vader b/test/command_callback/test_languagetool_command_callback.vader index a79662b9..ff6b2064 100644 --- a/test/command_callback/test_languagetool_command_callback.vader +++ b/test/command_callback/test_languagetool_command_callback.vader @@ -12,4 +12,11 @@ Execute(Should be able to set a custom executable): let g:ale_languagetool_executable = 'foobar' AssertLinter 'foobar' , ale#Escape('foobar') - \ . ' --autoDetect %s' + \ . ' --autoDetect %s' + +Execute(Should be able to include custom languagetool options): + let g:ale_languagetool_options = '--language en' + + " is now 'foobar' based on above global + AssertLinter 'foobar', ale#Escape('foobar') + \ . ' --language en %s' diff --git a/test/completion/test_completion_events.vader b/test/completion/test_completion_events.vader index 6bc0035e..5672f8e5 100644 --- a/test/completion/test_completion_events.vader +++ b/test/completion/test_completion_events.vader @@ -62,6 +62,10 @@ After: delfunction CheckCompletionCalled + if exists('*CompleteCallback') + delfunction CompleteCallback + endif + " Stop any timers we left behind. " This stops the tests from failing randomly. call ale#completion#StopTimer() @@ -333,6 +337,30 @@ Execute(b:ale_completion_info should be set up correctly for other sources): \ b:ale_completion_info Assert !exists('b:ale_completion_result') +Execute(b:ale_completion_info should be set up correctly when requesting completions via callback): + let b:ale_completion_result = [] + call setpos('.', [bufnr(''), 3, 14, 0]) + + function! CompleteCallback() abort + echo 'Called' + endfunction + + + call ale#completion#GetCompletions('ale-callback', {'callback': funcref('CompleteCallback')}) + + AssertEqual + \ { + \ 'request_id': 0, + \ 'conn_id': 0, + \ 'column': 14, + \ 'line_length': 14, + \ 'line': 3, + \ 'prefix': 'ab', + \ 'source': 'ale-callback', + \ }, + \ b:ale_completion_info + Assert !exists('b:ale_completion_result') + Execute(The correct keybinds should be configured): redir => g:output silent map <Plug>(ale_show_completion_menu) diff --git a/test/d_files/test.d b/test/d_files/test.d new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/d_files/test.d diff --git a/test/fixers/test_black_fixer_callback.vader b/test/fixers/test_black_fixer_callback.vader index 25ad05db..75864479 100644 --- a/test/fixers/test_black_fixer_callback.vader +++ b/test/fixers/test_black_fixer_callback.vader @@ -36,6 +36,14 @@ Execute(The black callback should include options): \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/black')) . ' --some-option -' }, \ ale#fixers#black#Fix(bufnr('')) +Execute(The black callback should include --pyi for .pyi files): + let g:ale_python_black_change_directory = 0 + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.pyi') + AssertEqual + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/black')) . ' --pyi -' }, + \ ale#fixers#black#Fix(bufnr('')) + Execute(Pipenv is detected when python_black_auto_pipenv is set): let g:ale_python_black_auto_pipenv = 1 let g:ale_python_black_change_directory = 0 diff --git a/test/fixers/test_dfmt_fixer_callback.vader b/test/fixers/test_dfmt_fixer_callback.vader new file mode 100644 index 00000000..5ecb56e6 --- /dev/null +++ b/test/fixers/test_dfmt_fixer_callback.vader @@ -0,0 +1,40 @@ +Before: + Save g:ale_d_dfmt_executable + Save g:ale_d_dfmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_d_dfmt_executable = 'xxxinvalid' + let g:ale_d_dfmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The dfmt callback should return the correct default values): + call ale#test#SetFilename('../d_files/test.d') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' -i' + \ . ' %t', + \ }, + \ ale#fixers#dfmt#Fix(bufnr('')) + +Execute(The dfmt callback should include custom dfmt options): + let g:ale_d_dfmt_options = "--space-after-cast" + call ale#test#SetFilename('../d_files/test.d') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' -i' + \ . ' ' . g:ale_d_dfmt_options + \ . ' %t', + \ }, + \ ale#fixers#dfmt#Fix(bufnr('')) diff --git a/test/fixers/test_gnatpp_fixer_callback.vader b/test/fixers/test_gnatpp_fixer_callback.vader new file mode 100644 index 00000000..a2bf898e --- /dev/null +++ b/test/fixers/test_gnatpp_fixer_callback.vader @@ -0,0 +1,28 @@ +Before: + call ale#assert#SetUpFixerTest('ada', 'gnatpp') + +After: + " Reset fixers, variables, etc. + " + " Vader's 'Restore' command will be called here. + call ale#assert#TearDownFixerTest() + +Execute(The default command should be correct): + call ale#test#SetFilename('../ada_files/testfile.adb') + + AssertFixer + \ { + \ 'command': ale#Escape(g:ale_ada_gnatpp_executable) .' %t', + \ 'read_temporary_file': 1, + \ } + +Execute(The version check should be correct): + call ale#test#SetFilename('../ada_files/testfile.adb') + let g:ale_ada_gnatpp_options = '--no-alignment' + + AssertFixer + \ { + \ 'command': ale#Escape(g:ale_ada_gnatpp_executable) + \ . ' --no-alignment %t', + \ 'read_temporary_file': 1, + \ } diff --git a/test/fixers/test_hindent_fixer_callback.vader b/test/fixers/test_hindent_fixer_callback.vader new file mode 100644 index 00000000..2e5a8b9f --- /dev/null +++ b/test/fixers/test_hindent_fixer_callback.vader @@ -0,0 +1,18 @@ +Before: + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The hindent callback should return the correct default values): + call ale#test#SetFilename('../haskell_files/testfile.hs') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('hindent') + \ . ' %t', + \ }, + \ ale#fixers#hindent#Fix(bufnr('')) diff --git a/test/handler/test_shell_handler.vader b/test/handler/test_shell_handler.vader index 2465f179..c61cf37d 100644 --- a/test/handler/test_shell_handler.vader +++ b/test/handler/test_shell_handler.vader @@ -40,3 +40,138 @@ Execute(The shell handler should parse lines correctly): \ 'qfm:22: :11: :33: :44:', \ 'foo.sh: syntax error at line 9: `done'' unexpected', \ ]) + +Execute(The shell handler should parse Simplified Chinese lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': '未预期的符号“done”附近有语法错误', + \ }, + \ { + \ 'lnum': 90, + \ 'text': '寻找匹配的“"”时遇到了未预期的文件结束符', + \ }, + \ { + \ 'lnum': 111, + \ 'text': '语法错误: 未预期的文件结尾', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh:行0: 未预期的符号“done”附近有语法错误', + \ '/tmp/nvimWL5sOL/2/a.sh:行90: 寻找匹配的“"”时遇到了未预期的文件结束符', + \ '/tmp/nvimWL5sOL/2/a.sh:行111: 语法错误: 未预期的文件结尾', + \ '/tmp/nvimWL5sOL/2/a.sh:行22: :11: :33: :44:', + \ ]) + +Execute(The shell handler should parse Traditional Chinese lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': '未預期的字組「(」附近有語法錯誤', + \ }, + \ { + \ 'lnum': 90, + \ 'text': '尋找匹配的「"」時遇到了未預期的檔案結束符', + \ }, + \ { + \ 'lnum': 111, + \ 'text': '語法錯誤: 未預期的檔案結尾', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh: 列 0: 未預期的字組「(」附近有語法錯誤', + \ '/tmp/nvimWL5sOL/2/a.sh: 列 90: 尋找匹配的「"」時遇到了未預期的檔案結束符', + \ '/tmp/nvimWL5sOL/2/a.sh: 列 111: 語法錯誤: 未預期的檔案結尾', + \ '/tmp/nvimWL5sOL/2/a.sh: 列 22: :11: :33: :44:', + \ ]) + +Execute(The shell handler should parse Japanese lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': "予期しないトークン `(' 周辺に構文エラーがあります", + \ }, + \ { + \ 'lnum': 90, + \ 'text': "予期しないトークン `done' 周辺に構文エラーがあります", + \ }, + \ { + \ 'lnum': 111, + \ 'text': "対応する `\"' を探索中に予期しないファイル終了 (EOF) です", + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ "/tmp/nvimWL5sOL/2/a.sh: 行 0: 予期しないトークン `(' 周辺に構文エラーがあります", + \ "/tmp/nvimWL5sOL/2/a.sh: 行 90: 予期しないトークン `done' 周辺に構文エラーがあります", + \ "/tmp/nvimWL5sOL/2/a.sh: 行 111: 対応する `\"' を探索中に予期しないファイル終了 (EOF) です", + \ "/tmp/nvimWL5sOL/2/a.sh: 行 22: :11: :33: :44:", + \ ]) + +Execute(The shell handler should parse Greek lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': 'συντακτικό σφάλμα κοντά στο μη αναμενόμενο σύμβολο «done»', + \ }, + \ { + \ 'lnum': 90, + \ 'text': 'syntax error: μη αναμενόμενο τέλος αρχείου', + \ }, + \ { + \ 'lnum': 111, + \ 'text': 'μη αναμενόμενο EOF κατά την αναζήτηση «"»', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh: γραμμή 0: συντακτικό σφάλμα κοντά στο μη αναμενόμενο σύμβολο «done»', + \ '/tmp/nvimWL5sOL/2/a.sh: γραμμή 90: syntax error: μη αναμενόμενο τέλος αρχείου', + \ '/tmp/nvimWL5sOL/2/a.sh: γραμμή 111: μη αναμενόμενο EOF κατά την αναζήτηση «"»', + \ "/tmp/nvimWL5sOL/2/a.sh: γραμμή 22: :11: :33: :44:", + \ ]) + +Execute(The shell handler should parse Russian lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': 'синтаксическая ошибка рядом с неожиданным маркером «done»', + \ }, + \ { + \ 'lnum': 90, + \ 'text': 'синтаксическая ошибка: неожиданный конец файла', + \ }, + \ { + \ 'lnum': 111, + \ 'text': 'неожиданный конец файла во время поиска «"»', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh: строка 0: синтаксическая ошибка рядом с неожиданным маркером «done»', + \ '/tmp/nvimWL5sOL/2/a.sh: строка 90: синтаксическая ошибка: неожиданный конец файла', + \ '/tmp/nvimWL5sOL/2/a.sh: строка 111: неожиданный конец файла во время поиска «"»', + \ '/tmp/nvimWL5sOL/2/a.sh: строка 22: :11: :33: :44:', + \ ]) diff --git a/test/script/check-toc b/test/script/check-toc index f3a57ed2..87a61262 100755 --- a/test/script/check-toc +++ b/test/script/check-toc @@ -35,7 +35,7 @@ sed -n "$toc_start_line,$toc_end_line"p doc/ale.txt \ > "$toc_file" # Get all of the doc files in a natural sorted order. -doc_files="$(/bin/ls -1v doc | grep ^ale- | sed 's/^/doc\//' | paste -sd ' ' -)" +doc_files="$(/usr/bin/env ls -1v doc | grep ^ale- | sed 's/^/doc\//' | paste -sd ' ' -)" # shellcheck disable=SC2086 grep -h '\*ale-.*-options\|^[a-z].*\*ale-.*\*$' $doc_files \ diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader index e1208679..2bc8c635 100644 --- a/test/test_ale_info.vader +++ b/test/test_ale_info.vader @@ -47,6 +47,7 @@ Before: Save g:ale_sign_style_error Save g:ale_sign_style_warning Save g:ale_sign_warning + Save g:ale_sign_highlight_linenrs Save g:ale_statusline_format Save g:ale_type_map Save g:ale_use_global_executables @@ -102,6 +103,7 @@ Before: let g:ale_sign_style_error = '>>' let g:ale_sign_style_warning = '--' let g:ale_sign_warning = '--' + let g:ale_sign_highlight_linenrs = 0 let g:ale_statusline_format = ['%d error(s)', '%d warning(s)', 'OK'] let g:ale_type_map = {} let g:ale_use_global_executables = v:null @@ -178,6 +180,7 @@ Before: \ 'let g:ale_sign_style_error = ''>>''', \ 'let g:ale_sign_style_warning = ''--''', \ 'let g:ale_sign_warning = ''--''', + \ 'let g:ale_sign_highlight_linenrs = 0', \ 'let g:ale_statusline_format = [''%d error(s)'', ''%d warning(s)'', ''OK'']', \ 'let g:ale_type_map = {}', \ 'let g:ale_use_global_executables = v:null', diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader index d0bca329..a0763cb1 100644 --- a/test/test_ale_toggle.vader +++ b/test/test_ale_toggle.vader @@ -36,6 +36,7 @@ Before: \ 'ALEEvents', \ 'ALEHighlightBufferGroup', \] + let g:has_nvim_highlight = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace') function! ToggleTestCallback(buffer, output) return [{ @@ -91,6 +92,7 @@ After: unlet! g:expected_groups unlet! b:ale_enabled unlet! g:output + unlet! g:has_nvim_highlight call ale#linter#Reset() @@ -120,9 +122,14 @@ Execute(ALEToggle should reset everything and then run again): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + " Only check the legacy matches if not using the new NeoVIM API. + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual g:expected_groups, ParseAuGroups() AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist @@ -133,7 +140,11 @@ Execute(ALEToggle should reset everything and then run again): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif + AssertEqual g:expected_groups, ParseAuGroups() " Toggle ALE on, everything should be set up and run again. @@ -142,9 +153,13 @@ Execute(ALEToggle should reset everything and then run again): AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual g:expected_groups, ParseAuGroups() AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist @@ -228,9 +243,13 @@ Execute(ALEReset should reset everything for a buffer): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist " Now Toggle ALE off. @@ -241,7 +260,10 @@ Execute(ALEReset should reset everything for a buffer): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif AssertEqual 1, g:ale_enabled @@ -254,9 +276,13 @@ Execute(ALEToggleBuffer should reset everything and then run again): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist " Now Toggle ALE off. @@ -266,7 +292,10 @@ Execute(ALEToggleBuffer should reset everything and then run again): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif " Toggle ALE on, everything should be set up and run again. ALEToggleBuffer @@ -274,9 +303,13 @@ Execute(ALEToggleBuffer should reset everything and then run again): AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual g:expected_groups, ParseAuGroups() AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist @@ -325,9 +358,13 @@ Execute(ALEResetBuffer should reset everything for a buffer): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist " Now Toggle ALE off. @@ -338,7 +375,10 @@ Execute(ALEResetBuffer should reset everything for a buffer): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif AssertEqual 1, g:ale_enabled AssertEqual 1, get(b:, 'ale_enabled', 1) diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader index 045554a3..8ae6f9dc 100644 --- a/test/test_c_flag_parsing.vader +++ b/test/test_c_flag_parsing.vader @@ -14,7 +14,7 @@ Execute(The CFlags parser should be able to parse include directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c']) AssertEqual @@ -25,14 +25,14 @@ Execute(ParseCFlags should ignore -c and -o): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c -o a.out']) Execute(The CFlags parser should be able to parse macro directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -DTEST=1', \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=1 -c file.c']) @@ -40,7 +40,7 @@ Execute(The CFlags parser should be able to parse macro directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -DTEST=$(( 2 * 4 ))', \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c']) @@ -48,14 +48,14 @@ Execute(The CFlags parser should be able to parse shell directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=`date +%s` -c file.c']) Execute(ParseCFlags should be able to parse flags with relative paths): AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), @@ -67,8 +67,8 @@ Execute(ParseCFlags should be able to parse flags with relative paths): Execute(ParseCFlags should be able to parse -Dgoal): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), @@ -77,16 +77,30 @@ Execute(ParseCFlags should be able to parse -Dgoal): \ . ' -DTEST=`date +%s` -c file.c' \ ) +Execute(ParseCFlags should ignore -T and other arguments): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '--sysroot=subdir' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir --sysroot=subdir ' + \ . '-I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + Execute(ParseCFlags should handle paths with spaces in double quotes): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/"dir with spaces"') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) @@ -94,28 +108,29 @@ Execute(ParseCFlags should handle paths with spaces in double quotes): Execute(ParseCFlags should handle paths with spaces in single quotes): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. "/test_c_projects/makefile_project/'dir with spaces'") - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' - \ . "-I'dir with spaces'" . ' -I'. ale#path#Simplify('kernel/include') + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) Execute(ParseCFlags should handle paths with minuses): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' - \ . ' -Idir-with-dash' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' \ . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) @@ -123,14 +138,17 @@ Execute(ParseCFlags should handle paths with minuses): Execute(ParseCFlags should handle -D with minuses): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -Dmacro-with-dash' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . '-Dmacro-with-dash ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' \ . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) @@ -138,11 +156,16 @@ Execute(ParseCFlags should handle -D with minuses): Execute(ParseCFlags should handle flags at the end of the line): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' -Dmacro-with-dash' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-Dmacro-with-dash ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' \ . ' -I'. ale#path#Simplify('kernel/include') \ ) @@ -156,14 +179,14 @@ Execute(ParseCompileCommandsFlags should parse some basic flags): silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I ' . ale#path#Simplify('/usr/include/xmms2'), \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [ \ { - \ 'directory': '/foo/bar/xmms2-mpris', - \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2' + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' - \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', - \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), \ }, \ ] }, {}) @@ -183,14 +206,14 @@ Execute(ParseCompileCommandsFlags should fall back to files in the same director silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I ' . ale#path#Simplify('/usr/include/xmms2'), \ ale#c#ParseCompileCommandsFlags(bufnr(''), {}, { "src": [ \ { - \ 'directory': '/foo/bar/xmms2-mpris', - \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2' + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' - \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', - \ 'file': (has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c', + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'), \ }, \ ] }) @@ -198,7 +221,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .c files fo silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I /usr/include/xmms2', \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -220,7 +243,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .cpp files silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.hpp')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I /usr/include/xmms2', \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -242,7 +265,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .cpp files silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I /usr/include/xmms2', \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -282,13 +305,64 @@ Execute(ParseCompileCommandsFlags should not take commands from .c files for .h \ }, \ ) +Execute(ParseCFlags should not merge flags): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . 'subdir/somedep1.o ' . 'subdir/somedep2.o ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash ' + \ . 'subdir/somedep3.o ' . 'subdir/somedep4.o ' + \ . ' -I'. ale#path#Simplify('kernel/include') . ' ' + \ . 'subdir/somedep5.o ' . 'subdir/somedep6.o ' + \ ) + Execute(ParseCFlags should handle parenthesis and quotes): AssertEqual - \ '-Dgoal=9 -Dtest1="('' '')" file1.o -Dtest2=''(` `)'' file2.o -Dtest3=`(" ")` file3.o', + \ '-Dgoal=9 -Dtest1="('' '')" -Dtest2=''(` `)'' -Dtest3=`(" ")`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla ' \ . '-Dtest1="('' '')" file1.o ' \ . '-Dtest2=''(` `)'' file2.o ' \ . '-Dtest3=`(" ")` file3.o ' \ ) + +Execute(CFlags we want to pass): + AssertEqual + \ '-I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/inc')) + \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/include')) + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incquote')) + \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incsystem')) + \ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter')) + \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2' + \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3' + \ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir' + \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi' + \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++' + \ . ' -iplugindir=dir -march=native -w', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc' + \ . ' -Iinc -I include -iquote incquote -isystem incsystem -idirafter incafter' + \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2' + \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3' + \ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir' + \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi' + \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++' + \ . ' -iplugindir=dir -march=native -w' + \ ) + +Execute(CFlags we dont want to pass): + AssertEqual + \ '', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Wl,option -Wa,option -Wp,option filename.c somelib.a ' + \ . '-fdump-file=name -fdiagnostics-arg -fno-show-column' + \ ) diff --git a/test/test_highlight_placement.vader b/test/test_highlight_placement.vader index 87ac6073..c062018b 100644 --- a/test/test_highlight_placement.vader +++ b/test/test_highlight_placement.vader @@ -8,6 +8,8 @@ Before: Save g:ale_set_quickfix Save g:ale_set_signs + runtime autoload/ale/highlight.vim + let g:ale_run_synchronously = 1 unlet! g:ale_run_synchronously_callbacks let g:ale_set_highlights = 1 @@ -42,16 +44,54 @@ Before: \] endfunction + let g:has_nvim_highlight = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace') + let g:nvim_highlight_matches = {} + + function! ale#highlight#nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) abort + if a:line_end != -1 + throw 'nvim api behavior not supported' + endif + + let l:matches = get(g:nvim_highlight_matches, a:buffer, []) + call filter( + \ l:matches, + \ {_, val -> val.pos1[0] < (a:line_start + 1) }, + \) + endfunction + + function! ale#highlight#nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) abort + if a:col_end == -1 + throw 'nvim api behavior not supported' + endif + + let l:matches = get(g:nvim_highlight_matches, a:buffer, []) + let g:nvim_highlight_matches[a:buffer] = l:matches + + let l:new_match = { + \ 'group': a:hl_group, + \ 'priority': 10, + \ 'pos1': [a:line + 1, a:col_start + 1, a:col_end - a:col_start], + \} + + call add(l:matches, l:new_match) + " sort by line number to emulate getmatches faithfully + call sort(l:matches, {m1, m2 -> m1.pos1[0] - m2.pos1[0]}) + endfunction + " We don't care what the IDs are, just that we have some matches. " The IDs are generated. function! GetMatchesWithoutIDs() abort - let l:list = getmatches() + if g:has_nvim_highlight + return get(g:nvim_highlight_matches, bufnr(''), []) + else + let l:list = getmatches() - for l:item in l:list - call remove(l:item, 'id') - endfor + for l:item in l:list + call remove(l:item, 'id') + endfor - return l:list + return l:list + endif endfunction call ale#linter#Define('testft', { @@ -68,6 +108,8 @@ After: unlet! g:ale_run_synchronously_callbacks unlet! g:items unlet! b:ale_enabled + unlet! g:has_nvim_highlight + unlet! g:nvim_highlight_matches delfunction GenerateResults call ale#linter#Reset() @@ -75,6 +117,8 @@ After: sign unplace * highlight clear SomeOtherGroup + runtime autoload/ale/highlight.vim + Given testft(A Javscript file with warnings/errors): foo bar @@ -102,9 +146,12 @@ Execute(Highlights set by ALE should be removed when buffer cleanup is done): \ {'bufnr': bufnr('%'), 'type': 'E', 'lnum': 3, 'col': 2}, \]) - AssertEqual - \ [{'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}], - \ GetMatchesWithoutIDs() + if !g:has_nvim_highlight + " This check doesn't work with the new API, for some reason. + AssertEqual + \ [{'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}], + \ GetMatchesWithoutIDs() + endif call ale#engine#Cleanup(bufnr('%')) @@ -145,31 +192,53 @@ Execute(Only ALE highlights should be restored when buffers are restored): call matchaddpos('SomeOtherGroup', [[1, 1, 1]]) " We should have both highlights. - AssertEqual - \ [ - \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + " When the newer NeoVim API is used, we don't have to worry about + " other highlights, namespacing is available. + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ ], + \ GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, + \ ], + \ sort(GetMatchesWithoutIDs(), {m1, m2 -> m1.group < m2.group ? -1 : 1}) + endif call ale#highlight#BufferHidden(bufnr('%')) " We should remove our highlight, but not the other one. - AssertEqual - \ [ - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]} - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + AssertEqual [], GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]} + \ ], + \ GetMatchesWithoutIDs() + endif call ale#highlight#UpdateHighlights() " Our highlight should apper again. - AssertEqual - \ [ - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ ], + \ GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, + \ ], + \ sort(GetMatchesWithoutIDs(), {m1, m2 -> m1.group < m2.group ? -1 : 1}) + endif Execute(Higlight end columns should set an appropriate size): call ale#highlight#SetHighlights(bufnr('%'), [ @@ -216,32 +285,67 @@ Execute(Highlighting should support errors spanning many lines): call ale#highlight#SetHighlights(bufnr(''), g:items) - " We should set 2 highlights for the item, as we can only add 8 at a time. - AssertEqual - \ [ - \ { - \ 'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1073741824], - \ 'pos2': [2], 'pos3': [3], 'pos4': [4], 'pos5': [5], 'pos6': [6], - \ 'pos7': [7], 'pos8': [8], - \ }, - \ { - \ 'group': 'ALEError', 'priority': 10, - \ 'pos1': [9], 'pos2': [10, 1, 3] - \ }, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + " The newer NeoVim highlight API produces different output. + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [2, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [4, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [5, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [6, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [7, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [8, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [9, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [10, 1, 3]}, + \ ], + \ GetMatchesWithoutIDs() + else + " We should set 2 highlights for the item, as we can only add 8 at a time. + AssertEqual + \ [ + \ { + \ 'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1073741824], + \ 'pos2': [2], 'pos3': [3], 'pos4': [4], 'pos5': [5], 'pos6': [6], + \ 'pos7': [7], 'pos8': [8], + \ }, + \ { + \ 'group': 'ALEError', 'priority': 10, + \ 'pos1': [9], 'pos2': [10, 1, 3] + \ }, + \ ], + \ GetMatchesWithoutIDs() + endif Execute(Highlights should always be cleared when the buffer highlight list is empty): - " Add our highlights and something else. - call matchaddpos('ALEError', [[1, 1, 1]]) - call matchaddpos('SomeOtherGroup', [[1, 1, 1]]) + if g:has_nvim_highlight + " The newer API uses namespacing. We'll emulate it here. + call ale#highlight#nvim_buf_add_highlight( + \ bufnr(''), + \ 1, + \ 'ALEError', + \ 0, + \ 0, + \ 1, + \) + + AssertEqual + \ [{'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}], + \ GetMatchesWithoutIDs() + else + " Add our highlights and something else. + call matchaddpos('ALEError', [[1, 1, 1]]) + call matchaddpos('SomeOtherGroup', [[1, 1, 1]]) + + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, + \ ], + \ GetMatchesWithoutIDs() + endif - AssertEqual - \ [ - \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ ], - \ GetMatchesWithoutIDs() " Set the List we use for holding highlights for buffers. let b:ale_highlight_items = [] @@ -251,11 +355,13 @@ Execute(Highlights should always be cleared when the buffer highlight list is em call ale#highlight#UpdateHighlights() " Check that we remove our highlights. - AssertEqual - \ [ - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + AssertEqual [], GetMatchesWithoutIDs() + else + AssertEqual + \ [{'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}], + \ GetMatchesWithoutIDs() + endif Execute(Highlights should be cleared when ALE is disabled): let g:ale_enabled = 1 @@ -291,16 +397,30 @@ Execute(Line highlights should be set when signs are disabled): \ {'bufnr': bufnr(''), 'type': 'I', 'lnum': 3, 'col': 1}, \]) - AssertEqual - \ [ - \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, - \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]}, - \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [3, 1, 1]}, - \ {'group': 'aleerrorline', 'priority': 10, 'pos1': [1]}, - \ {'group': 'ALEWarningLine', 'priority': 10, 'pos1': [2]}, - \ {'group': 'ALEInfoLine', 'priority': 10, 'pos1': [3]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + " The output is different with the newer NeoVIM highlight API. + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, + \ {'group': 'ALEErrorLine', 'priority': 10, 'pos1': [1, 1, 1073741824]}, + \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]}, + \ {'group': 'ALEWarningLine', 'priority': 10, 'pos1': [2, 1, 1073741824]}, + \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [3, 1, 1]}, + \ {'group': 'ALEInfoLine', 'priority': 10, 'pos1': [3, 1, 1073741824]} + \ ], + \ GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, + \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]}, + \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [3, 1, 1]}, + \ {'group': 'aleerrorline', 'priority': 10, 'pos1': [1]}, + \ {'group': 'ALEWarningLine', 'priority': 10, 'pos1': [2]}, + \ {'group': 'ALEInfoLine', 'priority': 10, 'pos1': [3]}, + \ ], + \ GetMatchesWithoutIDs() + endif " All of the highlights should be removed. call ale#highlight#RemoveHighlights() |