diff options
Diffstat (limited to 'autoload')
-rw-r--r-- | autoload/ale/c.vim | 131 | ||||
-rw-r--r-- | autoload/ale/completion.vim | 28 | ||||
-rw-r--r-- | autoload/ale/debugging.vim | 7 | ||||
-rw-r--r-- | autoload/ale/fix/registry.vim | 5 | ||||
-rw-r--r-- | autoload/ale/fixers/black.vim | 4 | ||||
-rw-r--r-- | autoload/ale/fixers/gnatpp.vim | 17 | ||||
-rw-r--r-- | autoload/ale/list.vim | 37 | ||||
-rw-r--r-- | autoload/asyncomplete/sources/ale.vim | 26 |
8 files changed, 199 insertions, 56 deletions
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 7cdbabaa..379c0d73 100644 --- a/autoload/ale/debugging.vim +++ b/autoload/ale/debugging.vim @@ -62,7 +62,7 @@ function! s:Echo(message) abort execute 'echo a:message' endfunction -function! s:GetLinterVariables(filetype, linter_names) abort +function! s:GetLinterVariables(filetype, exclude_linter_names) abort let l:variable_list = [] let l:filetype_parts = split(a:filetype, '\.') @@ -73,7 +73,7 @@ function! s:GetLinterVariables(filetype, linter_names) abort " Include matching variables. if !empty(l:match) \&& index(l:filetype_parts, l:match[1]) >= 0 - \&& index(a:linter_names, l:match[2]) >= 0 + \&& index(a:exclude_linter_names, l:match[2]) == -1 call add(l:variable_list, l:key) endif endfor @@ -211,10 +211,11 @@ function! ale#debugging#Info() abort let l:all_names = map(copy(l:all_linters), 'v:val[''name'']') let l:enabled_names = map(copy(l:enabled_linters), 'v:val[''name'']') + let l:exclude_names = filter(copy(l:all_names), 'index(l:enabled_names, v:val) == -1') " Load linter variables to display " This must be done after linters are loaded. - let l:variable_list = s:GetLinterVariables(l:filetype, l:enabled_names) + let l:variable_list = s:GetLinterVariables(l:filetype, l:exclude_names) let l:fixers = ale#fix#registry#SuggestedFixers(l:filetype) let l:fixers = uniq(sort(l:fixers[0] + l:fixers[1])) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index c5ef1544..3850d782 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -325,6 +325,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/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/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/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 |