mirror of
https://github.com/amix/vimrc
synced 2025-06-16 01:25:00 +08:00
Removed syntastic and replaced it with ale
Read more here: https://github.com/w0rp/ale
This commit is contained in:
287
sources_non_forked/ale/autoload/ale.vim
Normal file
287
sources_non_forked/ale/autoload/ale.vim
Normal file
@ -0,0 +1,287 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>, David Alexander <opensource@thelonelyghost.com>
|
||||
" Description: Primary code path for the plugin
|
||||
" Manages execution of linters when requested by autocommands
|
||||
|
||||
let s:lint_timer = -1
|
||||
let s:queued_buffer_number = -1
|
||||
let s:should_lint_file_for_buffer = {}
|
||||
let s:error_delay_ms = 1000 * 60 * 2
|
||||
|
||||
let s:timestamp_map = {}
|
||||
|
||||
" Given a key for a script variable for tracking the time to wait until
|
||||
" a given function should be called, a funcref for a function to call, and
|
||||
" a List of arguments, call the function and return whatever value it returns.
|
||||
"
|
||||
" If the function throws an exception, then the function will not be called
|
||||
" for a while, and 0 will be returned instead.
|
||||
function! ale#CallWithCooldown(timestamp_key, func, arglist) abort
|
||||
let l:now = ale#util#ClockMilliseconds()
|
||||
|
||||
if l:now < get(s:timestamp_map, a:timestamp_key, -1)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let s:timestamp_map[a:timestamp_key] = l:now + s:error_delay_ms
|
||||
|
||||
let l:return_value = call(a:func, a:arglist)
|
||||
|
||||
let s:timestamp_map[a:timestamp_key] = -1
|
||||
|
||||
return l:return_value
|
||||
endfunction
|
||||
|
||||
" Return 1 if a file is too large for ALE to handle.
|
||||
function! ale#FileTooLarge() abort
|
||||
let l:max = ale#Var(bufnr(''), 'maximum_file_size')
|
||||
|
||||
return l:max > 0 ? (line2byte(line('$') + 1) > l:max) : 0
|
||||
endfunction
|
||||
|
||||
let s:getcmdwintype_exists = exists('*getcmdwintype')
|
||||
|
||||
" A function for checking various conditions whereby ALE just shouldn't
|
||||
" attempt to do anything, say if particular buffer types are open in Vim.
|
||||
function! ale#ShouldDoNothing(buffer) abort
|
||||
" The checks are split into separate if statements to make it possible to
|
||||
" profile each check individually with Vim's profiling tools.
|
||||
|
||||
" Don't perform any checks when newer NeoVim versions are exiting.
|
||||
if get(v:, 'exiting', v:null) isnot v:null
|
||||
return 1
|
||||
endif
|
||||
|
||||
" Do nothing for blacklisted files
|
||||
if index(g:ale_filetype_blacklist, getbufvar(a:buffer, '&filetype')) >= 0
|
||||
return 1
|
||||
endif
|
||||
|
||||
" Do nothing if running from command mode
|
||||
if s:getcmdwintype_exists && !empty(getcmdwintype())
|
||||
return 1
|
||||
endif
|
||||
|
||||
let l:filename = fnamemodify(bufname(a:buffer), ':t')
|
||||
|
||||
if l:filename is# '.'
|
||||
return 1
|
||||
endif
|
||||
|
||||
" Do nothing if running in the sandbox
|
||||
if ale#util#InSandbox()
|
||||
return 1
|
||||
endif
|
||||
|
||||
" Do nothing if ALE is disabled.
|
||||
if !ale#Var(a:buffer, 'enabled')
|
||||
return 1
|
||||
endif
|
||||
|
||||
" Do nothing if the file is too large.
|
||||
if ale#FileTooLarge()
|
||||
return 1
|
||||
endif
|
||||
|
||||
" Do nothing from CtrlP buffers with CtrlP-funky.
|
||||
if exists(':CtrlPFunky') is 2
|
||||
\&& getbufvar(a:buffer, '&l:statusline') =~# 'CtrlPMode.*funky'
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" (delay, [linting_flag, buffer_number])
|
||||
function! ale#Queue(delay, ...) abort
|
||||
if a:0 > 2
|
||||
throw 'too many arguments!'
|
||||
endif
|
||||
|
||||
" Default linting_flag to ''
|
||||
let l:linting_flag = get(a:000, 0, '')
|
||||
let l:buffer = get(a:000, 1, bufnr(''))
|
||||
|
||||
return ale#CallWithCooldown(
|
||||
\ 'dont_queue_until',
|
||||
\ function('s:ALEQueueImpl'),
|
||||
\ [a:delay, l:linting_flag, l:buffer],
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! s:ALEQueueImpl(delay, linting_flag, buffer) abort
|
||||
if a:linting_flag isnot# '' && a:linting_flag isnot# 'lint_file'
|
||||
throw "linting_flag must be either '' or 'lint_file'"
|
||||
endif
|
||||
|
||||
if type(a:buffer) != type(0)
|
||||
throw 'buffer_number must be a Number'
|
||||
endif
|
||||
|
||||
if ale#ShouldDoNothing(a:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
" Remember that we want to check files for this buffer.
|
||||
" We will remember this until we finally run the linters, via any event.
|
||||
if a:linting_flag is# 'lint_file'
|
||||
let s:should_lint_file_for_buffer[a:buffer] = 1
|
||||
endif
|
||||
|
||||
if s:lint_timer != -1
|
||||
call timer_stop(s:lint_timer)
|
||||
let s:lint_timer = -1
|
||||
endif
|
||||
|
||||
let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype'))
|
||||
|
||||
" Don't set up buffer data and so on if there are no linters to run.
|
||||
if empty(l:linters)
|
||||
" If we have some previous buffer data, then stop any jobs currently
|
||||
" running and clear everything.
|
||||
if has_key(g:ale_buffer_info, a:buffer)
|
||||
call ale#engine#RunLinters(a:buffer, [], 1)
|
||||
endif
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
if a:delay > 0
|
||||
let s:queued_buffer_number = a:buffer
|
||||
let s:lint_timer = timer_start(a:delay, function('ale#Lint'))
|
||||
else
|
||||
call ale#Lint(-1, a:buffer)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#Lint(...) abort
|
||||
if a:0 > 1
|
||||
" Use the buffer number given as the optional second argument.
|
||||
let l:buffer = a:2
|
||||
elseif a:0 > 0 && a:1 == s:lint_timer
|
||||
" Use the buffer number for the buffer linting was queued for.
|
||||
let l:buffer = s:queued_buffer_number
|
||||
else
|
||||
" Use the current buffer number.
|
||||
let l:buffer = bufnr('')
|
||||
endif
|
||||
|
||||
return ale#CallWithCooldown(
|
||||
\ 'dont_lint_until',
|
||||
\ function('s:ALELintImpl'),
|
||||
\ [l:buffer],
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! s:ALELintImpl(buffer) abort
|
||||
if ale#ShouldDoNothing(a:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
" Use the filetype from the buffer
|
||||
let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype'))
|
||||
let l:should_lint_file = 0
|
||||
|
||||
" Check if we previously requested checking the file.
|
||||
if has_key(s:should_lint_file_for_buffer, a:buffer)
|
||||
unlet s:should_lint_file_for_buffer[a:buffer]
|
||||
" Lint files if they exist.
|
||||
let l:should_lint_file = filereadable(expand('#' . a:buffer . ':p'))
|
||||
endif
|
||||
|
||||
call ale#engine#RunLinters(a:buffer, l:linters, l:should_lint_file)
|
||||
endfunction
|
||||
|
||||
" Reset flags indicating that files should be checked for all buffers.
|
||||
function! ale#ResetLintFileMarkers() abort
|
||||
let s:should_lint_file_for_buffer = {}
|
||||
endfunction
|
||||
|
||||
function! ale#ResetErrorDelays() abort
|
||||
let s:timestamp_map = {}
|
||||
endfunction
|
||||
|
||||
let g:ale_has_override = get(g:, 'ale_has_override', {})
|
||||
|
||||
" Call has(), but check a global Dictionary so we can force flags on or off
|
||||
" for testing purposes.
|
||||
function! ale#Has(feature) abort
|
||||
return get(g:ale_has_override, a:feature, has(a:feature))
|
||||
endfunction
|
||||
|
||||
" Given a buffer number and a variable name, look for that variable in the
|
||||
" buffer scope, then in global scope. If the name does not exist in the global
|
||||
" scope, an exception will be thrown.
|
||||
"
|
||||
" Every variable name will be prefixed with 'ale_'.
|
||||
function! ale#Var(buffer, variable_name) abort
|
||||
let l:nr = str2nr(a:buffer)
|
||||
let l:full_name = 'ale_' . a:variable_name
|
||||
|
||||
if bufexists(l:nr)
|
||||
let l:vars = getbufvar(l:nr, '')
|
||||
elseif has_key(g:, 'ale_fix_buffer_data')
|
||||
let l:vars = get(g:ale_fix_buffer_data, l:nr, {'vars': {}}).vars
|
||||
else
|
||||
let l:vars = {}
|
||||
endif
|
||||
|
||||
return get(l:vars, l:full_name, g:[l:full_name])
|
||||
endfunction
|
||||
|
||||
" Initialize a variable with a default value, if it isn't already set.
|
||||
"
|
||||
" Every variable name will be prefixed with 'ale_'.
|
||||
function! ale#Set(variable_name, default) abort
|
||||
let l:full_name = 'ale_' . a:variable_name
|
||||
let l:value = get(g:, l:full_name, a:default)
|
||||
let g:[l:full_name] = l:value
|
||||
|
||||
return l:value
|
||||
endfunction
|
||||
|
||||
" Escape a string suitably for each platform.
|
||||
" shellescape does not work on Windows.
|
||||
function! ale#Escape(str) abort
|
||||
if fnamemodify(&shell, ':t') is? 'cmd.exe'
|
||||
" If the string contains spaces, it will be surrounded by quotes.
|
||||
" Otherwise, special characters will be escaped with carets (^).
|
||||
return substitute(
|
||||
\ a:str =~# ' '
|
||||
\ ? '"' . substitute(a:str, '"', '""', 'g') . '"'
|
||||
\ : substitute(a:str, '\v([&|<>^])', '^\1', 'g'),
|
||||
\ '%',
|
||||
\ '%%',
|
||||
\ 'g',
|
||||
\)
|
||||
endif
|
||||
|
||||
return shellescape (a:str)
|
||||
endfunction
|
||||
|
||||
" Get the loclist item message according to a given format string.
|
||||
"
|
||||
" See `:help g:ale_loclist_msg_format` and `:help g:ale_echo_msg_format`
|
||||
function! ale#GetLocItemMessage(item, format_string) abort
|
||||
let l:msg = a:format_string
|
||||
let l:severity = g:ale_echo_msg_warning_str
|
||||
let l:code = get(a:item, 'code', '')
|
||||
let l:type = get(a:item, 'type', 'E')
|
||||
let l:linter_name = get(a:item, 'linter_name', '')
|
||||
let l:code_repl = !empty(l:code) ? '\=submatch(1) . l:code . submatch(2)' : ''
|
||||
|
||||
if l:type is# 'E'
|
||||
let l:severity = g:ale_echo_msg_error_str
|
||||
elseif l:type is# 'I'
|
||||
let l:severity = g:ale_echo_msg_info_str
|
||||
endif
|
||||
|
||||
" Replace special markers with certain information.
|
||||
" \=l:variable is used to avoid escaping issues.
|
||||
let l:msg = substitute(l:msg, '\V%severity%', '\=l:severity', 'g')
|
||||
let l:msg = substitute(l:msg, '\V%linter%', '\=l:linter_name', 'g')
|
||||
let l:msg = substitute(l:msg, '\v\%([^\%]*)code([^\%]*)\%', l:code_repl, 'g')
|
||||
" Replace %s with the text.
|
||||
let l:msg = substitute(l:msg, '\V%s', '\=a:item.text', 'g')
|
||||
|
||||
return l:msg
|
||||
endfunction
|
28
sources_non_forked/ale/autoload/ale/balloon.vim
Normal file
28
sources_non_forked/ale/autoload/ale/balloon.vim
Normal file
@ -0,0 +1,28 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: balloonexpr support for ALE.
|
||||
|
||||
function! ale#balloon#MessageForPos(bufnr, lnum, col) abort
|
||||
" Don't show balloons if they are disabled, or linting is disabled.
|
||||
if !ale#Var(a:bufnr, 'set_balloons')
|
||||
\|| !g:ale_enabled
|
||||
\|| !getbufvar(a:bufnr, 'ale_enabled', 1)
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:loclist = get(g:ale_buffer_info, a:bufnr, {'loclist': []}).loclist
|
||||
let l:index = ale#util#BinarySearch(l:loclist, a:bufnr, a:lnum, a:col)
|
||||
|
||||
return l:index >= 0 ? l:loclist[l:index].text : ''
|
||||
endfunction
|
||||
|
||||
function! ale#balloon#Expr() abort
|
||||
return ale#balloon#MessageForPos(v:beval_bufnr, v:beval_lnum, v:beval_col)
|
||||
endfunction
|
||||
|
||||
function! ale#balloon#Disable() abort
|
||||
set noballooneval balloonexpr=
|
||||
endfunction
|
||||
|
||||
function! ale#balloon#Enable() abort
|
||||
set ballooneval balloonexpr=ale#balloon#Expr()
|
||||
endfunction
|
176
sources_non_forked/ale/autoload/ale/c.vim
Normal file
176
sources_non_forked/ale/autoload/ale/c.vim
Normal file
@ -0,0 +1,176 @@
|
||||
" Author: gagbo <gagbobada@gmail.com>, w0rp <devw0rp@gmail.com>, roel0 <postelmansroel@gmail.com>
|
||||
" Description: Functions for integrating with C-family linters.
|
||||
|
||||
call ale#Set('c_parse_makefile', 0)
|
||||
let s:sep = has('win32') ? '\' : '/'
|
||||
|
||||
function! ale#c#FindProjectRoot(buffer) abort
|
||||
for l:project_filename in ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt']
|
||||
let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename)
|
||||
|
||||
if !empty(l:full_path)
|
||||
let l:path = fnamemodify(l:full_path, ':h')
|
||||
|
||||
" Correct .git path detection.
|
||||
if fnamemodify(l:path, ':t') is# '.git'
|
||||
let l:path = fnamemodify(l:path, ':h')
|
||||
endif
|
||||
|
||||
return l:path
|
||||
endif
|
||||
endfor
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort
|
||||
let l:cflags_list = []
|
||||
let l:previous_options = []
|
||||
|
||||
for l:option in a:cflags
|
||||
call add(l:previous_options, l:option)
|
||||
" Check if cflag contained a '-' and should not have been splitted
|
||||
let l:option_list = split(l:option, '\zs')
|
||||
if l:option_list[-1] isnot# ' '
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:option = join(l:previous_options, '-')
|
||||
let l:previous_options = []
|
||||
|
||||
let l:option = '-' . substitute(l:option, '^\s*\(.\{-}\)\s*$', '\1', '')
|
||||
|
||||
" Fix relative paths if needed
|
||||
if stridx(l:option, '-I') >= 0 &&
|
||||
\ stridx(l:option, '-I' . s:sep) < 0
|
||||
let l:rel_path = join(split(l:option, '\zs')[2:], '')
|
||||
let l:rel_path = substitute(l:rel_path, '"', '', 'g')
|
||||
let l:rel_path = substitute(l:rel_path, '''', '', 'g')
|
||||
let l:option = ale#Escape('-I' . a:path_prefix .
|
||||
\ s:sep . l:rel_path)
|
||||
endif
|
||||
|
||||
" Parse the cflag
|
||||
if stridx(l:option, '-I') >= 0 ||
|
||||
\ stridx(l:option, '-D') >= 0
|
||||
if index(l:cflags_list, l:option) < 0
|
||||
call add(l:cflags_list, l:option)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:cflags_list
|
||||
endfunction
|
||||
|
||||
function! ale#c#ParseCFlags(buffer, stdout_make) abort
|
||||
if !g:ale_c_parse_makefile
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:buffer_filename = expand('#' . a:buffer . ':t')
|
||||
let l:cflags = []
|
||||
for l:lines in split(a:stdout_make, '\\n')
|
||||
if stridx(l:lines, l:buffer_filename) >= 0
|
||||
let l:cflags = split(l:lines, '-')
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
|
||||
return ale#c#ParseCFlagsToList(fnamemodify(l:makefile_path, ':p:h'), l:cflags)
|
||||
endfunction
|
||||
|
||||
function! ale#c#GetCFlags(buffer, output) abort
|
||||
let l:cflags = ' '
|
||||
|
||||
if g:ale_c_parse_makefile && !empty(a:output)
|
||||
let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') . ' '
|
||||
endif
|
||||
|
||||
if l:cflags is# ' '
|
||||
let l:cflags = ale#c#IncludeOptions(ale#c#FindLocalHeaderPaths(a:buffer))
|
||||
endif
|
||||
|
||||
return l:cflags
|
||||
endfunction
|
||||
|
||||
function! ale#c#GetMakeCommand(buffer) abort
|
||||
if g:ale_c_parse_makefile
|
||||
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
|
||||
if !empty(l:makefile_path)
|
||||
return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n'
|
||||
endif
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Given a buffer number, search for a project root, and output a List
|
||||
" of directories to include based on some heuristics.
|
||||
"
|
||||
" For projects with headers in the project root, the project root will
|
||||
" be returned.
|
||||
"
|
||||
" For projects with an 'include' directory, that directory will be returned.
|
||||
function! ale#c#FindLocalHeaderPaths(buffer) abort
|
||||
let l:project_root = ale#c#FindProjectRoot(a:buffer)
|
||||
|
||||
if empty(l:project_root)
|
||||
return []
|
||||
endif
|
||||
|
||||
" See if we can find .h files directory in the project root.
|
||||
" If we can, that's our include directory.
|
||||
if !empty(globpath(l:project_root, '*.h', 0))
|
||||
return [l:project_root]
|
||||
endif
|
||||
|
||||
" Look for .hpp files too.
|
||||
if !empty(globpath(l:project_root, '*.hpp', 0))
|
||||
return [l:project_root]
|
||||
endif
|
||||
|
||||
" If we find an 'include' directory in the project root, then use that.
|
||||
if isdirectory(l:project_root . '/include')
|
||||
return [ale#path#Simplify(l:project_root . s:sep . 'include')]
|
||||
endif
|
||||
|
||||
return []
|
||||
endfunction
|
||||
|
||||
" Given a List of include paths, create a string containing the -I include
|
||||
" options for those paths, with the paths escaped for use in the shell.
|
||||
function! ale#c#IncludeOptions(include_paths) abort
|
||||
let l:option_list = []
|
||||
|
||||
for l:path in a:include_paths
|
||||
call add(l:option_list, '-I' . ale#Escape(l:path))
|
||||
endfor
|
||||
|
||||
if empty(l:option_list)
|
||||
return ''
|
||||
endif
|
||||
|
||||
return ' ' . join(l:option_list) . ' '
|
||||
endfunction
|
||||
|
||||
let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [
|
||||
\ 'build',
|
||||
\ 'bin',
|
||||
\])
|
||||
|
||||
" Given a buffer number, find the build subdirectory with compile commands
|
||||
" The subdirectory is returned without the trailing /
|
||||
function! ale#c#FindCompileCommands(buffer) abort
|
||||
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
|
||||
for l:dirname in ale#Var(a:buffer, 'c_build_dir_names')
|
||||
let l:c_build_dir = l:path . s:sep . l:dirname
|
||||
|
||||
if filereadable(l:c_build_dir . '/compile_commands.json')
|
||||
return l:c_build_dir
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return ''
|
||||
endfunction
|
57
sources_non_forked/ale/autoload/ale/command.vim
Normal file
57
sources_non_forked/ale/autoload/ale/command.vim
Normal file
@ -0,0 +1,57 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Special command formatting for creating temporary files and
|
||||
" passing buffer filenames easily.
|
||||
|
||||
function! s:TemporaryFilename(buffer) abort
|
||||
let l:filename = fnamemodify(bufname(a:buffer), ':t')
|
||||
|
||||
if empty(l:filename)
|
||||
" If the buffer's filename is empty, create a dummy filename.
|
||||
let l:ft = getbufvar(a:buffer, '&filetype')
|
||||
let l:filename = 'file' . ale#filetypes#GuessExtension(l:ft)
|
||||
endif
|
||||
|
||||
" Create a temporary filename, <temp_dir>/<original_basename>
|
||||
" The file itself will not be created by this function.
|
||||
return tempname() . (has('win32') ? '\' : '/') . l:filename
|
||||
endfunction
|
||||
|
||||
" Given a command string, replace every...
|
||||
" %s -> with the current filename
|
||||
" %t -> with the name of an unused file in a temporary directory
|
||||
" %% -> with a literal %
|
||||
function! ale#command#FormatCommand(buffer, command, pipe_file_if_needed) abort
|
||||
let l:temporary_file = ''
|
||||
let l:command = a:command
|
||||
|
||||
" First replace all uses of %%, used for literal percent characters,
|
||||
" with an ugly string.
|
||||
let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g')
|
||||
|
||||
" Replace all %s occurrences in the string with the name of the current
|
||||
" file.
|
||||
if l:command =~# '%s'
|
||||
let l:filename = fnamemodify(bufname(a:buffer), ':p')
|
||||
let l:command = substitute(l:command, '%s', '\=ale#Escape(l:filename)', 'g')
|
||||
endif
|
||||
|
||||
if l:command =~# '%t'
|
||||
" Create a temporary filename, <temp_dir>/<original_basename>
|
||||
" The file itself will not be created by this function.
|
||||
let l:temporary_file = s:TemporaryFilename(a:buffer)
|
||||
let l:command = substitute(l:command, '%t', '\=ale#Escape(l:temporary_file)', 'g')
|
||||
endif
|
||||
|
||||
" Finish formatting so %% becomes %.
|
||||
let l:command = substitute(l:command, '<<PERCENTS>>', '%', 'g')
|
||||
|
||||
if a:pipe_file_if_needed && empty(l:temporary_file)
|
||||
" If we are to send the Vim buffer to a command, we'll do it
|
||||
" in the shell. We'll write out the file to a temporary file,
|
||||
" and then read it back in, in the shell.
|
||||
let l:temporary_file = s:TemporaryFilename(a:buffer)
|
||||
let l:command = l:command . ' < ' . ale#Escape(l:temporary_file)
|
||||
endif
|
||||
|
||||
return [l:temporary_file, l:command]
|
||||
endfunction
|
477
sources_non_forked/ale/autoload/ale/completion.vim
Normal file
477
sources_non_forked/ale/autoload/ale/completion.vim
Normal file
@ -0,0 +1,477 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Completion support for LSP linters
|
||||
|
||||
let s:timer_id = -1
|
||||
let s:last_done_pos = []
|
||||
|
||||
" CompletionItemKind values from the LSP protocol.
|
||||
let s:LSP_COMPLETION_TEXT_KIND = 1
|
||||
let s:LSP_COMPLETION_METHOD_KIND = 2
|
||||
let s:LSP_COMPLETION_FUNCTION_KIND = 3
|
||||
let s:LSP_COMPLETION_CONSTRUCTOR_KIND = 4
|
||||
let s:LSP_COMPLETION_FIELD_KIND = 5
|
||||
let s:LSP_COMPLETION_VARIABLE_KIND = 6
|
||||
let s:LSP_COMPLETION_CLASS_KIND = 7
|
||||
let s:LSP_COMPLETION_INTERFACE_KIND = 8
|
||||
let s:LSP_COMPLETION_MODULE_KIND = 9
|
||||
let s:LSP_COMPLETION_PROPERTY_KIND = 10
|
||||
let s:LSP_COMPLETION_UNIT_KIND = 11
|
||||
let s:LSP_COMPLETION_VALUE_KIND = 12
|
||||
let s:LSP_COMPLETION_ENUM_KIND = 13
|
||||
let s:LSP_COMPLETION_KEYWORD_KIND = 14
|
||||
let s:LSP_COMPLETION_SNIPPET_KIND = 15
|
||||
let s:LSP_COMPLETION_COLOR_KIND = 16
|
||||
let s:LSP_COMPLETION_FILE_KIND = 17
|
||||
let s:LSP_COMPLETION_REFERENCE_KIND = 18
|
||||
|
||||
" Regular expressions for checking the characters in the line before where
|
||||
" the insert cursor is. If one of these matches, we'll check for completions.
|
||||
let s:should_complete_map = {
|
||||
\ '<default>': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$',
|
||||
\}
|
||||
|
||||
" Regular expressions for finding the start column to replace with completion.
|
||||
let s:omni_start_map = {
|
||||
\ '<default>': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$',
|
||||
\}
|
||||
|
||||
" A map of exact characters for triggering LSP completions.
|
||||
let s:trigger_character_map = {
|
||||
\ '<default>': ['.'],
|
||||
\}
|
||||
|
||||
function! s:GetFiletypeValue(map, filetype) abort
|
||||
for l:part in reverse(split(a:filetype, '\.'))
|
||||
let l:regex = get(a:map, l:part, [])
|
||||
|
||||
if !empty(l:regex)
|
||||
return l:regex
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Use the default regex for other files.
|
||||
return a:map['<default>']
|
||||
endfunction
|
||||
|
||||
" Check if we should look for completions for a language.
|
||||
function! ale#completion#GetPrefix(filetype, line, column) abort
|
||||
let l:regex = s:GetFiletypeValue(s:should_complete_map, a:filetype)
|
||||
" The column we're using completions for is where we are inserting text,
|
||||
" like so:
|
||||
" abc
|
||||
" ^
|
||||
" So we need check the text in the column before that position.
|
||||
return matchstr(getline(a:line)[: a:column - 2], l:regex)
|
||||
endfunction
|
||||
|
||||
function! ale#completion#GetTriggerCharacter(filetype, prefix) abort
|
||||
let l:char_list = s:GetFiletypeValue(s:trigger_character_map, a:filetype)
|
||||
|
||||
if index(l:char_list, a:prefix) >= 0
|
||||
return a:prefix
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Filter(suggestions, prefix) abort
|
||||
" For completing...
|
||||
" foo.
|
||||
" ^
|
||||
" We need to include all of the given suggestions.
|
||||
if a:prefix is# '.'
|
||||
return a:suggestions
|
||||
endif
|
||||
|
||||
let l:filtered_suggestions = []
|
||||
|
||||
" Filter suggestions down to those starting with the prefix we used for
|
||||
" finding suggestions in the first place.
|
||||
"
|
||||
" Some completion tools will include suggestions which don't even start
|
||||
" with the characters we have already typed.
|
||||
for l:item in a:suggestions
|
||||
" A List of String values or a List of completion item Dictionaries
|
||||
" is accepted here.
|
||||
let l:word = type(l:item) == type('') ? l:item : l:item.word
|
||||
|
||||
" Add suggestions if the suggestion starts with a case-insensitive
|
||||
" match for the prefix.
|
||||
if l:word[: len(a:prefix) - 1] is? a:prefix
|
||||
call add(l:filtered_suggestions, l:item)
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:filtered_suggestions
|
||||
endfunction
|
||||
|
||||
function! s:ReplaceCompleteopt() abort
|
||||
if !exists('b:ale_old_completopt')
|
||||
let b:ale_old_completopt = &l:completeopt
|
||||
endif
|
||||
|
||||
let &l:completeopt = 'menu,menuone,preview,noselect,noinsert'
|
||||
endfunction
|
||||
|
||||
function! ale#completion#OmniFunc(findstart, base) abort
|
||||
if a:findstart
|
||||
let l:line = b:ale_completion_info.line
|
||||
let l:column = b:ale_completion_info.column
|
||||
let l:regex = s:GetFiletypeValue(s:omni_start_map, &filetype)
|
||||
let l:up_to_column = getline(l:line)[: l:column - 2]
|
||||
let l:match = matchstr(l:up_to_column, l:regex)
|
||||
|
||||
return l:column - len(l:match) - 1
|
||||
else
|
||||
" Parse a new response if there is one.
|
||||
if exists('b:ale_completion_response')
|
||||
\&& exists('b:ale_completion_parser')
|
||||
let l:response = b:ale_completion_response
|
||||
let l:parser = b:ale_completion_parser
|
||||
|
||||
unlet b:ale_completion_response
|
||||
unlet b:ale_completion_parser
|
||||
|
||||
let b:ale_completion_result = function(l:parser)(l:response)
|
||||
endif
|
||||
|
||||
call s:ReplaceCompleteopt()
|
||||
|
||||
return get(b:, 'ale_completion_result', [])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Show(response, completion_parser) abort
|
||||
" Remember the old omnifunc value, if there is one.
|
||||
" If we don't store an old one, we'll just never reset the option.
|
||||
" This will stop some random exceptions from appearing.
|
||||
if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
|
||||
let b:ale_old_omnifunc = &l:omnifunc
|
||||
endif
|
||||
|
||||
" Set the list in the buffer, temporarily replace omnifunc with our
|
||||
" function, and then start omni-completion.
|
||||
let b:ale_completion_response = a:response
|
||||
let b:ale_completion_parser = a:completion_parser
|
||||
let &l:omnifunc = 'ale#completion#OmniFunc'
|
||||
call s:ReplaceCompleteopt()
|
||||
call ale#util#FeedKeys("\<C-x>\<C-o>", 'n')
|
||||
endfunction
|
||||
|
||||
function! s:CompletionStillValid(request_id) abort
|
||||
let [l:line, l:column] = getcurpos()[1:2]
|
||||
|
||||
return has_key(b:, 'ale_completion_info')
|
||||
\&& b:ale_completion_info.request_id == a:request_id
|
||||
\&& b:ale_completion_info.line == l:line
|
||||
\&& b:ale_completion_info.column == l:column
|
||||
endfunction
|
||||
|
||||
function! ale#completion#ParseTSServerCompletions(response) abort
|
||||
let l:names = []
|
||||
|
||||
for l:suggestion in a:response.body
|
||||
call add(l:names, l:suggestion.name)
|
||||
endfor
|
||||
|
||||
return l:names
|
||||
endfunction
|
||||
|
||||
function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
|
||||
let l:results = []
|
||||
|
||||
for l:suggestion in a:response.body
|
||||
let l:displayParts = []
|
||||
|
||||
for l:part in l:suggestion.displayParts
|
||||
call add(l:displayParts, l:part.text)
|
||||
endfor
|
||||
|
||||
" Each one of these parts has 'kind' properties
|
||||
let l:documentationParts = []
|
||||
|
||||
for l:part in get(l:suggestion, 'documentation', [])
|
||||
call add(l:documentationParts, l:part.text)
|
||||
endfor
|
||||
|
||||
if l:suggestion.kind is# 'clasName'
|
||||
let l:kind = 'f'
|
||||
elseif l:suggestion.kind is# 'parameterName'
|
||||
let l:kind = 'f'
|
||||
else
|
||||
let l:kind = 'v'
|
||||
endif
|
||||
|
||||
" See :help complete-items
|
||||
call add(l:results, {
|
||||
\ 'word': l:suggestion.name,
|
||||
\ 'kind': l:kind,
|
||||
\ 'icase': 1,
|
||||
\ 'menu': join(l:displayParts, ''),
|
||||
\ 'info': join(l:documentationParts, ''),
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:results
|
||||
endfunction
|
||||
|
||||
function! ale#completion#ParseLSPCompletions(response) abort
|
||||
let l:item_list = []
|
||||
|
||||
if type(get(a:response, 'result')) is type([])
|
||||
let l:item_list = a:response.result
|
||||
elseif type(get(a:response, 'result')) is type({})
|
||||
\&& type(get(a:response.result, 'items')) is type([])
|
||||
let l:item_list = a:response.result.items
|
||||
endif
|
||||
|
||||
let l:results = []
|
||||
|
||||
for l:item in l:item_list
|
||||
" See :help complete-items for Vim completion kinds
|
||||
if l:item.kind is s:LSP_COMPLETION_METHOD_KIND
|
||||
let l:kind = 'm'
|
||||
elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND
|
||||
let l:kind = 'm'
|
||||
elseif l:item.kind is s:LSP_COMPLETION_FUNCTION_KIND
|
||||
let l:kind = 'f'
|
||||
elseif l:item.kind is s:LSP_COMPLETION_CLASS_KIND
|
||||
let l:kind = 'f'
|
||||
elseif l:item.kind is s:LSP_COMPLETION_INTERFACE_KIND
|
||||
let l:kind = 'f'
|
||||
else
|
||||
let l:kind = 'v'
|
||||
endif
|
||||
|
||||
call add(l:results, {
|
||||
\ 'word': l:item.label,
|
||||
\ 'kind': l:kind,
|
||||
\ 'icase': 1,
|
||||
\ 'menu': l:item.detail,
|
||||
\ 'info': l:item.documentation,
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:results
|
||||
endfunction
|
||||
|
||||
function! ale#completion#HandleTSServerResponse(conn_id, response) abort
|
||||
if !s:CompletionStillValid(get(a:response, 'request_seq'))
|
||||
return
|
||||
endif
|
||||
|
||||
if !has_key(a:response, 'body')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:command = get(a:response, 'command', '')
|
||||
|
||||
if l:command is# 'completions'
|
||||
let l:names = ale#completion#Filter(
|
||||
\ ale#completion#ParseTSServerCompletions(a:response),
|
||||
\ b:ale_completion_info.prefix,
|
||||
\)[: g:ale_completion_max_suggestions - 1]
|
||||
|
||||
if !empty(l:names)
|
||||
let b:ale_completion_info.request_id = ale#lsp#Send(
|
||||
\ b:ale_completion_info.conn_id,
|
||||
\ ale#lsp#tsserver_message#CompletionEntryDetails(
|
||||
\ bufnr(''),
|
||||
\ b:ale_completion_info.line,
|
||||
\ b:ale_completion_info.column,
|
||||
\ l:names,
|
||||
\ ),
|
||||
\)
|
||||
endif
|
||||
elseif l:command is# 'completionEntryDetails'
|
||||
call ale#completion#Show(
|
||||
\ a:response,
|
||||
\ 'ale#completion#ParseTSServerCompletionEntryDetails',
|
||||
\)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! ale#completion#HandleLSPResponse(conn_id, response) abort
|
||||
if !s:CompletionStillValid(get(a:response, 'id'))
|
||||
return
|
||||
endif
|
||||
|
||||
call ale#completion#Show(
|
||||
\ a:response,
|
||||
\ 'ale#completion#ParseLSPCompletions',
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! s:GetLSPCompletions(linter) abort
|
||||
let l:buffer = bufnr('')
|
||||
let l:Callback = a:linter.lsp is# 'tsserver'
|
||||
\ ? function('ale#completion#HandleTSServerResponse')
|
||||
\ : function('ale#completion#HandleLSPResponse')
|
||||
|
||||
let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback)
|
||||
|
||||
if empty(l:lsp_details)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:id = l:lsp_details.connection_id
|
||||
let l:root = l:lsp_details.project_root
|
||||
|
||||
if a:linter.lsp is# 'tsserver'
|
||||
let l:message = ale#lsp#tsserver_message#Completions(
|
||||
\ l:buffer,
|
||||
\ b:ale_completion_info.line,
|
||||
\ b:ale_completion_info.column,
|
||||
\ b:ale_completion_info.prefix,
|
||||
\)
|
||||
else
|
||||
" Send a message saying the buffer has changed first, otherwise
|
||||
" completions won't know what text is nearby.
|
||||
call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root)
|
||||
|
||||
" For LSP completions, we need to clamp the column to the length of
|
||||
" the line. python-language-server and perhaps others do not implement
|
||||
" this correctly.
|
||||
let l:message = ale#lsp#message#Completion(
|
||||
\ l:buffer,
|
||||
\ b:ale_completion_info.line,
|
||||
\ min([
|
||||
\ b:ale_completion_info.line_length,
|
||||
\ b:ale_completion_info.column,
|
||||
\ ]),
|
||||
\ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix),
|
||||
\)
|
||||
endif
|
||||
|
||||
let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
|
||||
|
||||
if l:request_id
|
||||
let b:ale_completion_info.conn_id = l:id
|
||||
let b:ale_completion_info.request_id = l:request_id
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#completion#GetCompletions() abort
|
||||
if !g:ale_completion_enabled
|
||||
return
|
||||
endif
|
||||
|
||||
let [l:line, l:column] = getcurpos()[1:2]
|
||||
|
||||
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
|
||||
|
||||
if empty(l:prefix)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:line_length = len(getline('.'))
|
||||
|
||||
let b:ale_completion_info = {
|
||||
\ 'line': l:line,
|
||||
\ 'line_length': l:line_length,
|
||||
\ 'column': l:column,
|
||||
\ 'prefix': l:prefix,
|
||||
\ 'conn_id': 0,
|
||||
\ 'request_id': 0,
|
||||
\}
|
||||
|
||||
for l:linter in ale#linter#Get(&filetype)
|
||||
if !empty(l:linter.lsp)
|
||||
if l:linter.lsp is# 'tsserver'
|
||||
\|| get(g:, 'ale_completion_experimental_lsp_support', 0)
|
||||
call s:GetLSPCompletions(l:linter)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:TimerHandler(...) abort
|
||||
let s:timer_id = -1
|
||||
|
||||
let [l:line, l:column] = getcurpos()[1:2]
|
||||
|
||||
" When running the timer callback, we have to be sure that the cursor
|
||||
" hasn't moved from where it was when we requested completions by typing.
|
||||
if s:timer_pos == [l:line, l:column]
|
||||
call ale#completion#GetCompletions()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Stop any completion timer that is queued. This is useful for tests.
|
||||
function! ale#completion#StopTimer() abort
|
||||
if s:timer_id != -1
|
||||
call timer_stop(s:timer_id)
|
||||
endif
|
||||
|
||||
let s:timer_id = -1
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Queue() abort
|
||||
if !g:ale_completion_enabled
|
||||
return
|
||||
endif
|
||||
|
||||
let s:timer_pos = getcurpos()[1:2]
|
||||
|
||||
if s:timer_pos == s:last_done_pos
|
||||
" Do not ask for completions if the cursor rests on the position we
|
||||
" last completed on.
|
||||
return
|
||||
endif
|
||||
|
||||
" If we changed the text again while we're still waiting for a response,
|
||||
" then invalidate the requests before the timer ticks again.
|
||||
if exists('b:ale_completion_info')
|
||||
let b:ale_completion_info.request_id = 0
|
||||
endif
|
||||
|
||||
call ale#completion#StopTimer()
|
||||
|
||||
let s:timer_id = timer_start(g:ale_completion_delay, function('s:TimerHandler'))
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Done() abort
|
||||
silent! pclose
|
||||
|
||||
" Reset settings when completion is done.
|
||||
if exists('b:ale_old_omnifunc')
|
||||
if b:ale_old_omnifunc isnot# 'pythoncomplete#Complete'
|
||||
let &l:omnifunc = b:ale_old_omnifunc
|
||||
endif
|
||||
|
||||
unlet b:ale_old_omnifunc
|
||||
endif
|
||||
|
||||
if exists('b:ale_old_completopt')
|
||||
let &l:completeopt = b:ale_old_completopt
|
||||
unlet b:ale_old_completopt
|
||||
endif
|
||||
|
||||
let s:last_done_pos = getcurpos()[1:2]
|
||||
endfunction
|
||||
|
||||
function! s:Setup(enabled) abort
|
||||
augroup ALECompletionGroup
|
||||
autocmd!
|
||||
|
||||
if a:enabled
|
||||
autocmd TextChangedI * call ale#completion#Queue()
|
||||
autocmd CompleteDone * call ale#completion#Done()
|
||||
endif
|
||||
augroup END
|
||||
|
||||
if !a:enabled
|
||||
augroup! ALECompletionGroup
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Enable() abort
|
||||
let g:ale_completion_enabled = 1
|
||||
call s:Setup(1)
|
||||
endfunction
|
||||
|
||||
function! ale#completion#Disable() abort
|
||||
let g:ale_completion_enabled = 0
|
||||
call s:Setup(0)
|
||||
endfunction
|
136
sources_non_forked/ale/autoload/ale/cursor.vim
Normal file
136
sources_non_forked/ale/autoload/ale/cursor.vim
Normal file
@ -0,0 +1,136 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Echoes lint message for the current line, if any
|
||||
|
||||
let s:cursor_timer = -1
|
||||
let s:last_pos = [0, 0, 0]
|
||||
|
||||
function! ale#cursor#TruncatedEcho(original_message) abort
|
||||
let l:message = a:original_message
|
||||
" Change tabs to spaces.
|
||||
let l:message = substitute(l:message, "\t", ' ', 'g')
|
||||
" Remove any newlines in the message.
|
||||
let l:message = substitute(l:message, "\n", '', 'g')
|
||||
|
||||
" We need to remember the setting for shortmess and reset it again.
|
||||
let l:shortmess_options = &l:shortmess
|
||||
|
||||
try
|
||||
let l:cursor_position = getcurpos()
|
||||
|
||||
" The message is truncated and saved to the history.
|
||||
setlocal shortmess+=T
|
||||
exec "norm! :echomsg l:message\n"
|
||||
|
||||
" Reset the cursor position if we moved off the end of the line.
|
||||
" Using :norm and :echomsg can move the cursor off the end of the
|
||||
" line.
|
||||
if l:cursor_position != getcurpos()
|
||||
call setpos('.', l:cursor_position)
|
||||
endif
|
||||
finally
|
||||
let &l:shortmess = l:shortmess_options
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:FindItemAtCursor() abort
|
||||
let l:buf = bufnr('')
|
||||
let l:info = get(g:ale_buffer_info, l:buf, {})
|
||||
let l:loclist = get(l:info, 'loclist', [])
|
||||
let l:pos = getcurpos()
|
||||
let l:index = ale#util#BinarySearch(l:loclist, l:buf, l:pos[1], l:pos[2])
|
||||
let l:loc = l:index >= 0 ? l:loclist[l:index] : {}
|
||||
|
||||
return [l:info, l:loc]
|
||||
endfunction
|
||||
|
||||
function! s:StopCursorTimer() abort
|
||||
if s:cursor_timer != -1
|
||||
call timer_stop(s:cursor_timer)
|
||||
let s:cursor_timer = -1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#cursor#EchoCursorWarning(...) abort
|
||||
return ale#CallWithCooldown('dont_echo_until', function('s:EchoImpl'), [])
|
||||
endfunction
|
||||
|
||||
function! s:EchoImpl() abort
|
||||
if !g:ale_echo_cursor
|
||||
return
|
||||
endif
|
||||
|
||||
" Only echo the warnings in normal mode, otherwise we will get problems.
|
||||
if mode() isnot# 'n'
|
||||
return
|
||||
endif
|
||||
|
||||
if ale#ShouldDoNothing(bufnr(''))
|
||||
return
|
||||
endif
|
||||
|
||||
let l:buffer = bufnr('')
|
||||
let [l:info, l:loc] = s:FindItemAtCursor()
|
||||
|
||||
if !empty(l:loc)
|
||||
let l:format = ale#Var(l:buffer, 'echo_msg_format')
|
||||
let l:msg = ale#GetLocItemMessage(l:loc, l:format)
|
||||
call ale#cursor#TruncatedEcho(l:msg)
|
||||
let l:info.echoed = 1
|
||||
elseif get(l:info, 'echoed')
|
||||
" We'll only clear the echoed message when moving off errors once,
|
||||
" so we don't continually clear the echo line.
|
||||
execute 'echo'
|
||||
let l:info.echoed = 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#cursor#EchoCursorWarningWithDelay() abort
|
||||
if !g:ale_echo_cursor
|
||||
return
|
||||
endif
|
||||
|
||||
" Only echo the warnings in normal mode, otherwise we will get problems.
|
||||
if mode() isnot# 'n'
|
||||
return
|
||||
endif
|
||||
|
||||
call s:StopCursorTimer()
|
||||
|
||||
let l:pos = getcurpos()[0:2]
|
||||
|
||||
" Check the current buffer, line, and column number against the last
|
||||
" recorded position. If the position has actually changed, *then*
|
||||
" we should echo something. Otherwise we can end up doing processing
|
||||
" the echo message far too frequently.
|
||||
if l:pos != s:last_pos
|
||||
let l:delay = ale#Var(bufnr(''), 'echo_delay')
|
||||
|
||||
let s:last_pos = l:pos
|
||||
let s:cursor_timer = timer_start(
|
||||
\ l:delay,
|
||||
\ function('ale#cursor#EchoCursorWarning')
|
||||
\)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#cursor#ShowCursorDetail() abort
|
||||
" Only echo the warnings in normal mode, otherwise we will get problems.
|
||||
if mode() isnot# 'n'
|
||||
return
|
||||
endif
|
||||
|
||||
if ale#ShouldDoNothing(bufnr(''))
|
||||
return
|
||||
endif
|
||||
|
||||
call s:StopCursorTimer()
|
||||
|
||||
let [l:info, l:loc] = s:FindItemAtCursor()
|
||||
|
||||
if !empty(l:loc)
|
||||
let l:message = get(l:loc, 'detail', l:loc.text)
|
||||
|
||||
call ale#preview#Show(split(l:message, "\n"))
|
||||
execute 'echo'
|
||||
endif
|
||||
endfunction
|
213
sources_non_forked/ale/autoload/ale/debugging.vim
Normal file
213
sources_non_forked/ale/autoload/ale/debugging.vim
Normal file
@ -0,0 +1,213 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: This file implements debugging information for ALE
|
||||
|
||||
let s:global_variable_list = [
|
||||
\ 'ale_cache_executable_check_failures',
|
||||
\ 'ale_change_sign_column_color',
|
||||
\ 'ale_command_wrapper',
|
||||
\ 'ale_completion_delay',
|
||||
\ 'ale_completion_enabled',
|
||||
\ 'ale_completion_max_suggestions',
|
||||
\ 'ale_echo_cursor',
|
||||
\ 'ale_echo_msg_error_str',
|
||||
\ 'ale_echo_msg_format',
|
||||
\ 'ale_echo_msg_info_str',
|
||||
\ 'ale_echo_msg_warning_str',
|
||||
\ 'ale_enabled',
|
||||
\ 'ale_fix_on_save',
|
||||
\ 'ale_fixers',
|
||||
\ 'ale_history_enabled',
|
||||
\ 'ale_history_log_output',
|
||||
\ 'ale_keep_list_window_open',
|
||||
\ 'ale_lint_delay',
|
||||
\ 'ale_lint_on_enter',
|
||||
\ 'ale_lint_on_filetype_changed',
|
||||
\ 'ale_lint_on_save',
|
||||
\ 'ale_lint_on_text_changed',
|
||||
\ 'ale_lint_on_insert_leave',
|
||||
\ 'ale_linter_aliases',
|
||||
\ 'ale_linters',
|
||||
\ 'ale_linters_explicit',
|
||||
\ 'ale_list_window_size',
|
||||
\ 'ale_list_vertical',
|
||||
\ 'ale_loclist_msg_format',
|
||||
\ 'ale_max_buffer_history_size',
|
||||
\ 'ale_max_signs',
|
||||
\ 'ale_maximum_file_size',
|
||||
\ 'ale_open_list',
|
||||
\ 'ale_pattern_options',
|
||||
\ 'ale_pattern_options_enabled',
|
||||
\ 'ale_set_balloons',
|
||||
\ 'ale_set_highlights',
|
||||
\ 'ale_set_loclist',
|
||||
\ 'ale_set_quickfix',
|
||||
\ 'ale_set_signs',
|
||||
\ 'ale_sign_column_always',
|
||||
\ 'ale_sign_error',
|
||||
\ 'ale_sign_info',
|
||||
\ 'ale_sign_offset',
|
||||
\ 'ale_sign_style_error',
|
||||
\ 'ale_sign_style_warning',
|
||||
\ 'ale_sign_warning',
|
||||
\ 'ale_statusline_format',
|
||||
\ 'ale_type_map',
|
||||
\ 'ale_warn_about_trailing_blank_lines',
|
||||
\ 'ale_warn_about_trailing_whitespace',
|
||||
\]
|
||||
|
||||
function! s:Echo(message) abort
|
||||
execute 'echo a:message'
|
||||
endfunction
|
||||
|
||||
function! s:GetLinterVariables(filetype, linter_names) abort
|
||||
let l:variable_list = []
|
||||
let l:filetype_parts = split(a:filetype, '\.')
|
||||
|
||||
for l:key in keys(g:)
|
||||
" Extract variable names like: 'ale_python_flake8_executable'
|
||||
let l:match = matchlist(l:key, '\v^ale_([^_]+)_([^_]+)_.+$')
|
||||
|
||||
" Include matching variables.
|
||||
if !empty(l:match)
|
||||
\&& index(l:filetype_parts, l:match[1]) >= 0
|
||||
\&& index(a:linter_names, l:match[2]) >= 0
|
||||
call add(l:variable_list, l:key)
|
||||
endif
|
||||
endfor
|
||||
|
||||
call sort(l:variable_list)
|
||||
|
||||
return l:variable_list
|
||||
endfunction
|
||||
|
||||
function! s:EchoLinterVariables(variable_list) abort
|
||||
for l:key in a:variable_list
|
||||
call s:Echo('let g:' . l:key . ' = ' . string(g:[l:key]))
|
||||
|
||||
if has_key(b:, l:key)
|
||||
call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key]))
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:EchoGlobalVariables() abort
|
||||
for l:key in s:global_variable_list
|
||||
call s:Echo('let g:' . l:key . ' = ' . string(get(g:, l:key, v:null)))
|
||||
|
||||
if has_key(b:, l:key)
|
||||
call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key]))
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" Echo a command that was run.
|
||||
function! s:EchoCommand(item) abort
|
||||
let l:status_message = a:item.status
|
||||
|
||||
" Include the exit code in output if we have it.
|
||||
if a:item.status is# 'finished'
|
||||
let l:status_message .= ' - exit code ' . a:item.exit_code
|
||||
endif
|
||||
|
||||
call s:Echo('(' . l:status_message . ') ' . string(a:item.command))
|
||||
|
||||
if g:ale_history_log_output && has_key(a:item, 'output')
|
||||
if empty(a:item.output)
|
||||
call s:Echo('')
|
||||
call s:Echo('<<<NO OUTPUT RETURNED>>>')
|
||||
call s:Echo('')
|
||||
else
|
||||
call s:Echo('')
|
||||
call s:Echo('<<<OUTPUT STARTS>>>')
|
||||
|
||||
for l:line in a:item.output
|
||||
call s:Echo(l:line)
|
||||
endfor
|
||||
|
||||
call s:Echo('<<<OUTPUT ENDS>>>')
|
||||
call s:Echo('')
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Echo the results of an executable check.
|
||||
function! s:EchoExecutable(item) abort
|
||||
call s:Echo(printf(
|
||||
\ '(executable check - %s) %s',
|
||||
\ a:item.status ? 'success' : 'failure',
|
||||
\ a:item.command,
|
||||
\))
|
||||
endfunction
|
||||
|
||||
function! s:EchoCommandHistory() abort
|
||||
let l:buffer = bufnr('%')
|
||||
|
||||
for l:item in ale#history#Get(l:buffer)
|
||||
if l:item.job_id is# 'executable'
|
||||
call s:EchoExecutable(l:item)
|
||||
else
|
||||
call s:EchoCommand(l:item)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:EchoLinterAliases(all_linters) abort
|
||||
let l:first = 1
|
||||
|
||||
for l:linter in a:all_linters
|
||||
if !empty(l:linter.aliases)
|
||||
if l:first
|
||||
call s:Echo(' Linter Aliases:')
|
||||
endif
|
||||
|
||||
let l:first = 0
|
||||
|
||||
call s:Echo(string(l:linter.name) . ' -> ' . string(l:linter.aliases))
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! ale#debugging#Info() abort
|
||||
let l:filetype = &filetype
|
||||
|
||||
" We get the list of enabled linters for free by the above function.
|
||||
let l:enabled_linters = deepcopy(ale#linter#Get(l:filetype))
|
||||
|
||||
" But have to build the list of available linters ourselves.
|
||||
let l:all_linters = []
|
||||
let l:linter_variable_list = []
|
||||
|
||||
for l:part in split(l:filetype, '\.')
|
||||
let l:aliased_filetype = ale#linter#ResolveFiletype(l:part)
|
||||
call extend(l:all_linters, ale#linter#GetAll(l:aliased_filetype))
|
||||
endfor
|
||||
|
||||
let l:all_names = map(copy(l:all_linters), 'v:val[''name'']')
|
||||
let l:enabled_names = map(copy(l:enabled_linters), 'v:val[''name'']')
|
||||
|
||||
" Load linter variables to display
|
||||
" This must be done after linters are loaded.
|
||||
let l:variable_list = s:GetLinterVariables(l:filetype, l:enabled_names)
|
||||
|
||||
call s:Echo(' Current Filetype: ' . l:filetype)
|
||||
call s:Echo('Available Linters: ' . string(l:all_names))
|
||||
call s:EchoLinterAliases(l:all_linters)
|
||||
call s:Echo(' Enabled Linters: ' . string(l:enabled_names))
|
||||
call s:Echo(' Linter Variables:')
|
||||
call s:Echo('')
|
||||
call s:EchoLinterVariables(l:variable_list)
|
||||
call s:Echo(' Global Variables:')
|
||||
call s:Echo('')
|
||||
call s:EchoGlobalVariables()
|
||||
call s:Echo(' Command History:')
|
||||
call s:Echo('')
|
||||
call s:EchoCommandHistory()
|
||||
endfunction
|
||||
|
||||
function! ale#debugging#InfoToClipboard() abort
|
||||
redir @+>
|
||||
silent call ale#debugging#Info()
|
||||
redir END
|
||||
|
||||
call s:Echo('ALEInfo copied to your clipboard')
|
||||
endfunction
|
125
sources_non_forked/ale/autoload/ale/definition.vim
Normal file
125
sources_non_forked/ale/autoload/ale/definition.vim
Normal file
@ -0,0 +1,125 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Go to definition support for LSP linters.
|
||||
|
||||
let s:go_to_definition_map = {}
|
||||
|
||||
" Used to get the definition map in tests.
|
||||
function! ale#definition#GetMap() abort
|
||||
return deepcopy(s:go_to_definition_map)
|
||||
endfunction
|
||||
|
||||
" Used to set the definition map in tests.
|
||||
function! ale#definition#SetMap(map) abort
|
||||
let s:go_to_definition_map = a:map
|
||||
endfunction
|
||||
|
||||
" This function is used so we can check the execution of commands without
|
||||
" running them.
|
||||
function! ale#definition#Execute(expr) abort
|
||||
execute a:expr
|
||||
endfunction
|
||||
|
||||
function! ale#definition#ClearLSPData() abort
|
||||
let s:go_to_definition_map = {}
|
||||
endfunction
|
||||
|
||||
function! ale#definition#Open(options, filename, line, column) abort
|
||||
if a:options.open_in_tab
|
||||
call ale#definition#Execute('tabedit ' . fnameescape(a:filename))
|
||||
else
|
||||
call ale#definition#Execute('edit ' . fnameescape(a:filename))
|
||||
endif
|
||||
|
||||
call cursor(a:line, a:column)
|
||||
endfunction
|
||||
|
||||
function! ale#definition#HandleTSServerResponse(conn_id, response) abort
|
||||
if get(a:response, 'command', '') is# 'definition'
|
||||
\&& has_key(s:go_to_definition_map, a:response.request_seq)
|
||||
let l:options = remove(s:go_to_definition_map, a:response.request_seq)
|
||||
|
||||
if get(a:response, 'success', v:false) is v:true
|
||||
let l:filename = a:response.body[0].file
|
||||
let l:line = a:response.body[0].start.line
|
||||
let l:column = a:response.body[0].start.offset
|
||||
|
||||
call ale#definition#Open(l:options, l:filename, l:line, l:column)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#definition#HandleLSPResponse(conn_id, response) abort
|
||||
if has_key(a:response, 'id')
|
||||
\&& has_key(s:go_to_definition_map, a:response.id)
|
||||
let l:options = remove(s:go_to_definition_map, a:response.id)
|
||||
|
||||
" The result can be a Dictionary item, a List of the same, or null.
|
||||
let l:result = get(a:response, 'result', v:null)
|
||||
|
||||
if type(l:result) is type({})
|
||||
let l:result = [l:result]
|
||||
elseif type(l:result) isnot type([])
|
||||
let l:result = []
|
||||
endif
|
||||
|
||||
for l:item in l:result
|
||||
let l:filename = ale#path#FromURI(l:item.uri)
|
||||
let l:line = l:item.range.start.line + 1
|
||||
let l:column = l:item.range.start.character
|
||||
|
||||
call ale#definition#Open(l:options, l:filename, l:line, l:column)
|
||||
break
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:GoToLSPDefinition(linter, options) abort
|
||||
let l:buffer = bufnr('')
|
||||
let [l:line, l:column] = getcurpos()[1:2]
|
||||
|
||||
let l:Callback = a:linter.lsp is# 'tsserver'
|
||||
\ ? function('ale#definition#HandleTSServerResponse')
|
||||
\ : function('ale#definition#HandleLSPResponse')
|
||||
|
||||
let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback)
|
||||
|
||||
if empty(l:lsp_details)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:id = l:lsp_details.connection_id
|
||||
let l:root = l:lsp_details.project_root
|
||||
|
||||
if a:linter.lsp is# 'tsserver'
|
||||
let l:message = ale#lsp#tsserver_message#Definition(
|
||||
\ l:buffer,
|
||||
\ l:line,
|
||||
\ l:column
|
||||
\)
|
||||
else
|
||||
" Send a message saying the buffer has changed first, or the
|
||||
" definition position probably won't make sense.
|
||||
call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root)
|
||||
|
||||
let l:column = min([l:column, len(getline(l:line))])
|
||||
|
||||
" For LSP completions, we need to clamp the column to the length of
|
||||
" the line. python-language-server and perhaps others do not implement
|
||||
" this correctly.
|
||||
let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column)
|
||||
endif
|
||||
|
||||
let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
|
||||
|
||||
let s:go_to_definition_map[l:request_id] = {
|
||||
\ 'open_in_tab': get(a:options, 'open_in_tab', 0),
|
||||
\}
|
||||
endfunction
|
||||
|
||||
function! ale#definition#GoTo(options) abort
|
||||
for l:linter in ale#linter#Get(&filetype)
|
||||
if !empty(l:linter.lsp)
|
||||
call s:GoToLSPDefinition(l:linter, a:options)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
950
sources_non_forked/ale/autoload/ale/engine.vim
Normal file
950
sources_non_forked/ale/autoload/ale/engine.vim
Normal file
@ -0,0 +1,950 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Backend execution and job management
|
||||
" Executes linters in the background, using NeoVim or Vim 8 jobs
|
||||
|
||||
" Stores information for each job including:
|
||||
"
|
||||
" linter: The linter dictionary for the job.
|
||||
" buffer: The buffer number for the job.
|
||||
" output: The array of lines for the output of the job.
|
||||
if !has_key(s:, 'job_info_map')
|
||||
let s:job_info_map = {}
|
||||
endif
|
||||
|
||||
" Associates LSP connection IDs with linter names.
|
||||
if !has_key(s:, 'lsp_linter_map')
|
||||
let s:lsp_linter_map = {}
|
||||
endif
|
||||
|
||||
if !has_key(s:, 'executable_cache_map')
|
||||
let s:executable_cache_map = {}
|
||||
endif
|
||||
|
||||
function! ale#engine#ResetExecutableCache() abort
|
||||
let s:executable_cache_map = {}
|
||||
endfunction
|
||||
|
||||
" Check if files are executable, and if they are, remember that they are
|
||||
" for subsequent calls. We'll keep checking until programs can be executed.
|
||||
function! ale#engine#IsExecutable(buffer, executable) abort
|
||||
if empty(a:executable)
|
||||
" Don't log the executable check if the executable string is empty.
|
||||
return 0
|
||||
endif
|
||||
|
||||
" Check for a cached executable() check.
|
||||
let l:result = get(s:executable_cache_map, a:executable, v:null)
|
||||
|
||||
if l:result isnot v:null
|
||||
return l:result
|
||||
endif
|
||||
|
||||
" Check if the file is executable, and convert -1 to 1.
|
||||
let l:result = executable(a:executable) isnot 0
|
||||
|
||||
" Cache the executable check if we found it, or if the option to cache
|
||||
" failing checks is on.
|
||||
if l:result || g:ale_cache_executable_check_failures
|
||||
let s:executable_cache_map[a:executable] = l:result
|
||||
endif
|
||||
|
||||
if g:ale_history_enabled
|
||||
call ale#history#Add(a:buffer, l:result, 'executable', a:executable)
|
||||
endif
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! ale#engine#InitBufferInfo(buffer) abort
|
||||
if !has_key(g:ale_buffer_info, a:buffer)
|
||||
" job_list will hold the list of job IDs
|
||||
" active_linter_list will hold the list of active linter names
|
||||
" loclist holds the loclist items after all jobs have completed.
|
||||
" temporary_file_list holds temporary files to be cleaned up
|
||||
" temporary_directory_list holds temporary directories to be cleaned up
|
||||
let g:ale_buffer_info[a:buffer] = {
|
||||
\ 'job_list': [],
|
||||
\ 'active_linter_list': [],
|
||||
\ 'loclist': [],
|
||||
\ 'temporary_file_list': [],
|
||||
\ 'temporary_directory_list': [],
|
||||
\}
|
||||
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Clear LSP linter data for the linting engine.
|
||||
function! ale#engine#ClearLSPData() abort
|
||||
let s:lsp_linter_map = {}
|
||||
endfunction
|
||||
|
||||
" This function is documented and part of the public API.
|
||||
"
|
||||
" Return 1 if ALE is busy checking a given buffer
|
||||
function! ale#engine#IsCheckingBuffer(buffer) abort
|
||||
let l:info = get(g:ale_buffer_info, a:buffer, {})
|
||||
|
||||
return !empty(get(l:info, 'active_linter_list', []))
|
||||
endfunction
|
||||
|
||||
" Register a temporary file to be managed with the ALE engine for
|
||||
" a current job run.
|
||||
function! ale#engine#ManageFile(buffer, filename) abort
|
||||
call add(g:ale_buffer_info[a:buffer].temporary_file_list, a:filename)
|
||||
endfunction
|
||||
|
||||
" Same as the above, but manage an entire directory.
|
||||
function! ale#engine#ManageDirectory(buffer, directory) abort
|
||||
call add(g:ale_buffer_info[a:buffer].temporary_directory_list, a:directory)
|
||||
endfunction
|
||||
|
||||
" Create a new temporary directory and manage it in one go.
|
||||
function! ale#engine#CreateDirectory(buffer) abort
|
||||
let l:temporary_directory = tempname()
|
||||
" Create the temporary directory for the file, unreadable by 'other'
|
||||
" users.
|
||||
call mkdir(l:temporary_directory, '', 0750)
|
||||
call ale#engine#ManageDirectory(a:buffer, l:temporary_directory)
|
||||
|
||||
return l:temporary_directory
|
||||
endfunction
|
||||
|
||||
function! ale#engine#RemoveManagedFiles(buffer) abort
|
||||
let l:info = get(g:ale_buffer_info, a:buffer, {})
|
||||
|
||||
" We can't delete anything in a sandbox, so wait until we escape from
|
||||
" it to delete temporary files and directories.
|
||||
if ale#util#InSandbox()
|
||||
return
|
||||
endif
|
||||
|
||||
" Delete files with a call akin to a plan `rm` command.
|
||||
if has_key(l:info, 'temporary_file_list')
|
||||
for l:filename in l:info.temporary_file_list
|
||||
call delete(l:filename)
|
||||
endfor
|
||||
|
||||
let l:info.temporary_file_list = []
|
||||
endif
|
||||
|
||||
" Delete directories like `rm -rf`.
|
||||
" Directories are handled differently from files, so paths that are
|
||||
" intended to be single files can be set up for automatic deletion without
|
||||
" accidentally deleting entire directories.
|
||||
if has_key(l:info, 'temporary_directory_list')
|
||||
for l:directory in l:info.temporary_directory_list
|
||||
call delete(l:directory, 'rf')
|
||||
endfor
|
||||
|
||||
let l:info.temporary_directory_list = []
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:GatherOutput(job_id, line) abort
|
||||
if has_key(s:job_info_map, a:job_id)
|
||||
call add(s:job_info_map[a:job_id].output, a:line)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#engine#HandleLoclist(linter_name, buffer, loclist) abort
|
||||
let l:info = get(g:ale_buffer_info, a:buffer, {})
|
||||
|
||||
if empty(l:info)
|
||||
return
|
||||
endif
|
||||
|
||||
" Remove this linter from the list of active linters.
|
||||
" This may have already been done when the job exits.
|
||||
call filter(l:info.active_linter_list, 'v:val isnot# a:linter_name')
|
||||
|
||||
" Make some adjustments to the loclists to fix common problems, and also
|
||||
" to set default values for loclist items.
|
||||
let l:linter_loclist = ale#engine#FixLocList(a:buffer, a:linter_name, a:loclist)
|
||||
|
||||
" Remove previous items for this linter.
|
||||
call filter(l:info.loclist, 'v:val.linter_name isnot# a:linter_name')
|
||||
|
||||
" We don't need to add items or sort the list when this list is empty.
|
||||
if !empty(l:linter_loclist)
|
||||
" Add the new items.
|
||||
call extend(l:info.loclist, l:linter_loclist)
|
||||
|
||||
" Sort the loclist again.
|
||||
" We need a sorted list so we can run a binary search against it
|
||||
" for efficient lookup of the messages in the cursor handler.
|
||||
call sort(l:info.loclist, 'ale#util#LocItemCompare')
|
||||
endif
|
||||
|
||||
if ale#ShouldDoNothing(a:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
call ale#engine#SetResults(a:buffer, l:info.loclist)
|
||||
endfunction
|
||||
|
||||
function! s:HandleExit(job_id, exit_code) abort
|
||||
if !has_key(s:job_info_map, a:job_id)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:job_info = s:job_info_map[a:job_id]
|
||||
let l:linter = l:job_info.linter
|
||||
let l:output = l:job_info.output
|
||||
let l:buffer = l:job_info.buffer
|
||||
let l:next_chain_index = l:job_info.next_chain_index
|
||||
|
||||
if g:ale_history_enabled
|
||||
call ale#history#SetExitCode(l:buffer, a:job_id, a:exit_code)
|
||||
endif
|
||||
|
||||
" Remove this job from the list.
|
||||
call ale#job#Stop(a:job_id)
|
||||
call remove(s:job_info_map, a:job_id)
|
||||
call filter(g:ale_buffer_info[l:buffer].job_list, 'v:val isnot# a:job_id')
|
||||
call filter(g:ale_buffer_info[l:buffer].active_linter_list, 'v:val isnot# l:linter.name')
|
||||
|
||||
" Stop here if we land in the handle for a job completing if we're in
|
||||
" a sandbox.
|
||||
if ale#util#InSandbox()
|
||||
return
|
||||
endif
|
||||
|
||||
if has('nvim') && !empty(l:output) && empty(l:output[-1])
|
||||
call remove(l:output, -1)
|
||||
endif
|
||||
|
||||
if l:next_chain_index < len(get(l:linter, 'command_chain', []))
|
||||
call s:InvokeChain(l:buffer, l:linter, l:next_chain_index, l:output)
|
||||
return
|
||||
endif
|
||||
|
||||
" Log the output of the command for ALEInfo if we should.
|
||||
if g:ale_history_enabled && g:ale_history_log_output
|
||||
call ale#history#RememberOutput(l:buffer, a:job_id, l:output[:])
|
||||
endif
|
||||
|
||||
let l:loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output)
|
||||
|
||||
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist)
|
||||
endfunction
|
||||
|
||||
function! s:HandleLSPDiagnostics(conn_id, response) abort
|
||||
let l:linter_name = s:lsp_linter_map[a:conn_id]
|
||||
let l:filename = ale#path#FromURI(a:response.params.uri)
|
||||
let l:buffer = bufnr(l:filename)
|
||||
|
||||
if l:buffer <= 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
|
||||
|
||||
call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist)
|
||||
endfunction
|
||||
|
||||
function! s:HandleTSServerDiagnostics(response, error_type) abort
|
||||
let l:buffer = bufnr(a:response.body.file)
|
||||
let l:info = get(g:ale_buffer_info, l:buffer, {})
|
||||
|
||||
if empty(l:info)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:thislist = ale#lsp#response#ReadTSServerDiagnostics(a:response)
|
||||
|
||||
" tsserver sends syntax and semantic errors in separate messages, so we
|
||||
" have to collect the messages separately for each buffer and join them
|
||||
" back together again.
|
||||
if a:error_type is# 'syntax'
|
||||
let l:info.syntax_loclist = l:thislist
|
||||
else
|
||||
let l:info.semantic_loclist = l:thislist
|
||||
endif
|
||||
|
||||
let l:loclist = get(l:info, 'semantic_loclist', [])
|
||||
\ + get(l:info, 'syntax_loclist', [])
|
||||
|
||||
call ale#engine#HandleLoclist('tsserver', l:buffer, l:loclist)
|
||||
endfunction
|
||||
|
||||
function! s:HandleLSPErrorMessage(error_message) abort
|
||||
execute 'echoerr ''Error from LSP:'''
|
||||
|
||||
for l:line in split(a:error_message, "\n")
|
||||
execute 'echoerr l:line'
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! ale#engine#HandleLSPResponse(conn_id, response) abort
|
||||
let l:method = get(a:response, 'method', '')
|
||||
|
||||
if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
|
||||
" Uncomment this line to print LSP error messages.
|
||||
" call s:HandleLSPErrorMessage(a:response.error.message)
|
||||
elseif l:method is# 'textDocument/publishDiagnostics'
|
||||
call s:HandleLSPDiagnostics(a:conn_id, a:response)
|
||||
elseif get(a:response, 'type', '') is# 'event'
|
||||
\&& get(a:response, 'event', '') is# 'semanticDiag'
|
||||
call s:HandleTSServerDiagnostics(a:response, 'semantic')
|
||||
elseif get(a:response, 'type', '') is# 'event'
|
||||
\&& get(a:response, 'event', '') is# 'syntaxDiag'
|
||||
call s:HandleTSServerDiagnostics(a:response, 'syntax')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#engine#SetResults(buffer, loclist) abort
|
||||
let l:linting_is_done = !ale#engine#IsCheckingBuffer(a:buffer)
|
||||
|
||||
" Set signs first. This could potentially fix some line numbers.
|
||||
" The List could be sorted again here by SetSigns.
|
||||
if g:ale_set_signs
|
||||
call ale#sign#SetSigns(a:buffer, a:loclist)
|
||||
endif
|
||||
|
||||
if g:ale_set_quickfix || g:ale_set_loclist
|
||||
call ale#list#SetLists(a:buffer, a:loclist)
|
||||
endif
|
||||
|
||||
if exists('*ale#statusline#Update')
|
||||
" Don't load/run if not already loaded.
|
||||
call ale#statusline#Update(a:buffer, a:loclist)
|
||||
endif
|
||||
|
||||
if g:ale_set_highlights
|
||||
call ale#highlight#SetHighlights(a:buffer, a:loclist)
|
||||
endif
|
||||
|
||||
if l:linting_is_done
|
||||
if g:ale_echo_cursor
|
||||
" Try and echo the warning now.
|
||||
" This will only do something meaningful if we're in normal mode.
|
||||
call ale#cursor#EchoCursorWarning()
|
||||
endif
|
||||
|
||||
" Reset the save event marker, used for opening windows, etc.
|
||||
call setbufvar(a:buffer, 'ale_save_event_fired', 0)
|
||||
" Set a marker showing how many times a buffer has been checked.
|
||||
call setbufvar(
|
||||
\ a:buffer,
|
||||
\ 'ale_linted',
|
||||
\ getbufvar(a:buffer, 'ale_linted', 0) + 1
|
||||
\)
|
||||
|
||||
" Automatically remove all managed temporary files and directories
|
||||
" now that all jobs have completed.
|
||||
call ale#engine#RemoveManagedFiles(a:buffer)
|
||||
|
||||
" Call user autocommands. This allows users to hook into ALE's lint cycle.
|
||||
silent doautocmd <nomodeline> User ALELintPost
|
||||
" Old DEPRECATED name; call it for backwards compatibility.
|
||||
silent doautocmd <nomodeline> User ALELint
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RemapItemTypes(type_map, loclist) abort
|
||||
for l:item in a:loclist
|
||||
let l:key = l:item.type
|
||||
\ . (get(l:item, 'sub_type', '') is# 'style' ? 'S' : '')
|
||||
let l:new_key = get(a:type_map, l:key, '')
|
||||
|
||||
if l:new_key is# 'E'
|
||||
\|| l:new_key is# 'ES'
|
||||
\|| l:new_key is# 'W'
|
||||
\|| l:new_key is# 'WS'
|
||||
\|| l:new_key is# 'I'
|
||||
let l:item.type = l:new_key[0]
|
||||
|
||||
if l:new_key is# 'ES' || l:new_key is# 'WS'
|
||||
let l:item.sub_type = 'style'
|
||||
elseif has_key(l:item, 'sub_type')
|
||||
call remove(l:item, 'sub_type')
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! ale#engine#FixLocList(buffer, linter_name, loclist) abort
|
||||
let l:bufnr_map = {}
|
||||
let l:new_loclist = []
|
||||
|
||||
" Some errors have line numbers beyond the end of the file,
|
||||
" so we need to adjust them so they set the error at the last line
|
||||
" of the file instead.
|
||||
let l:last_line_number = ale#util#GetLineCount(a:buffer)
|
||||
|
||||
for l:old_item in a:loclist
|
||||
" Copy the loclist item with some default values and corrections.
|
||||
"
|
||||
" line and column numbers will be converted to numbers.
|
||||
" The buffer will default to the buffer being checked.
|
||||
" The vcol setting will default to 0, a byte index.
|
||||
" The error type will default to 'E' for errors.
|
||||
" The error number will default to -1.
|
||||
"
|
||||
" The line number and text are the only required keys.
|
||||
"
|
||||
" The linter_name will be set on the errors so it can be used in
|
||||
" output, filtering, etc..
|
||||
let l:item = {
|
||||
\ 'bufnr': a:buffer,
|
||||
\ 'text': l:old_item.text,
|
||||
\ 'lnum': str2nr(l:old_item.lnum),
|
||||
\ 'col': str2nr(get(l:old_item, 'col', 0)),
|
||||
\ 'vcol': get(l:old_item, 'vcol', 0),
|
||||
\ 'type': get(l:old_item, 'type', 'E'),
|
||||
\ 'nr': get(l:old_item, 'nr', -1),
|
||||
\ 'linter_name': a:linter_name,
|
||||
\}
|
||||
|
||||
if has_key(l:old_item, 'code')
|
||||
let l:item.code = l:old_item.code
|
||||
endif
|
||||
|
||||
if has_key(l:old_item, 'filename')
|
||||
\&& !ale#path#IsTempName(l:old_item.filename)
|
||||
" Use the filename given.
|
||||
" Temporary files are assumed to be for this buffer,
|
||||
" and the filename is not included then, because it looks bad
|
||||
" in the loclist window.
|
||||
let l:filename = l:old_item.filename
|
||||
let l:item.filename = l:filename
|
||||
|
||||
if has_key(l:old_item, 'bufnr')
|
||||
" If a buffer number is also given, include that too.
|
||||
" If Vim detects that he buffer number is valid, it will
|
||||
" be used instead of the filename.
|
||||
let l:item.bufnr = l:old_item.bufnr
|
||||
elseif has_key(l:bufnr_map, l:filename)
|
||||
" Get the buffer number from the map, which can be faster.
|
||||
let l:item.bufnr = l:bufnr_map[l:filename]
|
||||
else
|
||||
" Look up the buffer number.
|
||||
let l:item.bufnr = bufnr(l:filename)
|
||||
let l:bufnr_map[l:filename] = l:item.bufnr
|
||||
endif
|
||||
elseif has_key(l:old_item, 'bufnr')
|
||||
let l:item.bufnr = l:old_item.bufnr
|
||||
endif
|
||||
|
||||
if has_key(l:old_item, 'detail')
|
||||
let l:item.detail = l:old_item.detail
|
||||
endif
|
||||
|
||||
" Pass on a end_col key if set, used for highlights.
|
||||
if has_key(l:old_item, 'end_col')
|
||||
let l:item.end_col = str2nr(l:old_item.end_col)
|
||||
endif
|
||||
|
||||
if has_key(l:old_item, 'end_lnum')
|
||||
let l:item.end_lnum = str2nr(l:old_item.end_lnum)
|
||||
endif
|
||||
|
||||
if has_key(l:old_item, 'sub_type')
|
||||
let l:item.sub_type = l:old_item.sub_type
|
||||
endif
|
||||
|
||||
if l:item.lnum < 1
|
||||
" When errors appear before line 1, put them at line 1.
|
||||
let l:item.lnum = 1
|
||||
elseif l:item.bufnr == a:buffer && l:item.lnum > l:last_line_number
|
||||
" When errors go beyond the end of the file, put them at the end.
|
||||
" This is only done for the current buffer.
|
||||
let l:item.lnum = l:last_line_number
|
||||
endif
|
||||
|
||||
call add(l:new_loclist, l:item)
|
||||
endfor
|
||||
|
||||
let l:type_map = get(ale#Var(a:buffer, 'type_map'), a:linter_name, {})
|
||||
|
||||
if !empty(l:type_map)
|
||||
call s:RemapItemTypes(l:type_map, l:new_loclist)
|
||||
endif
|
||||
|
||||
return l:new_loclist
|
||||
endfunction
|
||||
|
||||
" Given part of a command, replace any % with %%, so that no characters in
|
||||
" the string will be replaced with filenames, etc.
|
||||
function! ale#engine#EscapeCommandPart(command_part) abort
|
||||
return substitute(a:command_part, '%', '%%', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:CreateTemporaryFileForJob(buffer, temporary_file) abort
|
||||
if empty(a:temporary_file)
|
||||
" There is no file, so we didn't create anything.
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:temporary_directory = fnamemodify(a:temporary_file, ':h')
|
||||
" Create the temporary directory for the file, unreadable by 'other'
|
||||
" users.
|
||||
call mkdir(l:temporary_directory, '', 0750)
|
||||
" Automatically delete the directory later.
|
||||
call ale#engine#ManageDirectory(a:buffer, l:temporary_directory)
|
||||
" Write the buffer out to a file.
|
||||
let l:lines = getbufline(a:buffer, 1, '$')
|
||||
call ale#util#Writefile(a:buffer, l:lines, a:temporary_file)
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
" Run a job.
|
||||
"
|
||||
" Returns 1 when the job was started successfully.
|
||||
function! s:RunJob(options) abort
|
||||
let l:command = a:options.command
|
||||
let l:buffer = a:options.buffer
|
||||
let l:linter = a:options.linter
|
||||
let l:output_stream = a:options.output_stream
|
||||
let l:next_chain_index = a:options.next_chain_index
|
||||
let l:read_buffer = a:options.read_buffer
|
||||
let l:info = g:ale_buffer_info[l:buffer]
|
||||
|
||||
if empty(l:command)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let [l:temporary_file, l:command] = ale#command#FormatCommand(l:buffer, l:command, l:read_buffer)
|
||||
|
||||
if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file)
|
||||
" If a temporary filename has been formatted in to the command, then
|
||||
" we do not need to send the Vim buffer to the command.
|
||||
let l:read_buffer = 0
|
||||
endif
|
||||
|
||||
" Add a newline to commands which need it.
|
||||
" This is only used for Flow for now, and is not documented.
|
||||
if l:linter.add_newline
|
||||
if has('win32')
|
||||
let l:command = l:command . '; echo.'
|
||||
else
|
||||
let l:command = l:command . '; echo'
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
|
||||
let l:job_options = {
|
||||
\ 'mode': 'nl',
|
||||
\ 'exit_cb': function('s:HandleExit'),
|
||||
\}
|
||||
|
||||
if l:output_stream is# 'stderr'
|
||||
let l:job_options.err_cb = function('s:GatherOutput')
|
||||
elseif l:output_stream is# 'both'
|
||||
let l:job_options.out_cb = function('s:GatherOutput')
|
||||
let l:job_options.err_cb = function('s:GatherOutput')
|
||||
else
|
||||
let l:job_options.out_cb = function('s:GatherOutput')
|
||||
endif
|
||||
|
||||
if get(g:, 'ale_run_synchronously') == 1
|
||||
" Find a unique Job value to use, which will be the same as the ID for
|
||||
" running commands synchronously. This is only for test code.
|
||||
let l:job_id = len(s:job_info_map) + 1
|
||||
|
||||
while has_key(s:job_info_map, l:job_id)
|
||||
let l:job_id += 1
|
||||
endwhile
|
||||
else
|
||||
let l:job_id = ale#job#Start(l:command, l:job_options)
|
||||
endif
|
||||
|
||||
let l:status = 'failed'
|
||||
|
||||
" Only proceed if the job is being run.
|
||||
if l:job_id
|
||||
" Add the job to the list of jobs, so we can track them.
|
||||
call add(l:info.job_list, l:job_id)
|
||||
|
||||
if index(l:info.active_linter_list, l:linter.name) < 0
|
||||
call add(l:info.active_linter_list, l:linter.name)
|
||||
endif
|
||||
|
||||
let l:status = 'started'
|
||||
" Store the ID for the job in the map to read back again.
|
||||
let s:job_info_map[l:job_id] = {
|
||||
\ 'linter': l:linter,
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'output': [],
|
||||
\ 'next_chain_index': l:next_chain_index,
|
||||
\}
|
||||
endif
|
||||
|
||||
if g:ale_history_enabled
|
||||
call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
|
||||
endif
|
||||
|
||||
if get(g:, 'ale_run_synchronously') == 1
|
||||
" Run a command synchronously if this test option is set.
|
||||
let s:job_info_map[l:job_id].output = systemlist(
|
||||
\ type(l:command) == type([])
|
||||
\ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
|
||||
\ : l:command
|
||||
\)
|
||||
|
||||
call l:job_options.exit_cb(l:job_id, v:shell_error)
|
||||
endif
|
||||
|
||||
return l:job_id != 0
|
||||
endfunction
|
||||
|
||||
" Determine which commands to run for a link in a command chain, or
|
||||
" just a regular command.
|
||||
function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
|
||||
let l:output_stream = get(a:linter, 'output_stream', 'stdout')
|
||||
let l:read_buffer = a:linter.read_buffer
|
||||
let l:chain_index = a:chain_index
|
||||
let l:input = a:input
|
||||
|
||||
if has_key(a:linter, 'command_chain')
|
||||
while l:chain_index < len(a:linter.command_chain)
|
||||
" Run a chain of commands, one asynchronous command after the other,
|
||||
" so that many programs can be run in a sequence.
|
||||
let l:chain_item = a:linter.command_chain[l:chain_index]
|
||||
|
||||
if l:chain_index == 0
|
||||
" The first callback in the chain takes only a buffer number.
|
||||
let l:command = ale#util#GetFunction(l:chain_item.callback)(
|
||||
\ a:buffer
|
||||
\)
|
||||
else
|
||||
" The second callback in the chain takes some input too.
|
||||
let l:command = ale#util#GetFunction(l:chain_item.callback)(
|
||||
\ a:buffer,
|
||||
\ l:input
|
||||
\)
|
||||
endif
|
||||
|
||||
if !empty(l:command)
|
||||
" We hit a command to run, so we'll execute that
|
||||
|
||||
" The chain item can override the output_stream option.
|
||||
if has_key(l:chain_item, 'output_stream')
|
||||
let l:output_stream = l:chain_item.output_stream
|
||||
endif
|
||||
|
||||
" The chain item can override the read_buffer option.
|
||||
if has_key(l:chain_item, 'read_buffer')
|
||||
let l:read_buffer = l:chain_item.read_buffer
|
||||
elseif l:chain_index != len(a:linter.command_chain) - 1
|
||||
" Don't read the buffer for commands besides the last one
|
||||
" in the chain by default.
|
||||
let l:read_buffer = 0
|
||||
endif
|
||||
|
||||
break
|
||||
endif
|
||||
|
||||
" Command chain items can return an empty string to indicate that
|
||||
" a command should be skipped, so we should try the next item
|
||||
" with no input.
|
||||
let l:input = []
|
||||
let l:chain_index += 1
|
||||
endwhile
|
||||
else
|
||||
let l:command = ale#linter#GetCommand(a:buffer, a:linter)
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': l:command,
|
||||
\ 'buffer': a:buffer,
|
||||
\ 'linter': a:linter,
|
||||
\ 'output_stream': l:output_stream,
|
||||
\ 'next_chain_index': l:chain_index + 1,
|
||||
\ 'read_buffer': l:read_buffer,
|
||||
\}
|
||||
endfunction
|
||||
|
||||
function! s:InvokeChain(buffer, linter, chain_index, input) abort
|
||||
let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input)
|
||||
|
||||
return s:RunJob(l:options)
|
||||
endfunction
|
||||
|
||||
function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
|
||||
let l:info = get(g:ale_buffer_info, a:buffer, {})
|
||||
let l:new_job_list = []
|
||||
let l:new_active_linter_list = []
|
||||
|
||||
for l:job_id in get(l:info, 'job_list', [])
|
||||
let l:job_info = get(s:job_info_map, l:job_id, {})
|
||||
|
||||
if !empty(l:job_info)
|
||||
if a:include_lint_file_jobs || !l:job_info.linter.lint_file
|
||||
call ale#job#Stop(l:job_id)
|
||||
call remove(s:job_info_map, l:job_id)
|
||||
else
|
||||
call add(l:new_job_list, l:job_id)
|
||||
" Linters with jobs still running are still active.
|
||||
call add(l:new_active_linter_list, l:job_info.linter.name)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Remove duplicates from the active linter list.
|
||||
call uniq(sort(l:new_active_linter_list))
|
||||
|
||||
" Update the List, so it includes only the jobs we still need.
|
||||
let l:info.job_list = l:new_job_list
|
||||
" Update the active linter list, clearing out anything not running.
|
||||
let l:info.active_linter_list = l:new_active_linter_list
|
||||
endfunction
|
||||
|
||||
function! s:CheckWithLSP(buffer, linter) abort
|
||||
let l:info = g:ale_buffer_info[a:buffer]
|
||||
let l:lsp_details = ale#linter#StartLSP(
|
||||
\ a:buffer,
|
||||
\ a:linter,
|
||||
\ function('ale#engine#HandleLSPResponse'),
|
||||
\)
|
||||
|
||||
if empty(l:lsp_details)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:id = l:lsp_details.connection_id
|
||||
let l:root = l:lsp_details.project_root
|
||||
|
||||
" Remember the linter this connection is for.
|
||||
let s:lsp_linter_map[l:id] = a:linter.name
|
||||
|
||||
let l:change_message = a:linter.lsp is# 'tsserver'
|
||||
\ ? ale#lsp#tsserver_message#Geterr(a:buffer)
|
||||
\ : ale#lsp#message#DidChange(a:buffer)
|
||||
let l:request_id = ale#lsp#Send(l:id, l:change_message, l:root)
|
||||
|
||||
" If this was a file save event, also notify the server of that.
|
||||
if a:linter.lsp isnot# 'tsserver'
|
||||
\&& getbufvar(a:buffer, 'ale_save_event_fired', 0)
|
||||
let l:save_message = ale#lsp#message#DidSave(a:buffer)
|
||||
let l:request_id = ale#lsp#Send(l:id, l:save_message, l:root)
|
||||
endif
|
||||
|
||||
if l:request_id != 0
|
||||
if index(l:info.active_linter_list, a:linter.name) < 0
|
||||
call add(l:info.active_linter_list, a:linter.name)
|
||||
endif
|
||||
endif
|
||||
|
||||
return l:request_id != 0
|
||||
endfunction
|
||||
|
||||
function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
|
||||
" Figure out which linters are still enabled, and remove
|
||||
" problems for linters which are no longer enabled.
|
||||
let l:name_map = {}
|
||||
|
||||
for l:linter in a:linters
|
||||
let l:name_map[l:linter.name] = 1
|
||||
endfor
|
||||
|
||||
call filter(
|
||||
\ get(g:ale_buffer_info[a:buffer], 'loclist', []),
|
||||
\ 'get(l:name_map, get(v:val, ''linter_name''))',
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! s:AddProblemsFromOtherBuffers(buffer, linters) abort
|
||||
let l:filename = expand('#' . a:buffer . ':p')
|
||||
let l:loclist = []
|
||||
let l:name_map = {}
|
||||
|
||||
" Build a map of the active linters.
|
||||
for l:linter in a:linters
|
||||
let l:name_map[l:linter.name] = 1
|
||||
endfor
|
||||
|
||||
" Find the items from other buffers, for the linters that are enabled.
|
||||
for l:info in values(g:ale_buffer_info)
|
||||
for l:item in l:info.loclist
|
||||
if has_key(l:item, 'filename')
|
||||
\&& l:item.filename is# l:filename
|
||||
\&& has_key(l:name_map, l:item.linter_name)
|
||||
" Copy the items and set the buffer numbers to this one.
|
||||
let l:new_item = copy(l:item)
|
||||
let l:new_item.bufnr = a:buffer
|
||||
call add(l:loclist, l:new_item)
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
if !empty(l:loclist)
|
||||
call sort(l:loclist, function('ale#util#LocItemCompareWithText'))
|
||||
call uniq(l:loclist, function('ale#util#LocItemCompareWithText'))
|
||||
|
||||
" Set the loclist variable, used by some parts of ALE.
|
||||
let g:ale_buffer_info[a:buffer].loclist = l:loclist
|
||||
call ale#engine#SetResults(a:buffer, l:loclist)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Run a linter for a buffer.
|
||||
"
|
||||
" Returns 1 if the linter was successfully run.
|
||||
function! s:RunLinter(buffer, linter) abort
|
||||
if !empty(a:linter.lsp)
|
||||
return s:CheckWithLSP(a:buffer, a:linter)
|
||||
else
|
||||
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
|
||||
|
||||
if ale#engine#IsExecutable(a:buffer, l:executable)
|
||||
return s:InvokeChain(a:buffer, a:linter, 0, [])
|
||||
endif
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
|
||||
" Initialise the buffer information if needed.
|
||||
let l:new_buffer = ale#engine#InitBufferInfo(a:buffer)
|
||||
call s:StopCurrentJobs(a:buffer, a:should_lint_file)
|
||||
call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters)
|
||||
|
||||
" We can only clear the results if we aren't checking the buffer.
|
||||
let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer)
|
||||
|
||||
silent doautocmd <nomodeline> User ALELintPre
|
||||
|
||||
for l:linter in a:linters
|
||||
" Only run lint_file linters if we should.
|
||||
if !l:linter.lint_file || a:should_lint_file
|
||||
if s:RunLinter(a:buffer, l:linter)
|
||||
" If a single linter ran, we shouldn't clear everything.
|
||||
let l:can_clear_results = 0
|
||||
endif
|
||||
else
|
||||
" If we skipped running a lint_file linter still in the list,
|
||||
" we shouldn't clear everything.
|
||||
let l:can_clear_results = 0
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Clear the results if we can. This needs to be done when linters are
|
||||
" disabled, or ALE itself is disabled.
|
||||
if l:can_clear_results
|
||||
call ale#engine#SetResults(a:buffer, [])
|
||||
elseif l:new_buffer
|
||||
call s:AddProblemsFromOtherBuffers(a:buffer, a:linters)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Clean up a buffer.
|
||||
"
|
||||
" This function will stop all current jobs for the buffer,
|
||||
" clear the state of everything, and remove the Dictionary for managing
|
||||
" the buffer.
|
||||
function! ale#engine#Cleanup(buffer) abort
|
||||
" Don't bother with cleanup code when newer NeoVim versions are exiting.
|
||||
if get(v:, 'exiting', v:null) isnot v:null
|
||||
return
|
||||
endif
|
||||
|
||||
if !has_key(g:ale_buffer_info, a:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
call ale#engine#RunLinters(a:buffer, [], 1)
|
||||
|
||||
call remove(g:ale_buffer_info, a:buffer)
|
||||
endfunction
|
||||
|
||||
" Given a buffer number, return the warnings and errors for a given buffer.
|
||||
function! ale#engine#GetLoclist(buffer) abort
|
||||
if !has_key(g:ale_buffer_info, a:buffer)
|
||||
return []
|
||||
endif
|
||||
|
||||
return g:ale_buffer_info[a:buffer].loclist
|
||||
endfunction
|
||||
|
||||
" This function can be called with a timeout to wait for all jobs to finish.
|
||||
" If the jobs to not finish in the given number of milliseconds,
|
||||
" an exception will be thrown.
|
||||
"
|
||||
" The time taken will be a very rough approximation, and more time may be
|
||||
" permitted than is specified.
|
||||
function! ale#engine#WaitForJobs(deadline) abort
|
||||
let l:start_time = ale#util#ClockMilliseconds()
|
||||
|
||||
if l:start_time == 0
|
||||
throw 'Failed to read milliseconds from the clock!'
|
||||
endif
|
||||
|
||||
let l:job_list = []
|
||||
|
||||
" Gather all of the jobs from every buffer.
|
||||
for l:info in values(g:ale_buffer_info)
|
||||
call extend(l:job_list, get(l:info, 'job_list', []))
|
||||
endfor
|
||||
|
||||
" NeoVim has a built-in API for this, so use that.
|
||||
if has('nvim')
|
||||
let l:nvim_code_list = jobwait(l:job_list, a:deadline)
|
||||
|
||||
if index(l:nvim_code_list, -1) >= 0
|
||||
throw 'Jobs did not complete on time!'
|
||||
endif
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
let l:should_wait_more = 1
|
||||
|
||||
while l:should_wait_more
|
||||
let l:should_wait_more = 0
|
||||
|
||||
for l:job_id in l:job_list
|
||||
if ale#job#IsRunning(l:job_id)
|
||||
let l:now = ale#util#ClockMilliseconds()
|
||||
|
||||
if l:now - l:start_time > a:deadline
|
||||
" Stop waiting after a timeout, so we don't wait forever.
|
||||
throw 'Jobs did not complete on time!'
|
||||
endif
|
||||
|
||||
" Wait another 10 milliseconds
|
||||
let l:should_wait_more = 1
|
||||
sleep 10ms
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endwhile
|
||||
|
||||
" Sleep for a small amount of time after all jobs finish.
|
||||
" This seems to be enough to let handlers after jobs end run, and
|
||||
" prevents the occasional failure where this function exits after jobs
|
||||
" end, but before handlers are run.
|
||||
sleep 10ms
|
||||
|
||||
" We must check the buffer data again to see if new jobs started
|
||||
" for command_chain linters.
|
||||
let l:has_new_jobs = 0
|
||||
|
||||
" Check again to see if any jobs are running.
|
||||
for l:info in values(g:ale_buffer_info)
|
||||
for l:job_id in get(l:info, 'job_list', [])
|
||||
if ale#job#IsRunning(l:job_id)
|
||||
let l:has_new_jobs = 1
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
if l:has_new_jobs
|
||||
" We have to wait more. Offset the timeout by the time taken so far.
|
||||
let l:now = ale#util#ClockMilliseconds()
|
||||
let l:new_deadline = a:deadline - (l:now - l:start_time)
|
||||
|
||||
if l:new_deadline <= 0
|
||||
" Enough time passed already, so stop immediately.
|
||||
throw 'Jobs did not complete on time!'
|
||||
endif
|
||||
|
||||
call ale#engine#WaitForJobs(l:new_deadline)
|
||||
endif
|
||||
endfunction
|
69
sources_non_forked/ale/autoload/ale/events.vim
Normal file
69
sources_non_forked/ale/autoload/ale/events.vim
Normal file
@ -0,0 +1,69 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
|
||||
function! ale#events#QuitEvent(buffer) abort
|
||||
" Remember when ALE is quitting for BufWrite, etc.
|
||||
call setbufvar(a:buffer, 'ale_quitting', ale#util#ClockMilliseconds())
|
||||
endfunction
|
||||
|
||||
function! ale#events#QuitRecently(buffer) abort
|
||||
let l:time = getbufvar(a:buffer, 'ale_quitting', 0)
|
||||
|
||||
return l:time && ale#util#ClockMilliseconds() - l:time < 1000
|
||||
endfunction
|
||||
|
||||
function! ale#events#SaveEvent(buffer) abort
|
||||
let l:should_lint = ale#Var(a:buffer, 'enabled') && g:ale_lint_on_save
|
||||
|
||||
if l:should_lint
|
||||
call setbufvar(a:buffer, 'ale_save_event_fired', 1)
|
||||
endif
|
||||
|
||||
if ale#Var(a:buffer, 'fix_on_save')
|
||||
let l:will_fix = ale#fix#Fix('save_file')
|
||||
let l:should_lint = l:should_lint && !l:will_fix
|
||||
endif
|
||||
|
||||
if l:should_lint && !ale#events#QuitRecently(a:buffer)
|
||||
call ale#Queue(0, 'lint_file', a:buffer)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:LintOnEnter(buffer) abort
|
||||
if ale#Var(a:buffer, 'enabled')
|
||||
\&& g:ale_lint_on_enter
|
||||
\&& has_key(b:, 'ale_file_changed')
|
||||
call remove(b:, 'ale_file_changed')
|
||||
call ale#Queue(0, 'lint_file', a:buffer)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#events#EnterEvent(buffer) abort
|
||||
" When entering a buffer, we are no longer quitting it.
|
||||
call setbufvar(a:buffer, 'ale_quitting', 0)
|
||||
let l:filetype = getbufvar(a:buffer, '&filetype')
|
||||
call setbufvar(a:buffer, 'ale_original_filetype', l:filetype)
|
||||
|
||||
call s:LintOnEnter(a:buffer)
|
||||
endfunction
|
||||
|
||||
function! ale#events#FileTypeEvent(buffer, new_filetype) abort
|
||||
let l:filetype = getbufvar(a:buffer, 'ale_original_filetype', '')
|
||||
|
||||
" If we're setting the filetype for the first time after it was blank,
|
||||
" and the option for linting on enter is off, then we should set this
|
||||
" filetype as the original filetype. Otherwise ALE will still appear to
|
||||
" lint files because of the BufEnter event, etc.
|
||||
if empty(l:filetype) && !ale#Var(a:buffer, 'lint_on_enter')
|
||||
call setbufvar(a:buffer, 'ale_original_filetype', a:new_filetype)
|
||||
elseif a:new_filetype isnot# l:filetype
|
||||
call ale#Queue(300, 'lint_file', a:buffer)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#events#FileChangedEvent(buffer) abort
|
||||
call setbufvar(a:buffer, 'ale_file_changed', 1)
|
||||
|
||||
if bufnr('') == a:buffer
|
||||
call s:LintOnEnter(a:buffer)
|
||||
endif
|
||||
endfunction
|
60
sources_non_forked/ale/autoload/ale/filetypes.vim
Normal file
60
sources_non_forked/ale/autoload/ale/filetypes.vim
Normal file
@ -0,0 +1,60 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: This file handles guessing file extensions for filetypes, etc.
|
||||
|
||||
function! ale#filetypes#LoadExtensionMap() abort
|
||||
" Output includes:
|
||||
" '*.erl setf erlang'
|
||||
redir => l:output
|
||||
silent exec 'autocmd'
|
||||
redir end
|
||||
|
||||
let l:map = {}
|
||||
|
||||
for l:line in split(l:output, "\n")
|
||||
" Parse filetypes, like so:
|
||||
"
|
||||
" *.erl setf erlang
|
||||
" *.md set filetype=markdown
|
||||
" *.snippet setlocal filetype=snippets
|
||||
let l:match = matchlist(l:line, '\v^ *\*(\.[^ ]+).*set(f *| *filetype=|local *filetype=)([^ ]+)')
|
||||
|
||||
if !empty(l:match)
|
||||
let l:map[substitute(l:match[3], '^=', '', '')] = l:match[1]
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:map
|
||||
endfunction
|
||||
|
||||
let s:cached_map = {}
|
||||
|
||||
function! s:GetCachedExtensionMap() abort
|
||||
if empty(s:cached_map)
|
||||
let s:cached_map = ale#filetypes#LoadExtensionMap()
|
||||
endif
|
||||
|
||||
return s:cached_map
|
||||
endfunction
|
||||
|
||||
function! ale#filetypes#GuessExtension(filetype) abort
|
||||
let l:map = s:GetCachedExtensionMap()
|
||||
let l:ext = get(l:map, a:filetype, '')
|
||||
|
||||
" If we have an exact match, like something for javascript.jsx, use that.
|
||||
if !empty(l:ext)
|
||||
return l:ext
|
||||
endif
|
||||
|
||||
" If we don't have an exact match, use the first filetype in the compound
|
||||
" filetype.
|
||||
for l:part in split(a:filetype, '\.')
|
||||
let l:ext = get(l:map, l:part, '')
|
||||
|
||||
if !empty(l:ext)
|
||||
return l:ext
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Return an empty string if we don't find anything.
|
||||
return ''
|
||||
endfunction
|
484
sources_non_forked/ale/autoload/ale/fix.vim
Normal file
484
sources_non_forked/ale/autoload/ale/fix.vim
Normal file
@ -0,0 +1,484 @@
|
||||
" This global Dictionary tracks the ALE fix data for jobs, etc.
|
||||
" This Dictionary should not be accessed outside of the plugin. It is only
|
||||
" global so it can be modified in Vader tests.
|
||||
if !has_key(g:, 'ale_fix_buffer_data')
|
||||
let g:ale_fix_buffer_data = {}
|
||||
endif
|
||||
|
||||
if !has_key(s:, 'job_info_map')
|
||||
let s:job_info_map = {}
|
||||
endif
|
||||
|
||||
function! s:GatherOutput(job_id, line) abort
|
||||
if has_key(s:job_info_map, a:job_id)
|
||||
call add(s:job_info_map[a:job_id].output, a:line)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Apply fixes queued up for buffers which may be hidden.
|
||||
" Vim doesn't let you modify hidden buffers.
|
||||
function! ale#fix#ApplyQueuedFixes() abort
|
||||
let l:buffer = bufnr('')
|
||||
let l:data = get(g:ale_fix_buffer_data, l:buffer, {'done': 0})
|
||||
|
||||
if !l:data.done
|
||||
return
|
||||
endif
|
||||
|
||||
call remove(g:ale_fix_buffer_data, l:buffer)
|
||||
|
||||
if l:data.changes_made
|
||||
call setline(1, l:data.output)
|
||||
|
||||
let l:start_line = len(l:data.output) + 1
|
||||
let l:end_line = len(l:data.lines_before)
|
||||
|
||||
if l:end_line >= l:start_line
|
||||
let l:save = winsaveview()
|
||||
silent execute l:start_line . ',' . l:end_line . 'd_'
|
||||
call winrestview(l:save)
|
||||
endif
|
||||
|
||||
if l:data.should_save
|
||||
if empty(&buftype)
|
||||
noautocmd :w!
|
||||
else
|
||||
set nomodified
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:data.should_save
|
||||
let l:should_lint = g:ale_fix_on_save
|
||||
else
|
||||
let l:should_lint = l:data.changes_made
|
||||
endif
|
||||
|
||||
silent doautocmd <nomodeline> User ALEFixPost
|
||||
|
||||
" If ALE linting is enabled, check for problems with the file again after
|
||||
" fixing problems.
|
||||
if g:ale_enabled
|
||||
\&& l:should_lint
|
||||
\&& !ale#events#QuitRecently(l:buffer)
|
||||
call ale#Queue(0, l:data.should_save ? 'lint_file' : '')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#fix#ApplyFixes(buffer, output) abort
|
||||
call ale#fix#RemoveManagedFiles(a:buffer)
|
||||
|
||||
let l:data = g:ale_fix_buffer_data[a:buffer]
|
||||
let l:data.output = a:output
|
||||
let l:data.changes_made = l:data.lines_before != l:data.output
|
||||
|
||||
if l:data.changes_made && bufexists(a:buffer)
|
||||
let l:lines = getbufline(a:buffer, 1, '$')
|
||||
|
||||
if l:data.lines_before != l:lines
|
||||
call remove(g:ale_fix_buffer_data, a:buffer)
|
||||
execute 'echoerr ''The file was changed before fixing finished'''
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
if !bufexists(a:buffer)
|
||||
" Remove the buffer data when it doesn't exist.
|
||||
call remove(g:ale_fix_buffer_data, a:buffer)
|
||||
endif
|
||||
|
||||
let l:data.done = 1
|
||||
|
||||
" We can only change the lines of a buffer which is currently open,
|
||||
" so try and apply the fixes to the current buffer.
|
||||
call ale#fix#ApplyQueuedFixes()
|
||||
endfunction
|
||||
|
||||
function! s:HandleExit(job_id, exit_code) abort
|
||||
if !has_key(s:job_info_map, a:job_id)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:job_info = remove(s:job_info_map, a:job_id)
|
||||
let l:buffer = l:job_info.buffer
|
||||
|
||||
if g:ale_history_enabled
|
||||
call ale#history#SetExitCode(l:buffer, a:job_id, a:exit_code)
|
||||
endif
|
||||
|
||||
if has_key(l:job_info, 'file_to_read')
|
||||
let l:job_info.output = readfile(l:job_info.file_to_read)
|
||||
endif
|
||||
|
||||
let l:ChainCallback = get(l:job_info, 'chain_with', v:null)
|
||||
let l:ProcessWith = get(l:job_info, 'process_with', v:null)
|
||||
|
||||
" Post-process the output with a function if we have one.
|
||||
if l:ProcessWith isnot v:null
|
||||
let l:job_info.output = call(
|
||||
\ ale#util#GetFunction(l:ProcessWith),
|
||||
\ [l:buffer, l:job_info.output]
|
||||
\)
|
||||
endif
|
||||
|
||||
" Use the output of the job for changing the file if it isn't empty,
|
||||
" otherwise skip this job and use the input from before.
|
||||
"
|
||||
" We'll use the input from before for chained commands.
|
||||
if l:ChainCallback is v:null && !empty(split(join(l:job_info.output)))
|
||||
let l:input = l:job_info.output
|
||||
else
|
||||
let l:input = l:job_info.input
|
||||
endif
|
||||
|
||||
let l:next_index = l:ChainCallback is v:null
|
||||
\ ? l:job_info.callback_index + 1
|
||||
\ : l:job_info.callback_index
|
||||
|
||||
call s:RunFixer({
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'input': l:input,
|
||||
\ 'output': l:job_info.output,
|
||||
\ 'callback_list': l:job_info.callback_list,
|
||||
\ 'callback_index': l:next_index,
|
||||
\ 'chain_callback': l:ChainCallback,
|
||||
\})
|
||||
endfunction
|
||||
|
||||
function! ale#fix#ManageDirectory(buffer, directory) abort
|
||||
call add(g:ale_fix_buffer_data[a:buffer].temporary_directory_list, a:directory)
|
||||
endfunction
|
||||
|
||||
function! ale#fix#RemoveManagedFiles(buffer) abort
|
||||
if !has_key(g:ale_fix_buffer_data, a:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
" We can't delete anything in a sandbox, so wait until we escape from
|
||||
" it to delete temporary files and directories.
|
||||
if ale#util#InSandbox()
|
||||
return
|
||||
endif
|
||||
|
||||
" Delete directories like `rm -rf`.
|
||||
" Directories are handled differently from files, so paths that are
|
||||
" intended to be single files can be set up for automatic deletion without
|
||||
" accidentally deleting entire directories.
|
||||
for l:directory in g:ale_fix_buffer_data[a:buffer].temporary_directory_list
|
||||
call delete(l:directory, 'rf')
|
||||
endfor
|
||||
|
||||
let g:ale_fix_buffer_data[a:buffer].temporary_directory_list = []
|
||||
endfunction
|
||||
|
||||
function! s:CreateTemporaryFileForJob(buffer, temporary_file, input) abort
|
||||
if empty(a:temporary_file)
|
||||
" There is no file, so we didn't create anything.
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:temporary_directory = fnamemodify(a:temporary_file, ':h')
|
||||
" Create the temporary directory for the file, unreadable by 'other'
|
||||
" users.
|
||||
call mkdir(l:temporary_directory, '', 0750)
|
||||
" Automatically delete the directory later.
|
||||
call ale#fix#ManageDirectory(a:buffer, l:temporary_directory)
|
||||
" Write the buffer out to a file.
|
||||
call ale#util#Writefile(a:buffer, a:input, a:temporary_file)
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! s:RunJob(options) abort
|
||||
let l:buffer = a:options.buffer
|
||||
let l:command = a:options.command
|
||||
let l:input = a:options.input
|
||||
let l:output_stream = a:options.output_stream
|
||||
let l:read_temporary_file = a:options.read_temporary_file
|
||||
let l:ChainWith = a:options.chain_with
|
||||
let l:read_buffer = a:options.read_buffer
|
||||
|
||||
if empty(l:command)
|
||||
" If there's nothing further to chain the command with, stop here.
|
||||
if l:ChainWith is v:null
|
||||
return 0
|
||||
endif
|
||||
|
||||
" If there's another chained callback to run, then run that.
|
||||
call s:RunFixer({
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'input': l:input,
|
||||
\ 'callback_index': a:options.callback_index,
|
||||
\ 'callback_list': a:options.callback_list,
|
||||
\ 'chain_callback': l:ChainWith,
|
||||
\ 'output': [],
|
||||
\})
|
||||
|
||||
return 1
|
||||
endif
|
||||
|
||||
let [l:temporary_file, l:command] = ale#command#FormatCommand(
|
||||
\ l:buffer,
|
||||
\ l:command,
|
||||
\ l:read_buffer,
|
||||
\)
|
||||
call s:CreateTemporaryFileForJob(l:buffer, l:temporary_file, l:input)
|
||||
|
||||
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
|
||||
let l:job_options = {
|
||||
\ 'mode': 'nl',
|
||||
\ 'exit_cb': function('s:HandleExit'),
|
||||
\}
|
||||
|
||||
let l:job_info = {
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'input': l:input,
|
||||
\ 'output': [],
|
||||
\ 'chain_with': l:ChainWith,
|
||||
\ 'callback_index': a:options.callback_index,
|
||||
\ 'callback_list': a:options.callback_list,
|
||||
\ 'process_with': a:options.process_with,
|
||||
\}
|
||||
|
||||
if l:read_temporary_file
|
||||
" TODO: Check that a temporary file is set here.
|
||||
let l:job_info.file_to_read = l:temporary_file
|
||||
elseif l:output_stream is# 'stderr'
|
||||
let l:job_options.err_cb = function('s:GatherOutput')
|
||||
elseif l:output_stream is# 'both'
|
||||
let l:job_options.out_cb = function('s:GatherOutput')
|
||||
let l:job_options.err_cb = function('s:GatherOutput')
|
||||
else
|
||||
let l:job_options.out_cb = function('s:GatherOutput')
|
||||
endif
|
||||
|
||||
if get(g:, 'ale_emulate_job_failure') == 1
|
||||
let l:job_id = 0
|
||||
elseif get(g:, 'ale_run_synchronously') == 1
|
||||
" Find a unique Job value to use, which will be the same as the ID for
|
||||
" running commands synchronously. This is only for test code.
|
||||
let l:job_id = len(s:job_info_map) + 1
|
||||
|
||||
while has_key(s:job_info_map, l:job_id)
|
||||
let l:job_id += 1
|
||||
endwhile
|
||||
else
|
||||
let l:job_id = ale#job#Start(l:command, l:job_options)
|
||||
endif
|
||||
|
||||
let l:status = l:job_id ? 'started' : 'failed'
|
||||
|
||||
if g:ale_history_enabled
|
||||
call ale#history#Add(l:buffer, l:status, l:job_id, l:command)
|
||||
endif
|
||||
|
||||
if l:job_id == 0
|
||||
return 0
|
||||
endif
|
||||
|
||||
let s:job_info_map[l:job_id] = l:job_info
|
||||
|
||||
if get(g:, 'ale_run_synchronously') == 1
|
||||
" Run a command synchronously if this test option is set.
|
||||
let l:output = systemlist(
|
||||
\ type(l:command) == type([])
|
||||
\ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
|
||||
\ : l:command
|
||||
\)
|
||||
|
||||
if !l:read_temporary_file
|
||||
let s:job_info_map[l:job_id].output = l:output
|
||||
endif
|
||||
|
||||
call l:job_options.exit_cb(l:job_id, v:shell_error)
|
||||
endif
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! s:RunFixer(options) abort
|
||||
let l:buffer = a:options.buffer
|
||||
let l:input = a:options.input
|
||||
let l:index = a:options.callback_index
|
||||
let l:ChainCallback = get(a:options, 'chain_callback', v:null)
|
||||
|
||||
while len(a:options.callback_list) > l:index
|
||||
let l:Function = l:ChainCallback isnot v:null
|
||||
\ ? ale#util#GetFunction(l:ChainCallback)
|
||||
\ : a:options.callback_list[l:index]
|
||||
|
||||
if l:ChainCallback isnot v:null
|
||||
" Chained commands accept (buffer, output, [input])
|
||||
let l:result = ale#util#FunctionArgCount(l:Function) == 2
|
||||
\ ? call(l:Function, [l:buffer, a:options.output])
|
||||
\ : call(l:Function, [l:buffer, a:options.output, copy(l:input)])
|
||||
else
|
||||
" Chained commands accept (buffer, [input])
|
||||
let l:result = ale#util#FunctionArgCount(l:Function) == 1
|
||||
\ ? call(l:Function, [l:buffer])
|
||||
\ : call(l:Function, [l:buffer, copy(l:input)])
|
||||
endif
|
||||
|
||||
if type(l:result) == type(0) && l:result == 0
|
||||
" When `0` is returned, skip this item.
|
||||
let l:index += 1
|
||||
elseif type(l:result) == type([])
|
||||
let l:input = l:result
|
||||
let l:index += 1
|
||||
else
|
||||
let l:ChainWith = get(l:result, 'chain_with', v:null)
|
||||
" Default to piping the buffer for the last fixer in the chain.
|
||||
let l:read_buffer = get(l:result, 'read_buffer', l:ChainWith is v:null)
|
||||
|
||||
let l:job_ran = s:RunJob({
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'command': l:result.command,
|
||||
\ 'input': l:input,
|
||||
\ 'output_stream': get(l:result, 'output_stream', 'stdout'),
|
||||
\ 'read_temporary_file': get(l:result, 'read_temporary_file', 0),
|
||||
\ 'read_buffer': l:read_buffer,
|
||||
\ 'chain_with': l:ChainWith,
|
||||
\ 'callback_list': a:options.callback_list,
|
||||
\ 'callback_index': l:index,
|
||||
\ 'process_with': get(l:result, 'process_with', v:null),
|
||||
\})
|
||||
|
||||
if !l:job_ran
|
||||
" The job failed to run, so skip to the next item.
|
||||
let l:index += 1
|
||||
else
|
||||
" Stop here, we will handle exit later on.
|
||||
return
|
||||
endif
|
||||
endif
|
||||
endwhile
|
||||
|
||||
call ale#fix#ApplyFixes(l:buffer, l:input)
|
||||
endfunction
|
||||
|
||||
function! s:GetCallbacks() abort
|
||||
if type(get(b:, 'ale_fixers')) is type([])
|
||||
" Lists can be used for buffer-local variables only
|
||||
let l:callback_list = b:ale_fixers
|
||||
else
|
||||
" buffer and global options can use dictionaries mapping filetypes to
|
||||
" callbacks to run.
|
||||
let l:fixers = ale#Var(bufnr(''), 'fixers')
|
||||
let l:callback_list = []
|
||||
|
||||
for l:sub_type in split(&filetype, '\.')
|
||||
let l:sub_type_callacks = get(l:fixers, l:sub_type, [])
|
||||
|
||||
if type(l:sub_type_callacks) == type('')
|
||||
call add(l:callback_list, l:sub_type_callacks)
|
||||
else
|
||||
call extend(l:callback_list, l:sub_type_callacks)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
if empty(l:callback_list)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:corrected_list = []
|
||||
|
||||
" Variables with capital characters are needed, or Vim will complain about
|
||||
" funcref variables.
|
||||
for l:Item in l:callback_list
|
||||
if type(l:Item) == type('')
|
||||
let l:Func = ale#fix#registry#GetFunc(l:Item)
|
||||
|
||||
if !empty(l:Func)
|
||||
let l:Item = l:Func
|
||||
endif
|
||||
endif
|
||||
|
||||
try
|
||||
call add(l:corrected_list, ale#util#GetFunction(l:Item))
|
||||
catch /E475/
|
||||
" Rethrow exceptions for failing to get a function so we can print
|
||||
" a friendly message about it.
|
||||
throw 'BADNAME ' . v:exception
|
||||
endtry
|
||||
endfor
|
||||
|
||||
return l:corrected_list
|
||||
endfunction
|
||||
|
||||
function! ale#fix#InitBufferData(buffer, fixing_flag) abort
|
||||
" The 'done' flag tells the function for applying changes when fixing
|
||||
" is complete.
|
||||
let g:ale_fix_buffer_data[a:buffer] = {
|
||||
\ 'vars': getbufvar(a:buffer, ''),
|
||||
\ 'lines_before': getbufline(a:buffer, 1, '$'),
|
||||
\ 'filename': expand('#' . a:buffer . ':p'),
|
||||
\ 'done': 0,
|
||||
\ 'should_save': a:fixing_flag is# 'save_file',
|
||||
\ 'temporary_directory_list': [],
|
||||
\}
|
||||
endfunction
|
||||
|
||||
" Accepts an optional argument for what to do when fixing.
|
||||
"
|
||||
" Returns 0 if no fixes can be applied, and 1 if fixing can be done.
|
||||
function! ale#fix#Fix(...) abort
|
||||
if len(a:0) > 1
|
||||
throw 'too many arguments!'
|
||||
endif
|
||||
|
||||
let l:fixing_flag = get(a:000, 0, '')
|
||||
|
||||
if l:fixing_flag isnot# '' && l:fixing_flag isnot# 'save_file'
|
||||
throw "fixing_flag must be either '' or 'save_file'"
|
||||
endif
|
||||
|
||||
try
|
||||
let l:callback_list = s:GetCallbacks()
|
||||
catch /E700\|BADNAME/
|
||||
let l:function_name = join(split(split(v:exception, ':')[3]))
|
||||
let l:echo_message = printf(
|
||||
\ 'There is no fixer named `%s`. Check :ALEFixSuggest',
|
||||
\ l:function_name,
|
||||
\)
|
||||
execute 'echom l:echo_message'
|
||||
|
||||
return 0
|
||||
endtry
|
||||
|
||||
if empty(l:callback_list)
|
||||
if l:fixing_flag is# ''
|
||||
execute 'echom ''No fixers have been defined. Try :ALEFixSuggest'''
|
||||
endif
|
||||
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:buffer = bufnr('')
|
||||
|
||||
for l:job_id in keys(s:job_info_map)
|
||||
call remove(s:job_info_map, l:job_id)
|
||||
call ale#job#Stop(l:job_id)
|
||||
endfor
|
||||
|
||||
" Clean up any files we might have left behind from a previous run.
|
||||
call ale#fix#RemoveManagedFiles(l:buffer)
|
||||
call ale#fix#InitBufferData(l:buffer, l:fixing_flag)
|
||||
|
||||
silent doautocmd <nomodeline> User ALEFixPre
|
||||
|
||||
call s:RunFixer({
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'input': g:ale_fix_buffer_data[l:buffer].lines_before,
|
||||
\ 'callback_index': 0,
|
||||
\ 'callback_list': l:callback_list,
|
||||
\})
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
" Set up an autocmd command to try and apply buffer fixes when available.
|
||||
augroup ALEBufferFixGroup
|
||||
autocmd!
|
||||
autocmd BufEnter * call ale#fix#ApplyQueuedFixes()
|
||||
augroup END
|
349
sources_non_forked/ale/autoload/ale/fix/registry.vim
Normal file
349
sources_non_forked/ale/autoload/ale/fix/registry.vim
Normal file
@ -0,0 +1,349 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: A registry of functions for fixing things.
|
||||
|
||||
let s:default_registry = {
|
||||
\ 'add_blank_lines_for_python_control_statements': {
|
||||
\ 'function': 'ale#fixers#generic_python#AddLinesBeforeControlStatements',
|
||||
\ 'suggested_filetypes': ['python'],
|
||||
\ 'description': 'Add blank lines before control statements.',
|
||||
\ },
|
||||
\ 'align_help_tags': {
|
||||
\ 'function': 'ale#fixers#help#AlignTags',
|
||||
\ 'suggested_filetypes': ['help'],
|
||||
\ 'description': 'Align help tags to the right margin',
|
||||
\ },
|
||||
\ 'autopep8': {
|
||||
\ 'function': 'ale#fixers#autopep8#Fix',
|
||||
\ 'suggested_filetypes': ['python'],
|
||||
\ 'description': 'Fix PEP8 issues with autopep8.',
|
||||
\ },
|
||||
\ 'prettier_standard': {
|
||||
\ 'function': 'ale#fixers#prettier_standard#Fix',
|
||||
\ 'suggested_filetypes': ['javascript'],
|
||||
\ 'description': 'Apply prettier-standard to a file.',
|
||||
\ 'aliases': ['prettier-standard'],
|
||||
\ },
|
||||
\ 'elm-format': {
|
||||
\ 'function': 'ale#fixers#elm_format#Fix',
|
||||
\ 'suggested_filetypes': ['elm'],
|
||||
\ 'description': 'Apply elm-format to a file.',
|
||||
\ 'aliases': ['format'],
|
||||
\ },
|
||||
\ 'eslint': {
|
||||
\ 'function': 'ale#fixers#eslint#Fix',
|
||||
\ 'suggested_filetypes': ['javascript', 'typescript'],
|
||||
\ 'description': 'Apply eslint --fix to a file.',
|
||||
\ },
|
||||
\ 'mix_format': {
|
||||
\ 'function': 'ale#fixers#mix_format#Fix',
|
||||
\ 'suggested_filetypes': ['elixir'],
|
||||
\ 'description': 'Apply mix format to a file.',
|
||||
\ },
|
||||
\ 'isort': {
|
||||
\ 'function': 'ale#fixers#isort#Fix',
|
||||
\ 'suggested_filetypes': ['python'],
|
||||
\ 'description': 'Sort Python imports with isort.',
|
||||
\ },
|
||||
\ 'prettier': {
|
||||
\ 'function': 'ale#fixers#prettier#Fix',
|
||||
\ 'suggested_filetypes': ['javascript', 'typescript', 'json', 'css', 'scss', 'less', 'markdown', 'graphql', 'vue'],
|
||||
\ 'description': 'Apply prettier to a file.',
|
||||
\ },
|
||||
\ 'prettier_eslint': {
|
||||
\ 'function': 'ale#fixers#prettier_eslint#Fix',
|
||||
\ 'suggested_filetypes': ['javascript'],
|
||||
\ 'description': 'Apply prettier-eslint to a file.',
|
||||
\ 'aliases': ['prettier-eslint'],
|
||||
\ },
|
||||
\ 'importjs': {
|
||||
\ 'function': 'ale#fixers#importjs#Fix',
|
||||
\ 'suggested_filetypes': ['javascript'],
|
||||
\ 'description': 'automatic imports for javascript',
|
||||
\ },
|
||||
\ 'puppetlint': {
|
||||
\ 'function': 'ale#fixers#puppetlint#Fix',
|
||||
\ 'suggested_filetypes': ['puppet'],
|
||||
\ 'description': 'Run puppet-lint -f on a file.',
|
||||
\ },
|
||||
\ 'remove_trailing_lines': {
|
||||
\ 'function': 'ale#fixers#generic#RemoveTrailingBlankLines',
|
||||
\ 'suggested_filetypes': [],
|
||||
\ 'description': 'Remove all blank lines at the end of a file.',
|
||||
\ },
|
||||
\ 'trim_whitespace': {
|
||||
\ 'function': 'ale#fixers#generic#TrimWhitespace',
|
||||
\ 'suggested_filetypes': [],
|
||||
\ 'description': 'Remove all trailing whitespace characters at the end of every line.',
|
||||
\ },
|
||||
\ 'yapf': {
|
||||
\ 'function': 'ale#fixers#yapf#Fix',
|
||||
\ 'suggested_filetypes': ['python'],
|
||||
\ 'description': 'Fix Python files with yapf.',
|
||||
\ },
|
||||
\ 'rubocop': {
|
||||
\ 'function': 'ale#fixers#rubocop#Fix',
|
||||
\ 'suggested_filetypes': ['ruby'],
|
||||
\ 'description': 'Fix ruby files with rubocop --auto-correct.',
|
||||
\ },
|
||||
\ 'rufo': {
|
||||
\ 'function': 'ale#fixers#rufo#Fix',
|
||||
\ 'suggested_filetypes': ['ruby'],
|
||||
\ 'description': 'Fix ruby files with rufo',
|
||||
\ },
|
||||
\ 'standard': {
|
||||
\ 'function': 'ale#fixers#standard#Fix',
|
||||
\ 'suggested_filetypes': ['javascript'],
|
||||
\ 'description': 'Fix JavaScript files using standard --fix',
|
||||
\ },
|
||||
\ 'stylelint': {
|
||||
\ 'function': 'ale#fixers#stylelint#Fix',
|
||||
\ 'suggested_filetypes': ['css', 'sass', 'scss', 'stylus'],
|
||||
\ 'description': 'Fix stylesheet files using stylelint --fix.',
|
||||
\ },
|
||||
\ 'swiftformat': {
|
||||
\ 'function': 'ale#fixers#swiftformat#Fix',
|
||||
\ 'suggested_filetypes': ['swift'],
|
||||
\ 'description': 'Apply SwiftFormat to a file.',
|
||||
\ },
|
||||
\ 'phpcbf': {
|
||||
\ 'function': 'ale#fixers#phpcbf#Fix',
|
||||
\ 'suggested_filetypes': ['php'],
|
||||
\ 'description': 'Fix PHP files with phpcbf.',
|
||||
\ },
|
||||
\ 'php_cs_fixer': {
|
||||
\ 'function': 'ale#fixers#php_cs_fixer#Fix',
|
||||
\ 'suggested_filetypes': ['php'],
|
||||
\ 'description': 'Fix PHP files with php-cs-fixer.',
|
||||
\ },
|
||||
\ 'clang-format': {
|
||||
\ 'function': 'ale#fixers#clangformat#Fix',
|
||||
\ 'suggested_filetypes': ['c', 'cpp'],
|
||||
\ 'description': 'Fix C/C++ files with clang-format.',
|
||||
\ },
|
||||
\ 'gofmt': {
|
||||
\ 'function': 'ale#fixers#gofmt#Fix',
|
||||
\ 'suggested_filetypes': ['go'],
|
||||
\ 'description': 'Fix Go files with go fmt.',
|
||||
\ },
|
||||
\ 'goimports': {
|
||||
\ 'function': 'ale#fixers#goimports#Fix',
|
||||
\ 'suggested_filetypes': ['go'],
|
||||
\ 'description': 'Fix Go files imports with goimports.',
|
||||
\ },
|
||||
\ 'tslint': {
|
||||
\ 'function': 'ale#fixers#tslint#Fix',
|
||||
\ 'suggested_filetypes': ['typescript'],
|
||||
\ 'description': 'Fix typescript files with tslint --fix.',
|
||||
\ },
|
||||
\ 'rustfmt': {
|
||||
\ 'function': 'ale#fixers#rustfmt#Fix',
|
||||
\ 'suggested_filetypes': ['rust'],
|
||||
\ 'description': 'Fix Rust files with Rustfmt.',
|
||||
\ },
|
||||
\ 'hackfmt': {
|
||||
\ 'function': 'ale#fixers#hackfmt#Fix',
|
||||
\ 'suggested_filetypes': ['php'],
|
||||
\ 'description': 'Fix Hack files with hackfmt.',
|
||||
\ },
|
||||
\ 'hfmt': {
|
||||
\ 'function': 'ale#fixers#hfmt#Fix',
|
||||
\ 'suggested_filetypes': ['haskell'],
|
||||
\ 'description': 'Fix Haskell files with hfmt.',
|
||||
\ },
|
||||
\ 'brittany': {
|
||||
\ 'function': 'ale#fixers#brittany#Fix',
|
||||
\ 'suggested_filetypes': ['haskell'],
|
||||
\ 'description': 'Fix Haskell files with brittany.',
|
||||
\ },
|
||||
\ 'refmt': {
|
||||
\ 'function': 'ale#fixers#refmt#Fix',
|
||||
\ 'suggested_filetypes': ['reason'],
|
||||
\ 'description': 'Fix ReasonML files with refmt.',
|
||||
\ },
|
||||
\ 'shfmt': {
|
||||
\ 'function': 'ale#fixers#shfmt#Fix',
|
||||
\ 'suggested_filetypes': ['sh'],
|
||||
\ 'description': 'Fix sh files with shfmt.',
|
||||
\ },
|
||||
\ 'google_java_format': {
|
||||
\ 'function': 'ale#fixers#google_java_format#Fix',
|
||||
\ 'suggested_filetypes': ['java'],
|
||||
\ 'description': 'Fix Java files with google-java-format.',
|
||||
\ },
|
||||
\ 'fixjson': {
|
||||
\ 'function': 'ale#fixers#fixjson#Fix',
|
||||
\ 'suggested_filetypes': ['json'],
|
||||
\ 'description': 'Fix JSON files with fixjson.',
|
||||
\ },
|
||||
\ 'jq': {
|
||||
\ 'function': 'ale#fixers#jq#Fix',
|
||||
\ 'suggested_filetypes': ['json'],
|
||||
\ 'description': 'Fix JSON files with jq.',
|
||||
\ },
|
||||
\}
|
||||
|
||||
" Reset the function registry to the default entries.
|
||||
function! ale#fix#registry#ResetToDefaults() abort
|
||||
let s:entries = deepcopy(s:default_registry)
|
||||
let s:aliases = {}
|
||||
|
||||
" Set up aliases for fixers too.
|
||||
for [l:key, l:entry] in items(s:entries)
|
||||
for l:alias in get(l:entry, 'aliases', [])
|
||||
let s:aliases[l:alias] = l:key
|
||||
endfor
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" Set up entries now.
|
||||
call ale#fix#registry#ResetToDefaults()
|
||||
|
||||
" Remove everything from the registry, useful for tests.
|
||||
function! ale#fix#registry#Clear() abort
|
||||
let s:entries = {}
|
||||
let s:aliases = {}
|
||||
endfunction
|
||||
|
||||
" Add a function for fixing problems to the registry.
|
||||
" (name, func, filetypes, desc, aliases)
|
||||
function! ale#fix#registry#Add(name, func, filetypes, desc, ...) abort
|
||||
if type(a:name) != type('')
|
||||
throw '''name'' must be a String'
|
||||
endif
|
||||
|
||||
if type(a:func) != type('')
|
||||
throw '''func'' must be a String'
|
||||
endif
|
||||
|
||||
if type(a:filetypes) != type([])
|
||||
throw '''filetypes'' must be a List'
|
||||
endif
|
||||
|
||||
for l:type in a:filetypes
|
||||
if type(l:type) != type('')
|
||||
throw 'Each entry of ''filetypes'' must be a String'
|
||||
endif
|
||||
endfor
|
||||
|
||||
if type(a:desc) != type('')
|
||||
throw '''desc'' must be a String'
|
||||
endif
|
||||
|
||||
let l:aliases = get(a:000, 0, [])
|
||||
|
||||
if type(l:aliases) != type([])
|
||||
\|| !empty(filter(copy(l:aliases), 'type(v:val) != type('''')'))
|
||||
throw '''aliases'' must be a List of String values'
|
||||
endif
|
||||
|
||||
let s:entries[a:name] = {
|
||||
\ 'function': a:func,
|
||||
\ 'suggested_filetypes': a:filetypes,
|
||||
\ 'description': a:desc,
|
||||
\}
|
||||
|
||||
" Set up aliases for the fixer.
|
||||
if !empty(l:aliases)
|
||||
let s:entries[a:name].aliases = l:aliases
|
||||
|
||||
for l:alias in l:aliases
|
||||
let s:aliases[l:alias] = a:name
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Get a function from the registry by its short name.
|
||||
function! ale#fix#registry#GetFunc(name) abort
|
||||
" Use the exact name, or an alias.
|
||||
let l:resolved_name = !has_key(s:entries, a:name)
|
||||
\ ? get(s:aliases, a:name, a:name)
|
||||
\ : a:name
|
||||
|
||||
return get(s:entries, l:resolved_name, {'function': ''}).function
|
||||
endfunction
|
||||
|
||||
function! s:ShouldSuggestForType(suggested_filetypes, type_list) abort
|
||||
for l:type in a:type_list
|
||||
if index(a:suggested_filetypes, l:type) >= 0
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! s:FormatEntry(key, entry) abort
|
||||
let l:aliases_str = ''
|
||||
|
||||
" Show aliases in :ALEFixSuggest if they are there.
|
||||
if !empty(get(a:entry, 'aliases', []))
|
||||
let l:aliases_str = ', ' . join(
|
||||
\ map(copy(a:entry.aliases), 'string(v:val)'),
|
||||
\ ','
|
||||
\)
|
||||
endif
|
||||
|
||||
return printf(
|
||||
\ '%s%s - %s',
|
||||
\ string(a:key),
|
||||
\ l:aliases_str,
|
||||
\ a:entry.description,
|
||||
\)
|
||||
endfunction
|
||||
|
||||
" Suggest functions to use from the registry.
|
||||
function! ale#fix#registry#Suggest(filetype) abort
|
||||
let l:type_list = split(a:filetype, '\.')
|
||||
let l:filetype_fixer_list = []
|
||||
|
||||
for l:key in sort(keys(s:entries))
|
||||
let l:suggested_filetypes = s:entries[l:key].suggested_filetypes
|
||||
|
||||
if s:ShouldSuggestForType(l:suggested_filetypes, l:type_list)
|
||||
call add(
|
||||
\ l:filetype_fixer_list,
|
||||
\ s:FormatEntry(l:key, s:entries[l:key]),
|
||||
\)
|
||||
endif
|
||||
endfor
|
||||
|
||||
let l:generic_fixer_list = []
|
||||
|
||||
for l:key in sort(keys(s:entries))
|
||||
if empty(s:entries[l:key].suggested_filetypes)
|
||||
call add(
|
||||
\ l:generic_fixer_list,
|
||||
\ s:FormatEntry(l:key, s:entries[l:key]),
|
||||
\)
|
||||
endif
|
||||
endfor
|
||||
|
||||
let l:filetype_fixer_header = !empty(l:filetype_fixer_list)
|
||||
\ ? ['Try the following fixers appropriate for the filetype:', '']
|
||||
\ : []
|
||||
let l:generic_fixer_header = !empty(l:generic_fixer_list)
|
||||
\ ? ['Try the following generic fixers:', '']
|
||||
\ : []
|
||||
|
||||
let l:has_both_lists = !empty(l:filetype_fixer_list) && !empty(l:generic_fixer_list)
|
||||
|
||||
let l:lines =
|
||||
\ l:filetype_fixer_header
|
||||
\ + l:filetype_fixer_list
|
||||
\ + (l:has_both_lists ? [''] : [])
|
||||
\ + l:generic_fixer_header
|
||||
\ + l:generic_fixer_list
|
||||
|
||||
if empty(l:lines)
|
||||
let l:lines = ['There is nothing in the registry to suggest.']
|
||||
else
|
||||
let l:lines += ['', 'See :help ale-fix-configuration']
|
||||
endif
|
||||
|
||||
let l:lines += ['', 'Press q to close this window']
|
||||
|
||||
new +set\ filetype=ale-fix-suggest
|
||||
call setline(1, l:lines)
|
||||
setlocal nomodified
|
||||
setlocal nomodifiable
|
||||
endfunction
|
26
sources_non_forked/ale/autoload/ale/fixers/autopep8.vim
Normal file
26
sources_non_forked/ale/autoload/ale/fixers/autopep8.vim
Normal file
@ -0,0 +1,26 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Fixing files with autopep8.
|
||||
|
||||
call ale#Set('python_autopep8_executable', 'autopep8')
|
||||
call ale#Set('python_autopep8_use_global', 0)
|
||||
call ale#Set('python_autopep8_options', '')
|
||||
|
||||
function! ale#fixers#autopep8#Fix(buffer) abort
|
||||
let l:executable = ale#python#FindExecutable(
|
||||
\ a:buffer,
|
||||
\ 'python_autopep8',
|
||||
\ ['autopep8'],
|
||||
\)
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:options = ale#Var(a:buffer, 'python_autopep8_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' -',
|
||||
\}
|
||||
endfunction
|
15
sources_non_forked/ale/autoload/ale/fixers/brittany.vim
Normal file
15
sources_non_forked/ale/autoload/ale/fixers/brittany.vim
Normal file
@ -0,0 +1,15 @@
|
||||
" Author: eborden <evan@evan-borden.com>
|
||||
" Description: Integration of brittany with ALE.
|
||||
|
||||
call ale#Set('haskell_brittany_executable', 'brittany')
|
||||
|
||||
function! ale#fixers#brittany#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'haskell_brittany_executable')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
||||
|
22
sources_non_forked/ale/autoload/ale/fixers/clangformat.vim
Normal file
22
sources_non_forked/ale/autoload/ale/fixers/clangformat.vim
Normal file
@ -0,0 +1,22 @@
|
||||
scriptencoding utf-8
|
||||
" Author: Peter Renström <renstrom.peter@gmail.com>
|
||||
" Description: Fixing C/C++ files with clang-format.
|
||||
|
||||
call ale#Set('c_clangformat_executable', 'clang-format')
|
||||
call ale#Set('c_clangformat_use_global', 0)
|
||||
call ale#Set('c_clangformat_options', '')
|
||||
|
||||
function! ale#fixers#clangformat#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'c_clangformat', [
|
||||
\ 'clang-format',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#clangformat#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'c_clangformat_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(ale#fixers#clangformat#GetExecutable(a:buffer))
|
||||
\ . ' ' . l:options,
|
||||
\}
|
||||
endfunction
|
23
sources_non_forked/ale/autoload/ale/fixers/elm_format.vim
Normal file
23
sources_non_forked/ale/autoload/ale/fixers/elm_format.vim
Normal file
@ -0,0 +1,23 @@
|
||||
" Author: soywod <clement.douin@gmail.com>
|
||||
" Description: Integration of elm-format with ALE.
|
||||
|
||||
call ale#Set('elm_format_executable', 'elm-format')
|
||||
call ale#Set('elm_format_use_global', 0)
|
||||
call ale#Set('elm_format_options', '--yes')
|
||||
|
||||
function! ale#fixers#elm_format#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'elm_format', [
|
||||
\ 'node_modules/.bin/elm-format',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#elm_format#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'elm_format_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(ale#fixers#elm_format#GetExecutable(a:buffer))
|
||||
\ . ' %t'
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options),
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
70
sources_non_forked/ale/autoload/ale/fixers/eslint.vim
Normal file
70
sources_non_forked/ale/autoload/ale/fixers/eslint.vim
Normal file
@ -0,0 +1,70 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Fixing files with eslint.
|
||||
|
||||
function! ale#fixers#eslint#Fix(buffer) abort
|
||||
let l:executable = ale#handlers#eslint#GetExecutable(a:buffer)
|
||||
|
||||
let l:command = ale#semver#HasVersion(l:executable)
|
||||
\ ? ''
|
||||
\ : ale#node#Executable(a:buffer, l:executable) . ' --version'
|
||||
|
||||
return {
|
||||
\ 'command': l:command,
|
||||
\ 'chain_with': 'ale#fixers#eslint#ApplyFixForVersion',
|
||||
\}
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#eslint#ProcessFixDryRunOutput(buffer, output) abort
|
||||
for l:item in ale#util#FuzzyJSONDecode(a:output, [])
|
||||
return split(get(l:item, 'output', ''), "\n")
|
||||
endfor
|
||||
|
||||
return []
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#eslint#ProcessEslintDOutput(buffer, output) abort
|
||||
" If the output is an error message, don't use it.
|
||||
for l:line in a:output[:10]
|
||||
if l:line =~# '^Error:'
|
||||
return []
|
||||
endif
|
||||
endfor
|
||||
|
||||
return a:output
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#eslint#ApplyFixForVersion(buffer, version_output) abort
|
||||
let l:executable = ale#handlers#eslint#GetExecutable(a:buffer)
|
||||
let l:version = ale#semver#GetVersion(l:executable, a:version_output)
|
||||
|
||||
let l:config = ale#handlers#eslint#FindConfig(a:buffer)
|
||||
|
||||
if empty(l:config)
|
||||
return 0
|
||||
endif
|
||||
|
||||
" Use --fix-to-stdout with eslint_d
|
||||
if l:executable =~# 'eslint_d$' && ale#semver#GTE(l:version, [3, 19, 0])
|
||||
return {
|
||||
\ 'command': ale#node#Executable(a:buffer, l:executable)
|
||||
\ . ' --stdin-filename %s --stdin --fix-to-stdout',
|
||||
\ 'process_with': 'ale#fixers#eslint#ProcessEslintDOutput',
|
||||
\}
|
||||
endif
|
||||
|
||||
" 4.9.0 is the first version with --fix-dry-run
|
||||
if ale#semver#GTE(l:version, [4, 9, 0])
|
||||
return {
|
||||
\ 'command': ale#node#Executable(a:buffer, l:executable)
|
||||
\ . ' --stdin-filename %s --stdin --fix-dry-run --format=json',
|
||||
\ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput',
|
||||
\}
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#node#Executable(a:buffer, l:executable)
|
||||
\ . ' -c ' . ale#Escape(l:config)
|
||||
\ . ' --fix %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
27
sources_non_forked/ale/autoload/ale/fixers/fixjson.vim
Normal file
27
sources_non_forked/ale/autoload/ale/fixers/fixjson.vim
Normal file
@ -0,0 +1,27 @@
|
||||
" Author: rhysd <https://rhysd.github.io>
|
||||
" Description: Integration of fixjson with ALE.
|
||||
|
||||
call ale#Set('json_fixjson_executable', 'fixjson')
|
||||
call ale#Set('json_fixjson_options', '')
|
||||
call ale#Set('json_fixjson_use_global', 0)
|
||||
|
||||
function! ale#fixers#fixjson#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'json_fixjson', [
|
||||
\ 'node_modules/.bin/fixjson',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#fixjson#Fix(buffer) abort
|
||||
let l:executable = ale#Escape(ale#fixers#fixjson#GetExecutable(a:buffer))
|
||||
let l:filename = ale#Escape(bufname(a:buffer))
|
||||
let l:command = l:executable . ' --stdin-filename ' . l:filename
|
||||
|
||||
let l:options = ale#Var(a:buffer, 'json_fixjson_options')
|
||||
if l:options isnot# ''
|
||||
let l:command .= ' ' . l:options
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': l:command
|
||||
\}
|
||||
endfunction
|
25
sources_non_forked/ale/autoload/ale/fixers/generic.vim
Normal file
25
sources_non_forked/ale/autoload/ale/fixers/generic.vim
Normal file
@ -0,0 +1,25 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Generic functions for fixing files with.
|
||||
|
||||
function! ale#fixers#generic#RemoveTrailingBlankLines(buffer, lines) abort
|
||||
let l:end_index = len(a:lines) - 1
|
||||
|
||||
while l:end_index > 0 && empty(a:lines[l:end_index])
|
||||
let l:end_index -= 1
|
||||
endwhile
|
||||
|
||||
return a:lines[:l:end_index]
|
||||
endfunction
|
||||
|
||||
" Remove all whitespaces at the end of lines
|
||||
function! ale#fixers#generic#TrimWhitespace(buffer, lines) abort
|
||||
let l:index = 0
|
||||
let l:lines_new = range(len(a:lines))
|
||||
|
||||
for l:line in a:lines
|
||||
let l:lines_new[l:index] = substitute(l:line, '\s\+$', '', 'g')
|
||||
let l:index = l:index + 1
|
||||
endfor
|
||||
|
||||
return l:lines_new
|
||||
endfunction
|
@ -0,0 +1,60 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Generic fixer functions for Python.
|
||||
|
||||
" Add blank lines before control statements.
|
||||
function! ale#fixers#generic_python#AddLinesBeforeControlStatements(buffer, lines) abort
|
||||
let l:new_lines = []
|
||||
let l:last_indent_size = 0
|
||||
let l:last_line_is_blank = 0
|
||||
|
||||
for l:line in a:lines
|
||||
let l:indent_size = len(matchstr(l:line, '^ *'))
|
||||
|
||||
if !l:last_line_is_blank
|
||||
\&& l:indent_size <= l:last_indent_size
|
||||
\&& match(l:line, '\v^ *(return|if|for|while|break|continue)') >= 0
|
||||
call add(l:new_lines, '')
|
||||
endif
|
||||
|
||||
call add(l:new_lines, l:line)
|
||||
let l:last_indent_size = l:indent_size
|
||||
let l:last_line_is_blank = empty(split(l:line))
|
||||
endfor
|
||||
|
||||
return l:new_lines
|
||||
endfunction
|
||||
|
||||
" This function breaks up long lines so that autopep8 or other tools can
|
||||
" fix the badly-indented code which is produced as a result.
|
||||
function! ale#fixers#generic_python#BreakUpLongLines(buffer, lines) abort
|
||||
" Default to a maximum line length of 79
|
||||
let l:max_line_length = 79
|
||||
let l:conf = ale#path#FindNearestFile(a:buffer, 'setup.cfg')
|
||||
|
||||
" Read the maximum line length from setup.cfg
|
||||
if !empty(l:conf)
|
||||
for l:match in ale#util#GetMatches(
|
||||
\ readfile(l:conf),
|
||||
\ '\v^ *max-line-length *\= *(\d+)',
|
||||
\)
|
||||
let l:max_line_length = str2nr(l:match[1])
|
||||
endfor
|
||||
endif
|
||||
|
||||
let l:new_list = []
|
||||
|
||||
for l:line in a:lines
|
||||
if len(l:line) > l:max_line_length && l:line !~# '# *noqa'
|
||||
let l:line = substitute(l:line, '\v([(,])([^)])', '\1\n\2', 'g')
|
||||
let l:line = substitute(l:line, '\v([^(])([)])', '\1,\n\2', 'g')
|
||||
|
||||
for l:split_line in split(l:line, "\n")
|
||||
call add(l:new_list, l:split_line)
|
||||
endfor
|
||||
else
|
||||
call add(l:new_list, l:line)
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:new_list
|
||||
endfunction
|
18
sources_non_forked/ale/autoload/ale/fixers/gofmt.vim
Normal file
18
sources_non_forked/ale/autoload/ale/fixers/gofmt.vim
Normal file
@ -0,0 +1,18 @@
|
||||
" Author: aliou <code@aliou.me>
|
||||
" Description: Integration of gofmt with ALE.
|
||||
|
||||
call ale#Set('go_gofmt_executable', 'gofmt')
|
||||
call ale#Set('go_gofmt_options', '')
|
||||
|
||||
function! ale#fixers#gofmt#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'go_gofmt_executable')
|
||||
let l:options = ale#Var(a:buffer, 'go_gofmt_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' -l -w'
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
22
sources_non_forked/ale/autoload/ale/fixers/goimports.vim
Normal file
22
sources_non_forked/ale/autoload/ale/fixers/goimports.vim
Normal file
@ -0,0 +1,22 @@
|
||||
" Author: Jeff Willette <jrwillette88@gmail.com>
|
||||
" Description: Integration of goimports with ALE.
|
||||
|
||||
call ale#Set('go_goimports_executable', 'goimports')
|
||||
call ale#Set('go_goimports_options', '')
|
||||
|
||||
function! ale#fixers#goimports#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'go_goimports_executable')
|
||||
let l:options = ale#Var(a:buffer, 'go_goimports_options')
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' -l -w -srcdir %s'
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
@ -0,0 +1,23 @@
|
||||
" Author: butlerx <butlerx@notthe,cloud>
|
||||
" Description: Integration of Google-java-format with ALE.
|
||||
|
||||
call ale#Set('google_java_format_executable', 'google-java-format')
|
||||
call ale#Set('google_java_format_use_global', 0)
|
||||
call ale#Set('google_java_format_options', '')
|
||||
|
||||
function! ale#fixers#google_java_format#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'google_java_format_options')
|
||||
let l:executable = ale#Var(a:buffer, 'google_java_format_executable')
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' ' . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\ . ' --replace'
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
18
sources_non_forked/ale/autoload/ale/fixers/hackfmt.vim
Normal file
18
sources_non_forked/ale/autoload/ale/fixers/hackfmt.vim
Normal file
@ -0,0 +1,18 @@
|
||||
" Author: Sam Howie <samhowie@gmail.com>
|
||||
" Description: Integration of hackfmt with ALE.
|
||||
|
||||
call ale#Set('php_hackfmt_executable', 'hackfmt')
|
||||
call ale#Set('php_hackfmt_options', '')
|
||||
|
||||
function! ale#fixers#hackfmt#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'php_hackfmt_executable')
|
||||
let l:options = ale#Var(a:buffer, 'php_hackfmt_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' -i'
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
24
sources_non_forked/ale/autoload/ale/fixers/help.vim
Normal file
24
sources_non_forked/ale/autoload/ale/fixers/help.vim
Normal file
@ -0,0 +1,24 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Generic fixer functions for Vim help documents.
|
||||
|
||||
function! ale#fixers#help#AlignTags(buffer, lines) abort
|
||||
let l:new_lines = []
|
||||
|
||||
for l:line in a:lines
|
||||
if len(l:line) != 79
|
||||
let l:match = matchlist(l:line, '\v +(\*[^*]+\*)$')
|
||||
|
||||
if !empty(l:match)
|
||||
let l:start = l:line[:-len(l:match[0]) - 1]
|
||||
let l:tag = l:match[1]
|
||||
let l:spaces = repeat(' ', 79 - len(l:start) - len(l:tag))
|
||||
|
||||
let l:line = l:start . l:spaces . l:tag
|
||||
endif
|
||||
endif
|
||||
|
||||
call add(l:new_lines, l:line)
|
||||
endfor
|
||||
|
||||
return l:new_lines
|
||||
endfunction
|
16
sources_non_forked/ale/autoload/ale/fixers/hfmt.vim
Normal file
16
sources_non_forked/ale/autoload/ale/fixers/hfmt.vim
Normal file
@ -0,0 +1,16 @@
|
||||
" Author: zack <zack@kourouma.me>
|
||||
" Description: Integration of hfmt with ALE.
|
||||
|
||||
call ale#Set('haskell_hfmt_executable', 'hfmt')
|
||||
|
||||
function! ale#fixers#hfmt#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'haskell_hfmt_executable')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' -w'
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
||||
|
24
sources_non_forked/ale/autoload/ale/fixers/importjs.vim
Normal file
24
sources_non_forked/ale/autoload/ale/fixers/importjs.vim
Normal file
@ -0,0 +1,24 @@
|
||||
" Author: Jeff Willette <jrwillette88@gmail.com>
|
||||
" Description: Integration of importjs with ALE.
|
||||
|
||||
call ale#Set('js_importjs_executable', 'importjs')
|
||||
|
||||
function! ale#fixers#importjs#ProcessOutput(buffer, output) abort
|
||||
let l:result = ale#util#FuzzyJSONDecode(a:output, [])
|
||||
return split(get(l:result, 'fileContent', ''), "\n")
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#importjs#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'js_importjs_executable')
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' fix'
|
||||
\ . ' %s',
|
||||
\ 'process_with': 'ale#fixers#importjs#ProcessOutput',
|
||||
\}
|
||||
endfunction
|
22
sources_non_forked/ale/autoload/ale/fixers/isort.vim
Normal file
22
sources_non_forked/ale/autoload/ale/fixers/isort.vim
Normal file
@ -0,0 +1,22 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Fixing Python imports with isort.
|
||||
|
||||
call ale#Set('python_isort_executable', 'isort')
|
||||
call ale#Set('python_isort_use_global', 0)
|
||||
|
||||
function! ale#fixers#isort#Fix(buffer) abort
|
||||
let l:executable = ale#python#FindExecutable(
|
||||
\ a:buffer,
|
||||
\ 'python_isort',
|
||||
\ ['isort'],
|
||||
\)
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#path#BufferCdString(a:buffer)
|
||||
\ . ale#Escape(l:executable) . ' -',
|
||||
\}
|
||||
endfunction
|
15
sources_non_forked/ale/autoload/ale/fixers/jq.vim
Normal file
15
sources_non_forked/ale/autoload/ale/fixers/jq.vim
Normal file
@ -0,0 +1,15 @@
|
||||
call ale#Set('json_jq_executable', 'jq')
|
||||
call ale#Set('json_jq_options', '')
|
||||
|
||||
function! ale#fixers#jq#GetExecutable(buffer) abort
|
||||
return ale#Var(a:buffer, 'json_jq_executable')
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#jq#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'json_jq_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(ale#fixers#jq#GetExecutable(a:buffer))
|
||||
\ . ' . ' . l:options,
|
||||
\}
|
||||
endfunction
|
25
sources_non_forked/ale/autoload/ale/fixers/mix_format.vim
Normal file
25
sources_non_forked/ale/autoload/ale/fixers/mix_format.vim
Normal file
@ -0,0 +1,25 @@
|
||||
" Author: carakan <carakan@gmail.com>, Fernando Mendes <fernando@mendes.codes>
|
||||
" Description: Fixing files with elixir formatter 'mix format'.
|
||||
|
||||
call ale#Set('elixir_mix_executable', 'mix')
|
||||
call ale#Set('elixir_mix_format_options', '')
|
||||
|
||||
function! ale#fixers#mix_format#GetExecutable(buffer) abort
|
||||
return ale#Var(a:buffer, 'elixir_mix_executable')
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#mix_format#GetCommand(buffer) abort
|
||||
let l:executable = ale#Escape(ale#fixers#mix_format#GetExecutable(a:buffer))
|
||||
let l:options = ale#Var(a:buffer, 'elixir_mix_format_options')
|
||||
|
||||
return l:executable . ' format'
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' %t'
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#mix_format#Fix(buffer) abort
|
||||
return {
|
||||
\ 'command': ale#fixers#mix_format#GetCommand(a:buffer),
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
23
sources_non_forked/ale/autoload/ale/fixers/php_cs_fixer.vim
Normal file
23
sources_non_forked/ale/autoload/ale/fixers/php_cs_fixer.vim
Normal file
@ -0,0 +1,23 @@
|
||||
" Author: Julien Deniau <julien.deniau@gmail.com>
|
||||
" Description: Fixing files with php-cs-fixer.
|
||||
|
||||
call ale#Set('php_cs_fixer_executable', 'php-cs-fixer')
|
||||
call ale#Set('php_cs_fixer_use_global', 0)
|
||||
|
||||
function! ale#fixers#php_cs_fixer#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'php_cs_fixer', [
|
||||
\ 'vendor/bin/php-cs-fixer',
|
||||
\ 'php-cs-fixer'
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#php_cs_fixer#Fix(buffer) abort
|
||||
let l:executable = ale#fixers#php_cs_fixer#GetExecutable(a:buffer)
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable) . ' fix %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
||||
|
||||
|
||||
|
24
sources_non_forked/ale/autoload/ale/fixers/phpcbf.vim
Normal file
24
sources_non_forked/ale/autoload/ale/fixers/phpcbf.vim
Normal file
@ -0,0 +1,24 @@
|
||||
" Author: notomo <notomo.motono@gmail.com>
|
||||
" Description: Fixing files with phpcbf.
|
||||
|
||||
call ale#Set('php_phpcbf_standard', '')
|
||||
call ale#Set('php_phpcbf_executable', 'phpcbf')
|
||||
call ale#Set('php_phpcbf_use_global', 0)
|
||||
|
||||
function! ale#fixers#phpcbf#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'php_phpcbf', [
|
||||
\ 'vendor/bin/phpcbf',
|
||||
\ 'phpcbf'
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#phpcbf#Fix(buffer) abort
|
||||
let l:executable = ale#fixers#phpcbf#GetExecutable(a:buffer)
|
||||
let l:standard = ale#Var(a:buffer, 'php_phpcbf_standard')
|
||||
let l:standard_option = !empty(l:standard)
|
||||
\ ? '--standard=' . l:standard
|
||||
\ : ''
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable) . ' --stdin-path=%s ' . l:standard_option . ' -'
|
||||
\}
|
||||
endfunction
|
53
sources_non_forked/ale/autoload/ale/fixers/prettier.vim
Normal file
53
sources_non_forked/ale/autoload/ale/fixers/prettier.vim
Normal file
@ -0,0 +1,53 @@
|
||||
" Author: tunnckoCore (Charlike Mike Reagent) <mameto2011@gmail.com>,
|
||||
" w0rp <devw0rp@gmail.com>, morhetz (Pavel Pertsev) <morhetz@gmail.com>
|
||||
" Description: Integration of Prettier with ALE.
|
||||
|
||||
call ale#Set('javascript_prettier_executable', 'prettier')
|
||||
call ale#Set('javascript_prettier_use_global', 0)
|
||||
call ale#Set('javascript_prettier_options', '')
|
||||
|
||||
function! ale#fixers#prettier#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'javascript_prettier', [
|
||||
\ 'node_modules/.bin/prettier_d',
|
||||
\ 'node_modules/prettier-cli/index.js',
|
||||
\ 'node_modules/.bin/prettier',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#prettier#Fix(buffer) abort
|
||||
let l:executable = ale#fixers#prettier#GetExecutable(a:buffer)
|
||||
|
||||
let l:command = ale#semver#HasVersion(l:executable)
|
||||
\ ? ''
|
||||
\ : ale#Escape(l:executable) . ' --version'
|
||||
|
||||
return {
|
||||
\ 'command': l:command,
|
||||
\ 'chain_with': 'ale#fixers#prettier#ApplyFixForVersion',
|
||||
\}
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#prettier#ApplyFixForVersion(buffer, version_output) abort
|
||||
let l:executable = ale#fixers#prettier#GetExecutable(a:buffer)
|
||||
let l:options = ale#Var(a:buffer, 'javascript_prettier_options')
|
||||
|
||||
let l:version = ale#semver#GetVersion(l:executable, a:version_output)
|
||||
|
||||
" 1.4.0 is the first version with --stdin-filepath
|
||||
if ale#semver#GTE(l:version, [1, 4, 0])
|
||||
return {
|
||||
\ 'command': ale#path#BufferCdString(a:buffer)
|
||||
\ . ale#Escape(l:executable)
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' --stdin-filepath %s --stdin',
|
||||
\}
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' %t'
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' --write',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
@ -0,0 +1,66 @@
|
||||
" Author: tunnckoCore (Charlike Mike Reagent) <mameto2011@gmail.com>,
|
||||
" w0rp <devw0rp@gmail.com>, morhetz (Pavel Pertsev) <morhetz@gmail.com>
|
||||
" Description: Integration between Prettier and ESLint.
|
||||
|
||||
function! ale#fixers#prettier_eslint#SetOptionDefaults() abort
|
||||
call ale#Set('javascript_prettier_eslint_executable', 'prettier-eslint')
|
||||
call ale#Set('javascript_prettier_eslint_use_global', 0)
|
||||
call ale#Set('javascript_prettier_eslint_options', '')
|
||||
endfunction
|
||||
|
||||
call ale#fixers#prettier_eslint#SetOptionDefaults()
|
||||
|
||||
function! ale#fixers#prettier_eslint#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'javascript_prettier_eslint', [
|
||||
\ 'node_modules/prettier-eslint-cli/dist/index.js',
|
||||
\ 'node_modules/.bin/prettier-eslint',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#prettier_eslint#Fix(buffer) abort
|
||||
let l:executable = ale#fixers#prettier_eslint#GetExecutable(a:buffer)
|
||||
|
||||
let l:command = ale#semver#HasVersion(l:executable)
|
||||
\ ? ''
|
||||
\ : ale#Escape(l:executable) . ' --version'
|
||||
|
||||
return {
|
||||
\ 'command': l:command,
|
||||
\ 'chain_with': 'ale#fixers#prettier_eslint#ApplyFixForVersion',
|
||||
\}
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#prettier_eslint#ApplyFixForVersion(buffer, version_output) abort
|
||||
let l:options = ale#Var(a:buffer, 'javascript_prettier_eslint_options')
|
||||
let l:executable = ale#fixers#prettier_eslint#GetExecutable(a:buffer)
|
||||
|
||||
let l:version = ale#semver#GetVersion(l:executable, a:version_output)
|
||||
|
||||
" 4.2.0 is the first version with --eslint-config-path
|
||||
let l:config = ale#semver#GTE(l:version, [4, 2, 0])
|
||||
\ ? ale#handlers#eslint#FindConfig(a:buffer)
|
||||
\ : ''
|
||||
let l:eslint_config_option = !empty(l:config)
|
||||
\ ? ' --eslint-config-path ' . ale#Escape(l:config)
|
||||
\ : ''
|
||||
|
||||
" 4.4.0 is the first version with --stdin-filepath
|
||||
if ale#semver#GTE(l:version, [4, 4, 0])
|
||||
return {
|
||||
\ 'command': ale#path#BufferCdString(a:buffer)
|
||||
\ . ale#Escape(l:executable)
|
||||
\ . l:eslint_config_option
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' --stdin-filepath %s --stdin',
|
||||
\}
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' %t'
|
||||
\ . l:eslint_config_option
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' --write',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
@ -0,0 +1,24 @@
|
||||
" Author: sheerun (Adam Stankiewicz) <sheerun@sher.pl>
|
||||
" Description: Integration of Prettier Standard with ALE.
|
||||
|
||||
call ale#Set('javascript_prettier_standard_executable', 'prettier-standard')
|
||||
call ale#Set('javascript_prettier_standard_use_global', 0)
|
||||
call ale#Set('javascript_prettier_standard_options', '')
|
||||
|
||||
function! ale#fixers#prettier_standard#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'javascript_prettier_standard', [
|
||||
\ 'node_modules/prettier-standard/lib/index.js',
|
||||
\ 'node_modules/.bin/prettier-standard',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#prettier_standard#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'javascript_prettier_standard_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(ale#fixers#prettier_standard#GetExecutable(a:buffer))
|
||||
\ . ' %t'
|
||||
\ . ' ' . l:options,
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
21
sources_non_forked/ale/autoload/ale/fixers/puppetlint.vim
Normal file
21
sources_non_forked/ale/autoload/ale/fixers/puppetlint.vim
Normal file
@ -0,0 +1,21 @@
|
||||
" Author: Alexander Olofsson <alexander.olofsson@liu.se>
|
||||
" Description: puppet-lint fixer
|
||||
|
||||
if !exists('g:ale_puppet_puppetlint_executable')
|
||||
let g:ale_puppet_puppetlint_executable = 'puppet-lint'
|
||||
endif
|
||||
if !exists('g:ale_puppet_puppetlint_options')
|
||||
let g:ale_puppet_puppetlint_options = ''
|
||||
endif
|
||||
|
||||
function! ale#fixers#puppetlint#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'puppet_puppetlint_executable')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . ' ' . ale#Var(a:buffer, 'puppet_puppetlint_options')
|
||||
\ . ' --fix'
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
18
sources_non_forked/ale/autoload/ale/fixers/refmt.vim
Normal file
18
sources_non_forked/ale/autoload/ale/fixers/refmt.vim
Normal file
@ -0,0 +1,18 @@
|
||||
" Author: Ahmed El Gabri <@ahmedelgabri>
|
||||
" Description: Integration of refmt with ALE.
|
||||
|
||||
call ale#Set('reasonml_refmt_executable', 'refmt')
|
||||
call ale#Set('reasonml_refmt_options', '')
|
||||
|
||||
function! ale#fixers#refmt#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'reasonml_refmt_executable')
|
||||
let l:options = ale#Var(a:buffer, 'reasonml_refmt_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\ . ' --in-place'
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
21
sources_non_forked/ale/autoload/ale/fixers/rubocop.vim
Normal file
21
sources_non_forked/ale/autoload/ale/fixers/rubocop.vim
Normal file
@ -0,0 +1,21 @@
|
||||
function! ale#fixers#rubocop#GetCommand(buffer) abort
|
||||
let l:executable = ale#handlers#rubocop#GetExecutable(a:buffer)
|
||||
let l:exec_args = l:executable =~? 'bundle$'
|
||||
\ ? ' exec rubocop'
|
||||
\ : ''
|
||||
let l:config = ale#path#FindNearestFile(a:buffer, '.rubocop.yml')
|
||||
let l:options = ale#Var(a:buffer, 'ruby_rubocop_options')
|
||||
|
||||
return ale#Escape(l:executable) . l:exec_args
|
||||
\ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '')
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' --auto-correct %t'
|
||||
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#rubocop#Fix(buffer) abort
|
||||
return {
|
||||
\ 'command': ale#fixers#rubocop#GetCommand(a:buffer),
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
20
sources_non_forked/ale/autoload/ale/fixers/rufo.vim
Normal file
20
sources_non_forked/ale/autoload/ale/fixers/rufo.vim
Normal file
@ -0,0 +1,20 @@
|
||||
" Author: Fohte (Hayato Kawai) https://github.com/fohte
|
||||
" Description: Integration of Rufo with ALE.
|
||||
|
||||
call ale#Set('ruby_rufo_executable', 'rufo')
|
||||
|
||||
function! ale#fixers#rufo#GetCommand(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'ruby_rufo_executable')
|
||||
let l:exec_args = l:executable =~? 'bundle$'
|
||||
\ ? ' exec rufo'
|
||||
\ : ''
|
||||
|
||||
return ale#Escape(l:executable) . l:exec_args . ' %t'
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#rufo#Fix(buffer) abort
|
||||
return {
|
||||
\ 'command': ale#fixers#rufo#GetCommand(a:buffer),
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
15
sources_non_forked/ale/autoload/ale/fixers/rustfmt.vim
Normal file
15
sources_non_forked/ale/autoload/ale/fixers/rustfmt.vim
Normal file
@ -0,0 +1,15 @@
|
||||
" Author: Kelly Fox <kelly@bumfuddled.com>
|
||||
" Description: Integration of rustfmt with ALE.
|
||||
|
||||
call ale#Set('rust_rustfmt_executable', 'rustfmt')
|
||||
call ale#Set('rust_rustfmt_options', '')
|
||||
|
||||
function! ale#fixers#rustfmt#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'rust_rustfmt_executable')
|
||||
let l:options = ale#Var(a:buffer, 'rust_rustfmt_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options),
|
||||
\}
|
||||
endfunction
|
17
sources_non_forked/ale/autoload/ale/fixers/shfmt.vim
Normal file
17
sources_non_forked/ale/autoload/ale/fixers/shfmt.vim
Normal file
@ -0,0 +1,17 @@
|
||||
scriptencoding utf-8
|
||||
" Author: Simon Bugert <simon.bugert@gmail.com>
|
||||
" Description: Fix sh files with shfmt.
|
||||
|
||||
call ale#Set('sh_shfmt_executable', 'shfmt')
|
||||
call ale#Set('sh_shfmt_options', '')
|
||||
|
||||
function! ale#fixers#shfmt#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'sh_shfmt_executable')
|
||||
let l:options = ale#Var(a:buffer, 'sh_shfmt_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\}
|
||||
|
||||
endfunction
|
23
sources_non_forked/ale/autoload/ale/fixers/standard.vim
Normal file
23
sources_non_forked/ale/autoload/ale/fixers/standard.vim
Normal file
@ -0,0 +1,23 @@
|
||||
" Author: Sumner Evans <sumner.evans98@gmail.com>
|
||||
" Description: Fixing files with Standard.
|
||||
|
||||
call ale#Set('javascript_standard_executable', 'standard')
|
||||
call ale#Set('javascript_standard_use_global', 0)
|
||||
call ale#Set('javascript_standard_options', '')
|
||||
|
||||
function! ale#fixers#standard#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'javascript_standard', [
|
||||
\ 'node_modules/standard/bin/cmd.js',
|
||||
\ 'node_modules/.bin/standard',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#standard#Fix(buffer) abort
|
||||
let l:executable = ale#fixers#standard#GetExecutable(a:buffer)
|
||||
|
||||
return {
|
||||
\ 'command': ale#node#Executable(a:buffer, l:executable)
|
||||
\ . ' --fix %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
23
sources_non_forked/ale/autoload/ale/fixers/stylelint.vim
Normal file
23
sources_non_forked/ale/autoload/ale/fixers/stylelint.vim
Normal file
@ -0,0 +1,23 @@
|
||||
" Author: Mahmoud Mostafa <mah@moud.info>
|
||||
" Description: Fixing files with stylelint.
|
||||
|
||||
call ale#Set('stylelint_executable', 'stylelint')
|
||||
call ale#Set('stylelint_use_global', 0)
|
||||
|
||||
function! ale#fixers#stylelint#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'stylelint', [
|
||||
\ 'node_modules/stylelint/bin/stylelint.js',
|
||||
\ 'node_modules/.bin/stylelint',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
|
||||
function! ale#fixers#stylelint#Fix(buffer) abort
|
||||
let l:executable = ale#fixers#stylelint#GetExecutable(a:buffer)
|
||||
|
||||
return {
|
||||
\ 'command': ale#node#Executable(a:buffer, l:executable)
|
||||
\ . ' --fix %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
25
sources_non_forked/ale/autoload/ale/fixers/swiftformat.vim
Normal file
25
sources_non_forked/ale/autoload/ale/fixers/swiftformat.vim
Normal file
@ -0,0 +1,25 @@
|
||||
" Author: gfontenot (Gordon Fontenot) <gordon@fonten.io>
|
||||
" Description: Integration of SwiftFormat with ALE.
|
||||
|
||||
call ale#Set('swift_swiftformat_executable', 'swiftformat')
|
||||
call ale#Set('swift_swiftformat_use_global', 0)
|
||||
call ale#Set('swift_swiftformat_options', '')
|
||||
|
||||
function! ale#fixers#swiftformat#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'swift_swiftformat', [
|
||||
\ 'Pods/SwiftFormat/CommandLineTool/swiftformat',
|
||||
\ 'ios/Pods/SwiftFormat/CommandLineTool/swiftformat',
|
||||
\ 'swiftformat',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#fixers#swiftformat#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'swift_swiftformat_options')
|
||||
|
||||
return {
|
||||
\ 'read_temporary_file': 1,
|
||||
\ 'command': ale#Escape(ale#fixers#swiftformat#GetExecutable(a:buffer))
|
||||
\ . ' %t'
|
||||
\ . ' ' . l:options,
|
||||
\}
|
||||
endfunction
|
22
sources_non_forked/ale/autoload/ale/fixers/tslint.vim
Normal file
22
sources_non_forked/ale/autoload/ale/fixers/tslint.vim
Normal file
@ -0,0 +1,22 @@
|
||||
" Author: carakan <carakan@gmail.com>
|
||||
" Description: Fixing files with tslint.
|
||||
|
||||
function! ale#fixers#tslint#Fix(buffer) abort
|
||||
let l:executable = ale_linters#typescript#tslint#GetExecutable(a:buffer)
|
||||
|
||||
let l:tslint_config_path = ale#path#ResolveLocalPath(
|
||||
\ a:buffer,
|
||||
\ 'tslint.json',
|
||||
\ ale#Var(a:buffer, 'typescript_tslint_config_path')
|
||||
\)
|
||||
let l:tslint_config_option = !empty(l:tslint_config_path)
|
||||
\ ? ' -c ' . ale#Escape(l:tslint_config_path)
|
||||
\ : ''
|
||||
|
||||
return {
|
||||
\ 'command': ale#node#Executable(a:buffer, l:executable)
|
||||
\ . l:tslint_config_option
|
||||
\ . ' --fix %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
26
sources_non_forked/ale/autoload/ale/fixers/yapf.vim
Normal file
26
sources_non_forked/ale/autoload/ale/fixers/yapf.vim
Normal file
@ -0,0 +1,26 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Fixing Python files with yapf.
|
||||
|
||||
call ale#Set('python_yapf_executable', 'yapf')
|
||||
call ale#Set('python_yapf_use_global', 0)
|
||||
|
||||
function! ale#fixers#yapf#Fix(buffer) abort
|
||||
let l:executable = ale#python#FindExecutable(
|
||||
\ a:buffer,
|
||||
\ 'python_yapf',
|
||||
\ ['yapf'],
|
||||
\)
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:config = ale#path#FindNearestFile(a:buffer, '.style.yapf')
|
||||
let l:config_options = !empty(l:config)
|
||||
\ ? ' --no-local-style --style ' . ale#Escape(l:config)
|
||||
\ : ''
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable) . l:config_options,
|
||||
\}
|
||||
endfunction
|
67
sources_non_forked/ale/autoload/ale/gradle.vim
Normal file
67
sources_non_forked/ale/autoload/ale/gradle.vim
Normal file
@ -0,0 +1,67 @@
|
||||
" Author: Michael Pardo <michael@michaelpardo.com>
|
||||
" Description: Functions for working with Gradle projects.
|
||||
|
||||
let s:script_path = fnamemodify(resolve(expand('<sfile>:p')), ':h')
|
||||
let s:init_path = has('win32')
|
||||
\ ? s:script_path . '\gradle\init.gradle'
|
||||
\ : s:script_path . '/gradle/init.gradle'
|
||||
|
||||
function! ale#gradle#GetInitPath() abort
|
||||
return s:init_path
|
||||
endfunction
|
||||
|
||||
" Given a buffer number, find a Gradle project root.
|
||||
function! ale#gradle#FindProjectRoot(buffer) abort
|
||||
let l:gradlew_path = ale#path#FindNearestFile(a:buffer, 'gradlew')
|
||||
|
||||
if !empty(l:gradlew_path)
|
||||
return fnamemodify(l:gradlew_path, ':h')
|
||||
endif
|
||||
|
||||
let l:settings_path = ale#path#FindNearestFile(a:buffer, 'settings.gradle')
|
||||
|
||||
if !empty(l:settings_path)
|
||||
return fnamemodify(l:settings_path, ':h')
|
||||
endif
|
||||
|
||||
let l:build_path = ale#path#FindNearestFile(a:buffer, 'build.gradle')
|
||||
|
||||
if !empty(l:build_path)
|
||||
return fnamemodify(l:build_path, ':h')
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Given a buffer number, find the path to the executable.
|
||||
" First search on the path for 'gradlew', if nothing is found, try the global
|
||||
" command. Returns an empty string if cannot find the executable.
|
||||
function! ale#gradle#FindExecutable(buffer) abort
|
||||
let l:gradlew_path = ale#path#FindNearestFile(a:buffer, 'gradlew')
|
||||
|
||||
if !empty(l:gradlew_path)
|
||||
return l:gradlew_path
|
||||
endif
|
||||
|
||||
if executable('gradle')
|
||||
return 'gradle'
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Given a buffer number, build a command to print the classpath of the root
|
||||
" project. Returns an empty string if cannot build the command.
|
||||
function! ale#gradle#BuildClasspathCommand(buffer) abort
|
||||
let l:executable = ale#gradle#FindExecutable(a:buffer)
|
||||
let l:project_root = ale#gradle#FindProjectRoot(a:buffer)
|
||||
|
||||
if !empty(l:executable) && !empty(l:project_root)
|
||||
return ale#path#CdString(l:project_root)
|
||||
\ . ale#Escape(l:executable)
|
||||
\ . ' -I ' . ale#Escape(s:init_path)
|
||||
\ . ' -q printClasspath'
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
23
sources_non_forked/ale/autoload/ale/gradle/init.gradle
Normal file
23
sources_non_forked/ale/autoload/ale/gradle/init.gradle
Normal file
@ -0,0 +1,23 @@
|
||||
class ClasspathPlugin implements Plugin<Project> {
|
||||
void apply(Project project) {
|
||||
project.task('printClasspath') {
|
||||
doLast {
|
||||
project
|
||||
.rootProject
|
||||
.allprojects
|
||||
.configurations
|
||||
.flatten()
|
||||
.findAll { it.name.endsWith('Classpath') }
|
||||
.collect { it.resolve() }
|
||||
.flatten()
|
||||
.unique()
|
||||
.findAll { it.exists() }
|
||||
.each { println it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject {
|
||||
apply plugin: ClasspathPlugin
|
||||
}
|
22
sources_non_forked/ale/autoload/ale/handlers/alex.vim
Normal file
22
sources_non_forked/ale/autoload/ale/handlers/alex.vim
Normal file
@ -0,0 +1,22 @@
|
||||
" Author: Johannes Wienke <languitar@semipol.de>
|
||||
" Description: Error handling for errors in alex output format
|
||||
|
||||
function! ale#handlers#alex#Handle(buffer, lines) abort
|
||||
" Example output:
|
||||
" 6:256-6:262 warning Be careful with “killed”, it’s profane in some cases killed retext-profanities
|
||||
let l:pattern = '^ *\(\d\+\):\(\d\+\)-\(\d\+\):\(\d\+\) \+warning \+\(.\{-\}\) \+\(.\{-\}\) \+\(.\{-\}\)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': l:match[2] + 0,
|
||||
\ 'end_lnum': l:match[3] + 0,
|
||||
\ 'end_col': l:match[4] - 1,
|
||||
\ 'text': l:match[5] . ' (' . (l:match[7]) . ')',
|
||||
\ 'type': 'W',
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
21
sources_non_forked/ale/autoload/ale/handlers/cppcheck.vim
Normal file
21
sources_non_forked/ale/autoload/ale/handlers/cppcheck.vim
Normal file
@ -0,0 +1,21 @@
|
||||
" Description: Handle errors for cppcheck.
|
||||
|
||||
function! ale#handlers#cppcheck#HandleCppCheckFormat(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
"
|
||||
" [test.cpp:5]: (error) Array 'a[10]' accessed at index 10, which is out of bounds
|
||||
let l:pattern = '\v^\[(.+):(\d+)\]: \(([a-z]+)\) (.+)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
if ale#path#IsBufferPath(a:buffer, l:match[1])
|
||||
call add(l:output, {
|
||||
\ 'lnum': str2nr(l:match[2]),
|
||||
\ 'type': l:match[3] is# 'error' ? 'E' : 'W',
|
||||
\ 'text': l:match[4],
|
||||
\})
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
21
sources_non_forked/ale/autoload/ale/handlers/cpplint.vim
Normal file
21
sources_non_forked/ale/autoload/ale/handlers/cpplint.vim
Normal file
@ -0,0 +1,21 @@
|
||||
" Author: Dawid Kurek https://github.com/dawikur
|
||||
" Description: Handle errors for cpplint.
|
||||
|
||||
function! ale#handlers#cpplint#HandleCppLintFormat(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
" test.cpp:5: Estra space after ( in function call [whitespace/parents] [4]
|
||||
let l:pattern = '^.\{-}:\(\d\+\): *\(.\+\) *\[\(.*/.*\)\] '
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': 0,
|
||||
\ 'text': join(split(l:match[2])),
|
||||
\ 'code': l:match[3],
|
||||
\ 'type': 'W',
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
70
sources_non_forked/ale/autoload/ale/handlers/css.vim
Normal file
70
sources_non_forked/ale/autoload/ale/handlers/css.vim
Normal file
@ -0,0 +1,70 @@
|
||||
scriptencoding utf-8
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Error handling for CSS linters.
|
||||
|
||||
function! ale#handlers#css#HandleCSSLintFormat(buffer, lines) abort
|
||||
" Matches patterns line the following:
|
||||
"
|
||||
" something.css: line 2, col 1, Error - Expected RBRACE at line 2, col 1. (errors)
|
||||
" something.css: line 2, col 5, Warning - Expected (inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex) but found 'wat'. (known-properties)
|
||||
"
|
||||
" These errors can be very massive, so the type will be moved to the front
|
||||
" so you can actually read the error type.
|
||||
let l:pattern = '\v^.*: line (\d+), col (\d+), (Error|Warning) - (.+)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
let l:item = {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': l:match[2] + 0,
|
||||
\ 'type': l:match[3] is# 'Warning' ? 'W' : 'E',
|
||||
\ 'text': l:match[4],
|
||||
\}
|
||||
|
||||
let l:code_match = matchlist(l:match[4], '\v(.+) \(([^(]+)\)$')
|
||||
|
||||
" Split up the error code and the text if we find one.
|
||||
if !empty(l:code_match)
|
||||
let l:item.text = l:code_match[1]
|
||||
let l:item.code = l:code_match[2]
|
||||
endif
|
||||
|
||||
call add(l:output, l:item)
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#css#HandleStyleLintFormat(buffer, lines) abort
|
||||
let l:exception_pattern = '\v^Error:'
|
||||
|
||||
for l:line in a:lines[:10]
|
||||
if len(matchlist(l:line, l:exception_pattern)) > 0
|
||||
return [{
|
||||
\ 'lnum': 1,
|
||||
\ 'text': 'stylelint exception thrown (type :ALEDetail for more information)',
|
||||
\ 'detail': join(a:lines, "\n"),
|
||||
\}]
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Matches patterns line the following:
|
||||
"
|
||||
" src/main.css
|
||||
" 108:10 ✖ Unexpected leading zero number-leading-zero
|
||||
" 116:20 ✖ Expected a trailing semicolon declaration-block-trailing-semicolon
|
||||
let l:pattern = '\v^.* (\d+):(\d+) \s+(\S+)\s+ (.*[^ ])\s+([^ ]+)\s*$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': l:match[2] + 0,
|
||||
\ 'type': l:match[3] is# '✖' ? 'E' : 'W',
|
||||
\ 'text': l:match[4],
|
||||
\ 'code': l:match[5],
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
153
sources_non_forked/ale/autoload/ale/handlers/eslint.vim
Normal file
153
sources_non_forked/ale/autoload/ale/handlers/eslint.vim
Normal file
@ -0,0 +1,153 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Functions for working with eslint, for checking or fixing files.
|
||||
|
||||
let s:sep = has('win32') ? '\' : '/'
|
||||
|
||||
call ale#Set('javascript_eslint_options', '')
|
||||
call ale#Set('javascript_eslint_executable', 'eslint')
|
||||
call ale#Set('javascript_eslint_use_global', 0)
|
||||
call ale#Set('javascript_eslint_suppress_eslintignore', 0)
|
||||
call ale#Set('javascript_eslint_suppress_missing_config', 0)
|
||||
|
||||
function! ale#handlers#eslint#FindConfig(buffer) abort
|
||||
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
|
||||
for l:basename in [
|
||||
\ '.eslintrc.js',
|
||||
\ '.eslintrc.yaml',
|
||||
\ '.eslintrc.yml',
|
||||
\ '.eslintrc.json',
|
||||
\ '.eslintrc',
|
||||
\]
|
||||
let l:config = ale#path#Simplify(join([l:path, l:basename], s:sep))
|
||||
|
||||
if filereadable(l:config)
|
||||
return l:config
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return ale#path#FindNearestFile(a:buffer, 'package.json')
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#eslint#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'javascript_eslint', [
|
||||
\ 'node_modules/.bin/eslint_d',
|
||||
\ 'node_modules/eslint/bin/eslint.js',
|
||||
\ 'node_modules/.bin/eslint',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#eslint#GetCommand(buffer) abort
|
||||
let l:executable = ale#handlers#eslint#GetExecutable(a:buffer)
|
||||
|
||||
let l:options = ale#Var(a:buffer, 'javascript_eslint_options')
|
||||
|
||||
return ale#node#Executable(a:buffer, l:executable)
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' -f unix --stdin --stdin-filename %s'
|
||||
endfunction
|
||||
|
||||
let s:col_end_patterns = [
|
||||
\ '\vParsing error: Unexpected token (.+) ?',
|
||||
\ '\v''(.+)'' is not defined.',
|
||||
\ '\v%(Unexpected|Redundant use of) [''`](.+)[''`]',
|
||||
\ '\vUnexpected (console) statement',
|
||||
\]
|
||||
|
||||
function! s:AddHintsForTypeScriptParsingErrors(output) abort
|
||||
for l:item in a:output
|
||||
let l:item.text = substitute(
|
||||
\ l:item.text,
|
||||
\ '^\(Parsing error\)',
|
||||
\ '\1 (You may need configure typescript-eslint-parser)',
|
||||
\ '',
|
||||
\)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:CheckForBadConfig(buffer, lines) abort
|
||||
let l:config_error_pattern = '\v^ESLint couldn''t find a configuration file'
|
||||
\ . '|^Cannot read config file'
|
||||
\ . '|^.*Configuration for rule .* is invalid'
|
||||
\ . '|^ImportDeclaration should appear'
|
||||
|
||||
" Look for a message in the first few lines which indicates that
|
||||
" a configuration file couldn't be found.
|
||||
for l:line in a:lines[:10]
|
||||
let l:match = matchlist(l:line, l:config_error_pattern)
|
||||
|
||||
if len(l:match) > 0
|
||||
" Don't show the missing config error if we've disabled it.
|
||||
if ale#Var(a:buffer, 'javascript_eslint_suppress_missing_config')
|
||||
\&& l:match[0] is# 'ESLint couldn''t find a configuration file'
|
||||
return 0
|
||||
endif
|
||||
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#eslint#Handle(buffer, lines) abort
|
||||
if s:CheckForBadConfig(a:buffer, a:lines)
|
||||
return [{
|
||||
\ 'lnum': 1,
|
||||
\ 'text': 'eslint configuration error (type :ALEDetail for more information)',
|
||||
\ 'detail': join(a:lines, "\n"),
|
||||
\}]
|
||||
endif
|
||||
|
||||
" Matches patterns line the following:
|
||||
"
|
||||
" /path/to/some-filename.js:47:14: Missing trailing comma. [Warning/comma-dangle]
|
||||
" /path/to/some-filename.js:56:41: Missing semicolon. [Error/semi]
|
||||
let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) \[\(.\+\)\]$'
|
||||
" This second pattern matches lines like the following:
|
||||
"
|
||||
" /path/to/some-filename.js:13:3: Parsing error: Unexpected token
|
||||
let l:parsing_pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, [l:pattern, l:parsing_pattern])
|
||||
let l:text = l:match[3]
|
||||
|
||||
if ale#Var(a:buffer, 'javascript_eslint_suppress_eslintignore')
|
||||
if l:text is# 'File ignored because of a matching ignore pattern. Use "--no-ignore" to override.'
|
||||
continue
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:obj = {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': l:match[2] + 0,
|
||||
\ 'text': l:text,
|
||||
\ 'type': 'E',
|
||||
\}
|
||||
|
||||
" Take the error type from the output if available.
|
||||
let l:split_code = split(l:match[4], '/')
|
||||
|
||||
if get(l:split_code, 0, '') is# 'Warning'
|
||||
let l:obj.type = 'W'
|
||||
endif
|
||||
|
||||
" The code can be something like 'Error/foo/bar', or just 'Error'
|
||||
if !empty(get(l:split_code, 1))
|
||||
let l:obj.code = join(l:split_code[1:], '/')
|
||||
endif
|
||||
|
||||
for l:col_match in ale#util#GetMatches(l:text, s:col_end_patterns)
|
||||
let l:obj.end_col = l:obj.col + len(l:col_match[1]) - 1
|
||||
endfor
|
||||
|
||||
call add(l:output, l:obj)
|
||||
endfor
|
||||
|
||||
if expand('#' . a:buffer . ':t') =~? '\.tsx\?$'
|
||||
call s:AddHintsForTypeScriptParsingErrors(l:output)
|
||||
endif
|
||||
|
||||
return l:output
|
||||
endfunction
|
47
sources_non_forked/ale/autoload/ale/handlers/flawfinder.vim
Normal file
47
sources_non_forked/ale/autoload/ale/handlers/flawfinder.vim
Normal file
@ -0,0 +1,47 @@
|
||||
" Author: Christian Gibbons <cgibbons@gmu.edu>
|
||||
" Description: This file defines a handler function that should work for the
|
||||
" flawfinder format with the -CDQS flags.
|
||||
|
||||
" Swiped this function from the GCC handler. Not sure if needed, but doesn't
|
||||
" hurt to have it.
|
||||
function! s:RemoveUnicodeQuotes(text) abort
|
||||
let l:text = a:text
|
||||
let l:text = substitute(l:text, '[`´‘’]', '''', 'g')
|
||||
let l:text = substitute(l:text, '\v\\u2018([^\\]+)\\u2019', '''\1''', 'g')
|
||||
let l:text = substitute(l:text, '[“”]', '"', 'g')
|
||||
|
||||
return l:text
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#flawfinder#HandleFlawfinderFormat(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
"
|
||||
" <stdin>:12:4: [2] (buffer) char:Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120). Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length.
|
||||
" <stdin>:31:4: [1] (buffer) strncpy:Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120).
|
||||
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ( \[[0-5]\] [^:]+):(.+)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
" Use severity level to determine if it should be considered a warning
|
||||
" or error.
|
||||
let l:severity = str2nr(matchstr(split(l:match[4])[0], '[0-5]'))
|
||||
|
||||
let l:item = {
|
||||
\ 'lnum': str2nr(l:match[2]),
|
||||
\ 'col': str2nr(l:match[3]),
|
||||
\ 'type': (l:severity < ale#Var(a:buffer, 'c_flawfinder_error_severity'))
|
||||
\ ? 'W' : 'E',
|
||||
\ 'text': s:RemoveUnicodeQuotes(join(split(l:match[4])[1:]) . ': ' . l:match[5]),
|
||||
\}
|
||||
|
||||
" If the filename is something like <stdin>, <nofile> or -, then
|
||||
" this is an error for the file we checked.
|
||||
if l:match[1] isnot# '-' && l:match[1][0] isnot# '<'
|
||||
let l:item['filename'] = l:match[1]
|
||||
endif
|
||||
|
||||
call add(l:output, l:item)
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
25
sources_non_forked/ale/autoload/ale/handlers/gawk.vim
Normal file
25
sources_non_forked/ale/autoload/ale/handlers/gawk.vim
Normal file
@ -0,0 +1,25 @@
|
||||
" Author: Anthony DeDominic <adedomin@gmail.com>
|
||||
" Description: Handle output from gawk's --lint option
|
||||
|
||||
function! ale#handlers#gawk#HandleGawkFormat(buffer, lines) abort
|
||||
" Look for lines like the following:
|
||||
" gawk: /tmp/v0fddXz/1/something.awk:1: ^ invalid char ''' in expression
|
||||
let l:pattern = '^.\{-}:\(\d\+\):\s\+\(warning:\|\^\)\s*\(.*\)'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
let l:ecode = 'E'
|
||||
if l:match[2] is? 'warning:'
|
||||
let l:ecode = 'W'
|
||||
endif
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': 0,
|
||||
\ 'text': l:match[3],
|
||||
\ 'code': 0,
|
||||
\ 'type': l:ecode,
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
69
sources_non_forked/ale/autoload/ale/handlers/gcc.vim
Normal file
69
sources_non_forked/ale/autoload/ale/handlers/gcc.vim
Normal file
@ -0,0 +1,69 @@
|
||||
scriptencoding utf-8
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: This file defines a handler function which ought to work for
|
||||
" any program which outputs errors in the format that GCC uses.
|
||||
|
||||
let s:pragma_error = '#pragma once in main file'
|
||||
|
||||
function! s:IsHeaderFile(filename) abort
|
||||
return a:filename =~? '\v\.(h|hpp)$'
|
||||
endfunction
|
||||
|
||||
function! s:RemoveUnicodeQuotes(text) abort
|
||||
let l:text = a:text
|
||||
let l:text = substitute(l:text, '[`´‘’]', '''', 'g')
|
||||
let l:text = substitute(l:text, '\v\\u2018([^\\]+)\\u2019', '''\1''', 'g')
|
||||
let l:text = substitute(l:text, '[“”]', '"', 'g')
|
||||
|
||||
return l:text
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
"
|
||||
" <stdin>:8:5: warning: conversion lacks type at end of format [-Wformat=]
|
||||
" <stdin>:10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’)
|
||||
" -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004]
|
||||
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
" Filter out the pragma errors
|
||||
if s:IsHeaderFile(bufname(bufnr('')))
|
||||
\&& l:match[5][:len(s:pragma_error) - 1] is# s:pragma_error
|
||||
continue
|
||||
endif
|
||||
|
||||
" If the 'error type' is a note, make it detail related to
|
||||
" the previous error parsed in output
|
||||
if l:match[4] is# 'note'
|
||||
if !empty(l:output)
|
||||
let l:output[-1]['detail'] =
|
||||
\ get(l:output[-1], 'detail', '')
|
||||
\ . s:RemoveUnicodeQuotes(l:match[0]) . "\n"
|
||||
endif
|
||||
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:item = {
|
||||
\ 'lnum': str2nr(l:match[2]),
|
||||
\ 'type': l:match[4] is# 'error' ? 'E' : 'W',
|
||||
\ 'text': s:RemoveUnicodeQuotes(l:match[5]),
|
||||
\}
|
||||
|
||||
if !empty(l:match[3])
|
||||
let l:item.col = str2nr(l:match[3])
|
||||
endif
|
||||
|
||||
" If the filename is something like <stdin>, <nofile> or -, then
|
||||
" this is an error for the file we checked.
|
||||
if l:match[1] isnot# '-' && l:match[1][0] isnot# '<'
|
||||
let l:item['filename'] = l:match[1]
|
||||
endif
|
||||
|
||||
call add(l:output, l:item)
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
25
sources_non_forked/ale/autoload/ale/handlers/go.vim
Normal file
25
sources_non_forked/ale/autoload/ale/handlers/go.vim
Normal file
@ -0,0 +1,25 @@
|
||||
" Author: neersighted <bjorn@neersighted.com>
|
||||
" Description: go vet for Go files
|
||||
"
|
||||
" Author: John Eikenberry <jae@zhar.net>
|
||||
" Description: updated to work with go1.10
|
||||
"
|
||||
" Author: Ben Paxton <ben@gn32.uk>
|
||||
" Description: moved to generic Golang file from govet
|
||||
|
||||
function! ale#handlers#go#Handler(buffer, lines) abort
|
||||
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):?(\d+)?:? ?(.+)$'
|
||||
let l:output = []
|
||||
let l:dir = expand('#' . a:buffer . ':p:h')
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:output, {
|
||||
\ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
|
||||
\ 'lnum': l:match[2] + 0,
|
||||
\ 'col': l:match[3] + 0,
|
||||
\ 'text': l:match[4],
|
||||
\ 'type': 'E',
|
||||
\})
|
||||
endfor
|
||||
return l:output
|
||||
endfunction
|
91
sources_non_forked/ale/autoload/ale/handlers/haskell.vim
Normal file
91
sources_non_forked/ale/autoload/ale/handlers/haskell.vim
Normal file
@ -0,0 +1,91 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Error handling for the format GHC outputs.
|
||||
|
||||
" Remember the directory used for temporary files for Vim.
|
||||
let s:temp_dir = fnamemodify(tempname(), ':h')
|
||||
" Build part of a regular expression for matching ALE temporary filenames.
|
||||
let s:temp_regex_prefix =
|
||||
\ '\M'
|
||||
\ . substitute(s:temp_dir, '\\', '\\\\', 'g')
|
||||
\ . '\.\{-}'
|
||||
|
||||
function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
"
|
||||
"Appoint/Lib.hs:8:1: warning:
|
||||
"Appoint/Lib.hs:8:1:
|
||||
let l:basename = expand('#' . a:buffer . ':t')
|
||||
" Build a complete regular expression for replacing temporary filenames
|
||||
" in Haskell error messages with the basename for this file.
|
||||
let l:temp_filename_regex = s:temp_regex_prefix . l:basename
|
||||
|
||||
let l:pattern = '\v^\s*([a-zA-Z]?:?[^:]+):(\d+):(\d+):(.*)?$'
|
||||
let l:output = []
|
||||
|
||||
let l:corrected_lines = []
|
||||
|
||||
" Group the lines into smaller lists.
|
||||
for l:line in a:lines
|
||||
if len(matchlist(l:line, l:pattern)) > 0
|
||||
call add(l:corrected_lines, [l:line])
|
||||
elseif l:line is# ''
|
||||
call add(l:corrected_lines, [l:line])
|
||||
elseif len(l:corrected_lines) > 0
|
||||
call add(l:corrected_lines[-1], l:line)
|
||||
endif
|
||||
endfor
|
||||
|
||||
for l:line_list in l:corrected_lines
|
||||
" Join the smaller lists into one large line to parse.
|
||||
let l:line = l:line_list[0]
|
||||
|
||||
for l:extra_line in l:line_list[1:]
|
||||
let l:line .= substitute(l:extra_line, '\v^\s+', ' ', '')
|
||||
endfor
|
||||
|
||||
let l:match = matchlist(l:line, l:pattern)
|
||||
|
||||
if len(l:match) == 0
|
||||
continue
|
||||
endif
|
||||
|
||||
if !ale#path#IsBufferPath(a:buffer, l:match[1])
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:errors = matchlist(l:match[4], '\v([wW]arning|[eE]rror): ?(.*)')
|
||||
|
||||
if len(l:errors) > 0
|
||||
let l:ghc_type = l:errors[1]
|
||||
let l:text = l:errors[2]
|
||||
else
|
||||
let l:ghc_type = ''
|
||||
let l:text = l:match[4][:0] is# ' ' ? l:match[4][1:] : l:match[4]
|
||||
endif
|
||||
|
||||
if l:ghc_type is? 'Warning'
|
||||
let l:type = 'W'
|
||||
else
|
||||
let l:type = 'E'
|
||||
endif
|
||||
|
||||
" Replace temporary filenames in problem messages with the basename
|
||||
let l:text = substitute(l:text, l:temp_filename_regex, l:basename, 'g')
|
||||
|
||||
let l:item = {
|
||||
\ 'lnum': l:match[2] + 0,
|
||||
\ 'col': l:match[3] + 0,
|
||||
\ 'text': l:text,
|
||||
\ 'type': l:type,
|
||||
\}
|
||||
|
||||
" Include extra lines as details if they are there.
|
||||
if len(l:line_list) > 1
|
||||
let l:item.detail = join(l:line_list[1:], "\n")
|
||||
endif
|
||||
|
||||
call add(l:output, l:item)
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
@ -0,0 +1,17 @@
|
||||
" Author: Ty-Lucas Kelley <tylucaskelley@gmail.com>
|
||||
" Description: Adds support for markdownlint
|
||||
|
||||
function! ale#handlers#markdownlint#Handle(buffer, lines) abort
|
||||
let l:pattern=': \(\d*\): \(MD\d\{3}\)\(\/\)\([A-Za-z0-9-]\+\)\(.*\)$'
|
||||
let l:output=[]
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'text': '(' . l:match[2] . l:match[3] . l:match[4] . ')' . l:match[5],
|
||||
\ 'type': 'W',
|
||||
\ })
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
25
sources_non_forked/ale/autoload/ale/handlers/ols.vim
Normal file
25
sources_non_forked/ale/autoload/ale/handlers/ols.vim
Normal file
@ -0,0 +1,25 @@
|
||||
" Author: Michael Jungo <michaeljungo92@gmail.com>
|
||||
" Description: Handlers for the OCaml language server
|
||||
|
||||
function! ale#handlers#ols#GetExecutable(buffer) abort
|
||||
let l:ols_setting = ale#handlers#ols#GetLanguage(a:buffer) . '_ols'
|
||||
return ale#node#FindExecutable(a:buffer, l:ols_setting, [
|
||||
\ 'node_modules/.bin/ocaml-language-server',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#ols#GetCommand(buffer) abort
|
||||
let l:executable = ale#handlers#ols#GetExecutable(a:buffer)
|
||||
|
||||
return ale#node#Executable(a:buffer, l:executable) . ' --stdio'
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#ols#GetLanguage(buffer) abort
|
||||
return getbufvar(a:buffer, '&filetype')
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#ols#GetProjectRoot(buffer) abort
|
||||
let l:merlin_file = ale#path#FindNearestFile(a:buffer, '.merlin')
|
||||
|
||||
return !empty(l:merlin_file) ? fnamemodify(l:merlin_file, ':h') : ''
|
||||
endfunction
|
34
sources_non_forked/ale/autoload/ale/handlers/pony.vim
Normal file
34
sources_non_forked/ale/autoload/ale/handlers/pony.vim
Normal file
@ -0,0 +1,34 @@
|
||||
scriptencoding utf-8
|
||||
" Description: This file defines a handler function which ought to work for
|
||||
" any program which outputs errors in the format that ponyc uses.
|
||||
|
||||
function! s:RemoveUnicodeQuotes(text) abort
|
||||
let l:text = a:text
|
||||
let l:text = substitute(l:text, '[`´‘’]', '''', 'g')
|
||||
let l:text = substitute(l:text, '\v\\u2018([^\\]+)\\u2019', '''\1''', 'g')
|
||||
let l:text = substitute(l:text, '[“”]', '"', 'g')
|
||||
|
||||
return l:text
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#pony#HandlePonycFormat(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
" /home/code/pony/classes/Wombat.pony:22:30: can't lookup private fields from outside the type
|
||||
|
||||
let l:pattern = '\v^([^:]+):(\d+):(\d+)?:? (.+)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
let l:item = {
|
||||
\ 'filename': l:match[1],
|
||||
\ 'lnum': str2nr(l:match[2]),
|
||||
\ 'col': str2nr(l:match[3]),
|
||||
\ 'type': 'E',
|
||||
\ 'text': s:RemoveUnicodeQuotes(l:match[4]),
|
||||
\}
|
||||
|
||||
call add(l:output, l:item)
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
@ -0,0 +1,6 @@
|
||||
call ale#Set('ruby_rails_best_practices_options', '')
|
||||
call ale#Set('ruby_rails_best_practices_executable', 'rails_best_practices')
|
||||
|
||||
function! ale#handlers#rails_best_practices#GetExecutable(buffer) abort
|
||||
return ale#Var(a:buffer, 'ruby_rails_best_practices_executable')
|
||||
endfunction
|
56
sources_non_forked/ale/autoload/ale/handlers/redpen.vim
Normal file
56
sources_non_forked/ale/autoload/ale/handlers/redpen.vim
Normal file
@ -0,0 +1,56 @@
|
||||
" Author: rhysd https://rhysd.github.io
|
||||
" Description: Redpen, a proofreading tool (http://redpen.cc)
|
||||
|
||||
function! ale#handlers#redpen#HandleRedpenOutput(buffer, lines) abort
|
||||
" Only one file was passed to redpen. So response array has only one
|
||||
" element.
|
||||
let l:res = json_decode(join(a:lines))[0]
|
||||
let l:output = []
|
||||
for l:err in l:res.errors
|
||||
let l:item = {
|
||||
\ 'text': l:err.message,
|
||||
\ 'type': 'W',
|
||||
\ 'code': l:err.validator,
|
||||
\}
|
||||
if has_key(l:err, 'startPosition')
|
||||
let l:item.lnum = l:err.startPosition.lineNum
|
||||
let l:item.col = l:err.startPosition.offset + 1
|
||||
if has_key(l:err, 'endPosition')
|
||||
let l:item.end_lnum = l:err.endPosition.lineNum
|
||||
let l:item.end_col = l:err.endPosition.offset
|
||||
endif
|
||||
else
|
||||
" Fallback to a whole sentence region when a region is not
|
||||
" specified by the error.
|
||||
let l:item.lnum = l:err.lineNum
|
||||
let l:item.col = l:err.sentenceStartColumnNum + 1
|
||||
endif
|
||||
|
||||
" Adjust column number for multibyte string
|
||||
let l:line = getline(l:item.lnum)
|
||||
if l:line is# ''
|
||||
let l:line = l:err.sentence
|
||||
endif
|
||||
let l:line = split(l:line, '\zs')
|
||||
|
||||
if l:item.col >= 2
|
||||
let l:col = 0
|
||||
for l:strlen in map(l:line[0:(l:item.col - 2)], 'strlen(v:val)')
|
||||
let l:col = l:col + l:strlen
|
||||
endfor
|
||||
let l:item.col = l:col + 1
|
||||
endif
|
||||
|
||||
if has_key(l:item, 'end_col')
|
||||
let l:col = 0
|
||||
for l:strlen in map(l:line[0:(l:item.end_col - 1)], 'strlen(v:val)')
|
||||
let l:col = l:col + l:strlen
|
||||
endfor
|
||||
let l:item.end_col = l:col
|
||||
endif
|
||||
|
||||
call add(l:output, l:item)
|
||||
endfor
|
||||
return l:output
|
||||
endfunction
|
||||
|
6
sources_non_forked/ale/autoload/ale/handlers/rubocop.vim
Normal file
6
sources_non_forked/ale/autoload/ale/handlers/rubocop.vim
Normal file
@ -0,0 +1,6 @@
|
||||
call ale#Set('ruby_rubocop_options', '')
|
||||
call ale#Set('ruby_rubocop_executable', 'rubocop')
|
||||
|
||||
function! ale#handlers#rubocop#GetExecutable(buffer) abort
|
||||
return ale#Var(a:buffer, 'ruby_rubocop_executable')
|
||||
endfunction
|
37
sources_non_forked/ale/autoload/ale/handlers/ruby.vim
Normal file
37
sources_non_forked/ale/autoload/ale/handlers/ruby.vim
Normal file
@ -0,0 +1,37 @@
|
||||
" Author: Brandon Roehl - https://github.com/BrandonRoehl, Matthias Guenther https://wikimatze.de
|
||||
"
|
||||
" Description: This file implements handlers specific to Ruby.
|
||||
|
||||
function! s:HandleSyntaxError(buffer, lines) abort
|
||||
" Matches patterns line the following:
|
||||
"
|
||||
" test.rb:3: warning: parentheses after method name is interpreted as an argument list, not a decomposed argument
|
||||
" test.rb:8: syntax error, unexpected keyword_end, expecting end-of-input
|
||||
let l:pattern = '\v^.+:(\d+): (warning: )?(.+)$'
|
||||
let l:column = '\v^(\s+)\^$'
|
||||
let l:output = []
|
||||
|
||||
for l:line in a:lines
|
||||
let l:match = matchlist(l:line, l:pattern)
|
||||
if len(l:match) == 0
|
||||
let l:match = matchlist(l:line, l:column)
|
||||
if len(l:match) != 0
|
||||
let l:output[len(l:output) - 1]['col'] = len(l:match[1])
|
||||
endif
|
||||
else
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': 0,
|
||||
\ 'text': l:match[2] . l:match[3],
|
||||
\ 'type': empty(l:match[2]) ? 'E' : 'W',
|
||||
\})
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#ruby#HandleSyntaxErrors(buffer, lines) abort
|
||||
return s:HandleSyntaxError(a:buffer, a:lines)
|
||||
endfunction
|
||||
|
64
sources_non_forked/ale/autoload/ale/handlers/rust.vim
Normal file
64
sources_non_forked/ale/autoload/ale/handlers/rust.vim
Normal file
@ -0,0 +1,64 @@
|
||||
" Author: Daniel Schemala <istjanichtzufassen@gmail.com>,
|
||||
" w0rp <devw0rp@gmail.com>
|
||||
"
|
||||
" Description: This file implements handlers specific to Rust.
|
||||
|
||||
if !exists('g:ale_rust_ignore_error_codes')
|
||||
let g:ale_rust_ignore_error_codes = []
|
||||
endif
|
||||
|
||||
function! s:FindSpan(buffer, span) abort
|
||||
if ale#path#IsBufferPath(a:buffer, a:span.file_name) || a:span.file_name is# '<anon>'
|
||||
return a:span
|
||||
endif
|
||||
|
||||
" Search inside the expansion of an error, as the problem for this buffer
|
||||
" could lie inside a nested object.
|
||||
if !empty(get(a:span, 'expansion', v:null))
|
||||
return s:FindSpan(a:buffer, a:span.expansion.span)
|
||||
endif
|
||||
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#rust#HandleRustErrors(buffer, lines) abort
|
||||
let l:output = []
|
||||
|
||||
for l:errorline in a:lines
|
||||
" ignore everything that is not JSON
|
||||
if l:errorline !~# '^{'
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:error = json_decode(l:errorline)
|
||||
|
||||
if has_key(l:error, 'message') && type(l:error.message) == type({})
|
||||
let l:error = l:error.message
|
||||
endif
|
||||
|
||||
if !has_key(l:error, 'code')
|
||||
continue
|
||||
endif
|
||||
|
||||
if !empty(l:error.code) && index(g:ale_rust_ignore_error_codes, l:error.code.code) > -1
|
||||
continue
|
||||
endif
|
||||
|
||||
for l:root_span in l:error.spans
|
||||
let l:span = s:FindSpan(a:buffer, l:root_span)
|
||||
|
||||
if !empty(l:span)
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:span.line_start,
|
||||
\ 'end_lnum': l:span.line_end,
|
||||
\ 'col': l:span.column_start,
|
||||
\ 'end_col': l:span.column_end,
|
||||
\ 'text': empty(l:span.label) ? l:error.message : printf('%s: %s', l:error.message, l:span.label),
|
||||
\ 'type': toupper(l:error.level[0]),
|
||||
\})
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
20
sources_non_forked/ale/autoload/ale/handlers/sh.vim
Normal file
20
sources_non_forked/ale/autoload/ale/handlers/sh.vim
Normal file
@ -0,0 +1,20 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
|
||||
" Get the shell type for a buffer, based on the hashbang line.
|
||||
function! ale#handlers#sh#GetShellType(buffer) abort
|
||||
let l:bang_line = get(getbufline(a:buffer, 1), 0, '')
|
||||
|
||||
" Take the shell executable from the hashbang, if we can.
|
||||
if l:bang_line[:1] is# '#!'
|
||||
" Remove options like -e, etc.
|
||||
let l:command = substitute(l:bang_line, ' --\?[a-zA-Z0-9]\+', '', 'g')
|
||||
|
||||
for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'sh']
|
||||
if l:command =~# l:possible_shell . '\s*$'
|
||||
return l:possible_shell
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
92
sources_non_forked/ale/autoload/ale/handlers/sml.vim
Normal file
92
sources_non_forked/ale/autoload/ale/handlers/sml.vim
Normal file
@ -0,0 +1,92 @@
|
||||
" Author: Jake Zimmerman <jake@zimmerman.io>
|
||||
" Description: Shared functions for SML linters
|
||||
|
||||
" The glob to use for finding the .cm file.
|
||||
"
|
||||
" See :help ale-sml-smlnj for more information.
|
||||
call ale#Set('sml_smlnj_cm_file', '*.cm')
|
||||
|
||||
function! ale#handlers#sml#GetCmFile(buffer) abort
|
||||
let l:pattern = ale#Var(a:buffer, 'sml_smlnj_cm_file')
|
||||
let l:as_list = 1
|
||||
|
||||
let l:cmfile = ''
|
||||
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
|
||||
let l:results = glob(l:path . '/' . l:pattern, 0, l:as_list)
|
||||
if len(l:results) > 0
|
||||
" If there is more than one CM file, we take the first one
|
||||
" See :help ale-sml-smlnj for how to configure this.
|
||||
let l:cmfile = l:results[0]
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:cmfile
|
||||
endfunction
|
||||
|
||||
" Only one of smlnj or smlnj-cm can be enabled at a time.
|
||||
" executable_callback is called before *every* lint attempt
|
||||
function! s:GetExecutable(buffer, source) abort
|
||||
if ale#handlers#sml#GetCmFile(a:buffer) is# ''
|
||||
" No CM file found; only allow single-file mode to be enabled
|
||||
if a:source is# 'smlnj-file'
|
||||
return 'sml'
|
||||
elseif a:source is# 'smlnj-cm'
|
||||
return ''
|
||||
endif
|
||||
else
|
||||
" Found a CM file; only allow cm-file mode to be enabled
|
||||
if a:source is# 'smlnj-file'
|
||||
return ''
|
||||
elseif a:source is# 'smlnj-cm'
|
||||
return 'sml'
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#sml#GetExecutableSmlnjCm(buffer) abort
|
||||
return s:GetExecutable(a:buffer, 'smlnj-cm')
|
||||
endfunction
|
||||
function! ale#handlers#sml#GetExecutableSmlnjFile(buffer) abort
|
||||
return s:GetExecutable(a:buffer, 'smlnj-file')
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#sml#Handle(buffer, lines) abort
|
||||
" Try to match basic sml errors
|
||||
" TODO(jez) We can get better errorfmt strings from Syntastic
|
||||
|
||||
let l:out = []
|
||||
let l:pattern = '^.*\:\([0-9\.]\+\)\ \(\w\+\)\:\ \(.*\)'
|
||||
let l:pattern2 = '^.*\:\([0-9]\+\)\.\?\([0-9]\+\).* \(\(Warning\|Error\): .*\)'
|
||||
|
||||
for l:line in a:lines
|
||||
let l:match2 = matchlist(l:line, l:pattern2)
|
||||
|
||||
if len(l:match2) != 0
|
||||
call add(l:out, {
|
||||
\ 'bufnr': a:buffer,
|
||||
\ 'lnum': l:match2[1] + 0,
|
||||
\ 'col' : l:match2[2] - 1,
|
||||
\ 'text': l:match2[3],
|
||||
\ 'type': l:match2[3] =~# '^Warning' ? 'W' : 'E',
|
||||
\})
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:match = matchlist(l:line, l:pattern)
|
||||
|
||||
if len(l:match) != 0
|
||||
call add(l:out, {
|
||||
\ 'bufnr': a:buffer,
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'text': l:match[2] . ': ' . l:match[3],
|
||||
\ 'type': l:match[2] is# 'error' ? 'E' : 'W',
|
||||
\})
|
||||
continue
|
||||
endif
|
||||
|
||||
endfor
|
||||
|
||||
return l:out
|
||||
endfunction
|
||||
|
||||
" vim:ts=4:sts=4:sw=4
|
39
sources_non_forked/ale/autoload/ale/handlers/textlint.vim
Normal file
39
sources_non_forked/ale/autoload/ale/handlers/textlint.vim
Normal file
@ -0,0 +1,39 @@
|
||||
" Author: tokida https://rouger.info, Yasuhiro Kiyota <yasuhiroki.duck@gmail.com>
|
||||
" Description: textlint, a proofreading tool (https://textlint.github.io/)
|
||||
|
||||
call ale#Set('textlint_executable', 'textlint')
|
||||
call ale#Set('textlint_use_global', 0)
|
||||
call ale#Set('textlint_options', '')
|
||||
|
||||
function! ale#handlers#textlint#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'textlint', [
|
||||
\ 'node_modules/.bin/textlint',
|
||||
\ 'node_modules/textlint/bin/textlint.js',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#textlint#GetCommand(buffer) abort
|
||||
let l:executable = ale#handlers#textlint#GetExecutable(a:buffer)
|
||||
let l:options = ale#Var(a:buffer, 'textlint_options')
|
||||
|
||||
return ale#node#Executable(a:buffer, l:executable)
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' -f json --stdin --stdin-filename %s'
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#textlint#HandleTextlintOutput(buffer, lines) abort
|
||||
let l:res = get(ale#util#FuzzyJSONDecode(a:lines, []), 0, {'messages': []})
|
||||
let l:output = []
|
||||
|
||||
for l:err in l:res.messages
|
||||
call add(l:output, {
|
||||
\ 'text': l:err.message,
|
||||
\ 'type': 'W',
|
||||
\ 'code': l:err.ruleId,
|
||||
\ 'lnum': l:err.line,
|
||||
\ 'col' : l:err.column
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
26
sources_non_forked/ale/autoload/ale/handlers/unix.vim
Normal file
26
sources_non_forked/ale/autoload/ale/handlers/unix.vim
Normal file
@ -0,0 +1,26 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Error handling for errors in a Unix format.
|
||||
|
||||
function! s:HandleUnixFormat(buffer, lines, type) abort
|
||||
let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):?(\d+)?:? ?(.+)$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:match[1] + 0,
|
||||
\ 'col': l:match[2] + 0,
|
||||
\ 'text': l:match[3],
|
||||
\ 'type': a:type,
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#unix#HandleAsError(buffer, lines) abort
|
||||
return s:HandleUnixFormat(a:buffer, a:lines, 'E')
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#unix#HandleAsWarning(buffer, lines) abort
|
||||
return s:HandleUnixFormat(a:buffer, a:lines, 'W')
|
||||
endfunction
|
38
sources_non_forked/ale/autoload/ale/handlers/vale.vim
Normal file
38
sources_non_forked/ale/autoload/ale/handlers/vale.vim
Normal file
@ -0,0 +1,38 @@
|
||||
" Author: Johannes Wienke <languitar@semipol.de>
|
||||
" Description: output handler for the vale JSON format
|
||||
|
||||
function! ale#handlers#vale#GetType(severity) abort
|
||||
if a:severity is? 'warning'
|
||||
return 'W'
|
||||
elseif a:severity is? 'suggestion'
|
||||
return 'I'
|
||||
endif
|
||||
|
||||
return 'E'
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#vale#Handle(buffer, lines) abort
|
||||
try
|
||||
let l:errors = json_decode(join(a:lines, ''))
|
||||
catch
|
||||
return []
|
||||
endtry
|
||||
|
||||
if empty(l:errors)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:output = []
|
||||
for l:error in l:errors[keys(l:errors)[0]]
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:error['Line'],
|
||||
\ 'col': l:error['Span'][0],
|
||||
\ 'end_col': l:error['Span'][1],
|
||||
\ 'code': l:error['Check'],
|
||||
\ 'text': l:error['Message'],
|
||||
\ 'type': ale#handlers#vale#GetType(l:error['Severity']),
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
61
sources_non_forked/ale/autoload/ale/handlers/writegood.vim
Normal file
61
sources_non_forked/ale/autoload/ale/handlers/writegood.vim
Normal file
@ -0,0 +1,61 @@
|
||||
" Author: Sumner Evans <sumner.evans98@gmail.com>
|
||||
" Description: Error handling for errors in the write-good format.
|
||||
|
||||
function! ale#handlers#writegood#ResetOptions() abort
|
||||
call ale#Set('writegood_options', '')
|
||||
call ale#Set('writegood_executable', 'write-good')
|
||||
call ale#Set('writegood_use_global', 0)
|
||||
endfunction
|
||||
|
||||
" Reset the options so the tests can test how they are set.
|
||||
call ale#handlers#writegood#ResetOptions()
|
||||
|
||||
function! ale#handlers#writegood#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'writegood', [
|
||||
\ 'node_modules/.bin/write-good',
|
||||
\ 'node_modules/write-good/bin/write-good.js',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#writegood#GetCommand(buffer) abort
|
||||
let l:executable = ale#handlers#writegood#GetExecutable(a:buffer)
|
||||
let l:options = ale#Var(a:buffer, 'writegood_options')
|
||||
|
||||
return ale#node#Executable(a:buffer, l:executable)
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' %t'
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#writegood#Handle(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
"
|
||||
" "it is" is wordy or unneeded on line 20 at column 53
|
||||
" "easily" can weaken meaning on line 154 at column 29
|
||||
let l:marks_pattern = '\v^ *(\^+) *$'
|
||||
let l:pattern = '\v^(".*"\s.*)\son\sline\s(\d+)\sat\scolumn\s(\d+)$'
|
||||
let l:output = []
|
||||
let l:last_len = 0
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, [l:marks_pattern, l:pattern])
|
||||
if empty(l:match[2])
|
||||
let l:last_len = len(l:match[1])
|
||||
else
|
||||
let l:col = l:match[3] + 1
|
||||
|
||||
" Add the linter error. Note that we need to add 1 to the col because
|
||||
" write-good reports the column corresponding to the space before the
|
||||
" offending word or phrase.
|
||||
call add(l:output, {
|
||||
\ 'text': l:match[1],
|
||||
\ 'lnum': l:match[2] + 0,
|
||||
\ 'col': l:col,
|
||||
\ 'end_col': l:last_len ? (l:col + l:last_len - 1) : l:col,
|
||||
\ 'type': 'W',
|
||||
\})
|
||||
|
||||
let l:last_len = 0
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
143
sources_non_forked/ale/autoload/ale/highlight.vim
Normal file
143
sources_non_forked/ale/autoload/ale/highlight.vim
Normal file
@ -0,0 +1,143 @@
|
||||
scriptencoding utf8
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: This module implements error/warning highlighting.
|
||||
|
||||
if !hlexists('ALEError')
|
||||
highlight link ALEError SpellBad
|
||||
endif
|
||||
|
||||
if !hlexists('ALEStyleError')
|
||||
highlight link ALEStyleError ALEError
|
||||
endif
|
||||
|
||||
if !hlexists('ALEWarning')
|
||||
highlight link ALEWarning SpellCap
|
||||
endif
|
||||
|
||||
if !hlexists('ALEStyleWarning')
|
||||
highlight link ALEStyleWarning ALEWarning
|
||||
endif
|
||||
|
||||
if !hlexists('ALEInfo')
|
||||
highlight link ALEInfo ALEWarning
|
||||
endif
|
||||
|
||||
" The maximum number of items for the second argument of matchaddpos()
|
||||
let s:MAX_POS_VALUES = 8
|
||||
let s:MAX_COL_SIZE = 1073741824 " pow(2, 30)
|
||||
|
||||
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.
|
||||
return [[[a:line, a:col, a:end_col - a:col + 1]]]
|
||||
endif
|
||||
|
||||
" Get positions from the first line at the first column, up to a large
|
||||
" integer for highlighting up to the end of the line, followed by
|
||||
" the lines in-between, for highlighting entire lines, and
|
||||
" a highlight for the last line, up to the end column.
|
||||
let l:all_positions =
|
||||
\ [[a:line, a:col, s:MAX_COL_SIZE]]
|
||||
\ + range(a:line + 1, a:end_line - 1)
|
||||
\ + [[a:end_line, 1, a:end_col]]
|
||||
|
||||
return map(
|
||||
\ range(0, len(l:all_positions) - 1, s:MAX_POS_VALUES),
|
||||
\ 'l:all_positions[v:val : v:val + s:MAX_POS_VALUES - 1]',
|
||||
\)
|
||||
endfunction
|
||||
|
||||
" Given a loclist for current items to highlight, remove all highlights
|
||||
" except these which have matching loclist item entries.
|
||||
function! ale#highlight#RemoveHighlights() abort
|
||||
for l:match in getmatches()
|
||||
if l:match.group =~# '^ALE'
|
||||
call matchdelete(l:match.id)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! ale#highlight#UpdateHighlights() abort
|
||||
let l:item_list = get(b:, 'ale_enabled', 1) && g:ale_enabled
|
||||
\ ? get(b:, 'ale_highlight_items', [])
|
||||
\ : []
|
||||
|
||||
call ale#highlight#RemoveHighlights()
|
||||
|
||||
for l:item in l:item_list
|
||||
if l:item.type is# 'W'
|
||||
if get(l:item, 'sub_type', '') is# 'style'
|
||||
let l:group = 'ALEStyleWarning'
|
||||
else
|
||||
let l:group = 'ALEWarning'
|
||||
endif
|
||||
elseif l:item.type is# 'I'
|
||||
let l:group = 'ALEInfo'
|
||||
elseif get(l:item, 'sub_type', '') is# 'style'
|
||||
let l:group = 'ALEStyleError'
|
||||
else
|
||||
let l:group = 'ALEError'
|
||||
endif
|
||||
|
||||
let l:line = l:item.lnum
|
||||
let l:col = l:item.col
|
||||
let l:end_line = get(l:item, 'end_lnum', l:line)
|
||||
let l:end_col = get(l:item, 'end_col', l:col)
|
||||
|
||||
" Set all of the positions, which are chunked into Lists which
|
||||
" are as large as will be accepted by matchaddpos.
|
||||
call map(
|
||||
\ ale#highlight#CreatePositions(l:line, l:col, l:end_line, l:end_col),
|
||||
\ 'matchaddpos(l:group, v:val)'
|
||||
\)
|
||||
endfor
|
||||
|
||||
" If highlights are enabled and signs are not enabled, we should still
|
||||
" offer line highlights by adding a separate set of highlights.
|
||||
if !g:ale_set_signs
|
||||
let l:available_groups = {
|
||||
\ 'ALEWarningLine': hlexists('ALEWarningLine'),
|
||||
\ 'ALEInfoLine': hlexists('ALEInfoLine'),
|
||||
\ 'ALEErrorLine': hlexists('ALEErrorLine'),
|
||||
\}
|
||||
|
||||
for l:item in l:item_list
|
||||
if l:item.type is# 'W'
|
||||
let l:group = 'ALEWarningLine'
|
||||
elseif l:item.type is# 'I'
|
||||
let l:group = 'ALEInfoLine'
|
||||
else
|
||||
let l:group = 'ALEErrorLine'
|
||||
endif
|
||||
|
||||
if l:available_groups[l:group]
|
||||
call matchaddpos(l:group, [l:item.lnum])
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#highlight#BufferHidden(buffer) abort
|
||||
" Remove highlights right away when buffers are hidden.
|
||||
" They will be restored later when buffers are entered.
|
||||
call ale#highlight#RemoveHighlights()
|
||||
endfunction
|
||||
|
||||
augroup ALEHighlightBufferGroup
|
||||
autocmd!
|
||||
autocmd BufEnter * call ale#highlight#UpdateHighlights()
|
||||
autocmd BufHidden * call ale#highlight#BufferHidden(expand('<abuf>'))
|
||||
augroup END
|
||||
|
||||
function! ale#highlight#SetHighlights(buffer, loclist) abort
|
||||
let l:new_list = getbufvar(a:buffer, 'ale_enabled', 1) && g:ale_enabled
|
||||
\ ? filter(copy(a:loclist), 'v:val.bufnr == a:buffer && v:val.col > 0')
|
||||
\ : []
|
||||
|
||||
" Set the list in the buffer variable.
|
||||
call setbufvar(str2nr(a:buffer), 'ale_highlight_items', l:new_list)
|
||||
|
||||
" Update highlights for the current buffer, which may or may not
|
||||
" be the buffer we just set highlights for.
|
||||
call ale#highlight#UpdateHighlights()
|
||||
endfunction
|
59
sources_non_forked/ale/autoload/ale/history.vim
Normal file
59
sources_non_forked/ale/autoload/ale/history.vim
Normal file
@ -0,0 +1,59 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Tools for managing command history
|
||||
|
||||
" Return a shallow copy of the command history for a given buffer number.
|
||||
function! ale#history#Get(buffer) abort
|
||||
return copy(getbufvar(a:buffer, 'ale_history', []))
|
||||
endfunction
|
||||
|
||||
function! ale#history#Add(buffer, status, job_id, command) abort
|
||||
if g:ale_max_buffer_history_size <= 0
|
||||
" Don't save anything if the history isn't a positive number.
|
||||
call setbufvar(a:buffer, 'ale_history', [])
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
let l:history = getbufvar(a:buffer, 'ale_history', [])
|
||||
|
||||
" Remove the first item if we hit the max history size.
|
||||
if len(l:history) >= g:ale_max_buffer_history_size
|
||||
let l:history = l:history[1:]
|
||||
endif
|
||||
|
||||
call add(l:history, {
|
||||
\ 'status': a:status,
|
||||
\ 'job_id': a:job_id,
|
||||
\ 'command': a:command,
|
||||
\})
|
||||
|
||||
call setbufvar(a:buffer, 'ale_history', l:history)
|
||||
endfunction
|
||||
|
||||
function! s:FindHistoryItem(buffer, job_id) abort
|
||||
" Search backwards to find a matching job ID. IDs might be recycled,
|
||||
" so finding the last one should be good enough.
|
||||
for l:obj in reverse(ale#history#Get(a:buffer))
|
||||
if l:obj.job_id == a:job_id
|
||||
return l:obj
|
||||
endif
|
||||
endfor
|
||||
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
" Set an exit code for a command which finished.
|
||||
function! ale#history#SetExitCode(buffer, job_id, exit_code) abort
|
||||
let l:obj = s:FindHistoryItem(a:buffer, a:job_id)
|
||||
|
||||
" If we find a match, then set the code and status.
|
||||
let l:obj.exit_code = a:exit_code
|
||||
let l:obj.status = 'finished'
|
||||
endfunction
|
||||
|
||||
" Set the output for a command which finished.
|
||||
function! ale#history#RememberOutput(buffer, job_id, output) abort
|
||||
let l:obj = s:FindHistoryItem(a:buffer, a:job_id)
|
||||
|
||||
let l:obj.output = a:output
|
||||
endfunction
|
341
sources_non_forked/ale/autoload/ale/job.vim
Normal file
341
sources_non_forked/ale/autoload/ale/job.vim
Normal file
@ -0,0 +1,341 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: APIs for working with Asynchronous jobs, with an API normalised
|
||||
" between Vim 8 and NeoVim.
|
||||
"
|
||||
" Important functions are described below. They are:
|
||||
"
|
||||
" ale#job#Start(command, options) -> job_id
|
||||
" ale#job#IsRunning(job_id) -> 1 if running, 0 otherwise.
|
||||
" ale#job#Stop(job_id)
|
||||
|
||||
if !has_key(s:, 'job_map')
|
||||
let s:job_map = {}
|
||||
endif
|
||||
|
||||
" A map from timer IDs to jobs, for tracking jobs that need to be killed
|
||||
" with SIGKILL if they don't terminate right away.
|
||||
if !has_key(s:, 'job_kill_timers')
|
||||
let s:job_kill_timers = {}
|
||||
endif
|
||||
|
||||
function! s:KillHandler(timer) abort
|
||||
let l:job = remove(s:job_kill_timers, a:timer)
|
||||
call job_stop(l:job, 'kill')
|
||||
endfunction
|
||||
|
||||
" Note that jobs and IDs are the same thing on NeoVim.
|
||||
function! ale#job#JoinNeovimOutput(job, last_line, data, mode, callback) abort
|
||||
if a:mode is# 'raw'
|
||||
call a:callback(a:job, join(a:data, "\n"))
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:lines = a:data[:-2]
|
||||
|
||||
if len(a:data) > 1
|
||||
let l:lines[0] = a:last_line . l:lines[0]
|
||||
let l:new_last_line = a:data[-1]
|
||||
else
|
||||
let l:new_last_line = a:last_line . get(a:data, 0, '')
|
||||
endif
|
||||
|
||||
for l:line in l:lines
|
||||
call a:callback(a:job, l:line)
|
||||
endfor
|
||||
|
||||
return l:new_last_line
|
||||
endfunction
|
||||
|
||||
function! s:NeoVimCallback(job, data, event) abort
|
||||
let l:info = s:job_map[a:job]
|
||||
|
||||
if a:event is# 'stdout'
|
||||
let l:info.out_cb_line = ale#job#JoinNeovimOutput(
|
||||
\ a:job,
|
||||
\ l:info.out_cb_line,
|
||||
\ a:data,
|
||||
\ l:info.mode,
|
||||
\ ale#util#GetFunction(l:info.out_cb),
|
||||
\)
|
||||
elseif a:event is# 'stderr'
|
||||
let l:info.err_cb_line = ale#job#JoinNeovimOutput(
|
||||
\ a:job,
|
||||
\ l:info.err_cb_line,
|
||||
\ a:data,
|
||||
\ l:info.mode,
|
||||
\ ale#util#GetFunction(l:info.err_cb),
|
||||
\)
|
||||
else
|
||||
if has_key(l:info, 'out_cb') && !empty(l:info.out_cb_line)
|
||||
call ale#util#GetFunction(l:info.out_cb)(a:job, l:info.out_cb_line)
|
||||
endif
|
||||
|
||||
if has_key(l:info, 'err_cb') && !empty(l:info.err_cb_line)
|
||||
call ale#util#GetFunction(l:info.err_cb)(a:job, l:info.err_cb_line)
|
||||
endif
|
||||
|
||||
try
|
||||
call ale#util#GetFunction(l:info.exit_cb)(a:job, a:data)
|
||||
finally
|
||||
" Automatically forget about the job after it's done.
|
||||
if has_key(s:job_map, a:job)
|
||||
call remove(s:job_map, a:job)
|
||||
endif
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:VimOutputCallback(channel, data) abort
|
||||
let l:job = ch_getjob(a:channel)
|
||||
let l:job_id = ale#job#ParseVim8ProcessID(string(l:job))
|
||||
|
||||
" Only call the callbacks for jobs which are valid.
|
||||
if l:job_id > 0 && has_key(s:job_map, l:job_id)
|
||||
call ale#util#GetFunction(s:job_map[l:job_id].out_cb)(l:job_id, a:data)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:VimErrorCallback(channel, data) abort
|
||||
let l:job = ch_getjob(a:channel)
|
||||
let l:job_id = ale#job#ParseVim8ProcessID(string(l:job))
|
||||
|
||||
" Only call the callbacks for jobs which are valid.
|
||||
if l:job_id > 0 && has_key(s:job_map, l:job_id)
|
||||
call ale#util#GetFunction(s:job_map[l:job_id].err_cb)(l:job_id, a:data)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:VimCloseCallback(channel) abort
|
||||
let l:job = ch_getjob(a:channel)
|
||||
let l:job_id = ale#job#ParseVim8ProcessID(string(l:job))
|
||||
let l:info = get(s:job_map, l:job_id, {})
|
||||
|
||||
if empty(l:info)
|
||||
return
|
||||
endif
|
||||
|
||||
" job_status() can trigger the exit handler.
|
||||
" The channel can close before the job has exited.
|
||||
if job_status(l:job) is# 'dead'
|
||||
try
|
||||
if !empty(l:info) && has_key(l:info, 'exit_cb')
|
||||
call ale#util#GetFunction(l:info.exit_cb)(l:job_id, l:info.exit_code)
|
||||
endif
|
||||
finally
|
||||
" Automatically forget about the job after it's done.
|
||||
if has_key(s:job_map, l:job_id)
|
||||
call remove(s:job_map, l:job_id)
|
||||
endif
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:VimExitCallback(job, exit_code) abort
|
||||
let l:job_id = ale#job#ParseVim8ProcessID(string(a:job))
|
||||
let l:info = get(s:job_map, l:job_id, {})
|
||||
|
||||
if empty(l:info)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:info.exit_code = a:exit_code
|
||||
|
||||
" The program can exit before the data has finished being read.
|
||||
if ch_status(job_getchannel(a:job)) is# 'closed'
|
||||
try
|
||||
if !empty(l:info) && has_key(l:info, 'exit_cb')
|
||||
call ale#util#GetFunction(l:info.exit_cb)(l:job_id, a:exit_code)
|
||||
endif
|
||||
finally
|
||||
" Automatically forget about the job after it's done.
|
||||
if has_key(s:job_map, l:job_id)
|
||||
call remove(s:job_map, l:job_id)
|
||||
endif
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#job#ParseVim8ProcessID(job_string) abort
|
||||
return matchstr(a:job_string, '\d\+') + 0
|
||||
endfunction
|
||||
|
||||
function! ale#job#ValidateArguments(command, options) abort
|
||||
if a:options.mode isnot# 'nl' && a:options.mode isnot# 'raw'
|
||||
throw 'Invalid mode: ' . a:options.mode
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:PrepareWrappedCommand(original_wrapper, command) abort
|
||||
let l:match = matchlist(a:command, '\v^(.*(\&\&|;)) *(.*)$')
|
||||
let l:prefix = ''
|
||||
let l:command = a:command
|
||||
|
||||
if !empty(l:match)
|
||||
let l:prefix = l:match[1] . ' '
|
||||
let l:command = l:match[3]
|
||||
endif
|
||||
|
||||
let l:format = a:original_wrapper
|
||||
|
||||
if l:format =~# '%@'
|
||||
let l:wrapped = substitute(l:format, '%@', ale#Escape(l:command), '')
|
||||
else
|
||||
if l:format !~# '%\*'
|
||||
let l:format .= ' %*'
|
||||
endif
|
||||
|
||||
let l:wrapped = substitute(l:format, '%\*', l:command, '')
|
||||
endif
|
||||
|
||||
return l:prefix . l:wrapped
|
||||
endfunction
|
||||
|
||||
function! ale#job#PrepareCommand(buffer, command) abort
|
||||
let l:wrapper = ale#Var(a:buffer, 'command_wrapper')
|
||||
|
||||
let l:command = !empty(l:wrapper)
|
||||
\ ? s:PrepareWrappedCommand(l:wrapper, a:command)
|
||||
\ : a:command
|
||||
|
||||
" The command will be executed in a subshell. This fixes a number of
|
||||
" issues, including reading the PATH variables correctly, %PATHEXT%
|
||||
" expansion on Windows, etc.
|
||||
"
|
||||
" NeoVim handles this issue automatically if the command is a String,
|
||||
" but we'll do this explicitly, so we use the same exact command for both
|
||||
" versions.
|
||||
if has('win32')
|
||||
return 'cmd /s/c "' . l:command . '"'
|
||||
endif
|
||||
|
||||
if &shell =~? 'fish$'
|
||||
return ['/bin/sh', '-c', l:command]
|
||||
endif
|
||||
|
||||
return split(&shell) + split(&shellcmdflag) + [l:command]
|
||||
endfunction
|
||||
|
||||
" Start a job with options which are agnostic to Vim and NeoVim.
|
||||
"
|
||||
" The following options are accepted:
|
||||
"
|
||||
" out_cb - A callback for receiving stdin. Arguments: (job_id, data)
|
||||
" err_cb - A callback for receiving stderr. Arguments: (job_id, data)
|
||||
" exit_cb - A callback for program exit. Arguments: (job_id, status_code)
|
||||
" mode - A mode for I/O. Can be 'nl' for split lines or 'raw'.
|
||||
function! ale#job#Start(command, options) abort
|
||||
call ale#job#ValidateArguments(a:command, a:options)
|
||||
|
||||
let l:job_info = copy(a:options)
|
||||
let l:job_options = {}
|
||||
|
||||
if has('nvim')
|
||||
if has_key(a:options, 'out_cb')
|
||||
let l:job_options.on_stdout = function('s:NeoVimCallback')
|
||||
let l:job_info.out_cb_line = ''
|
||||
endif
|
||||
|
||||
if has_key(a:options, 'err_cb')
|
||||
let l:job_options.on_stderr = function('s:NeoVimCallback')
|
||||
let l:job_info.err_cb_line = ''
|
||||
endif
|
||||
|
||||
if has_key(a:options, 'exit_cb')
|
||||
let l:job_options.on_exit = function('s:NeoVimCallback')
|
||||
endif
|
||||
|
||||
let l:job_info.job = jobstart(a:command, l:job_options)
|
||||
let l:job_id = l:job_info.job
|
||||
else
|
||||
let l:job_options = {
|
||||
\ 'in_mode': l:job_info.mode,
|
||||
\ 'out_mode': l:job_info.mode,
|
||||
\ 'err_mode': l:job_info.mode,
|
||||
\}
|
||||
|
||||
if has_key(a:options, 'out_cb')
|
||||
let l:job_options.out_cb = function('s:VimOutputCallback')
|
||||
endif
|
||||
|
||||
if has_key(a:options, 'err_cb')
|
||||
let l:job_options.err_cb = function('s:VimErrorCallback')
|
||||
endif
|
||||
|
||||
if has_key(a:options, 'exit_cb')
|
||||
" Set a close callback to which simply calls job_status()
|
||||
" when the channel is closed, which can trigger the exit callback
|
||||
" earlier on.
|
||||
let l:job_options.close_cb = function('s:VimCloseCallback')
|
||||
let l:job_options.exit_cb = function('s:VimExitCallback')
|
||||
endif
|
||||
|
||||
" Vim 8 will read the stdin from the file's buffer.
|
||||
let l:job_info.job = job_start(a:command, l:job_options)
|
||||
let l:job_id = ale#job#ParseVim8ProcessID(string(l:job_info.job))
|
||||
endif
|
||||
|
||||
if l:job_id > 0
|
||||
" Store the job in the map for later only if we can get the ID.
|
||||
let s:job_map[l:job_id] = l:job_info
|
||||
endif
|
||||
|
||||
return l:job_id
|
||||
endfunction
|
||||
|
||||
" Send raw data to the job.
|
||||
function! ale#job#SendRaw(job_id, string) abort
|
||||
if has('nvim')
|
||||
call jobsend(a:job_id, a:string)
|
||||
else
|
||||
call ch_sendraw(job_getchannel(s:job_map[a:job_id].job), a:string)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Given a job ID, return 1 if the job is currently running.
|
||||
" Invalid job IDs will be ignored.
|
||||
function! ale#job#IsRunning(job_id) abort
|
||||
if has('nvim')
|
||||
try
|
||||
" In NeoVim, if the job isn't running, jobpid() will throw.
|
||||
call jobpid(a:job_id)
|
||||
return 1
|
||||
catch
|
||||
endtry
|
||||
elseif has_key(s:job_map, a:job_id)
|
||||
let l:job = s:job_map[a:job_id].job
|
||||
return job_status(l:job) is# 'run'
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Given a Job ID, stop that job.
|
||||
" Invalid job IDs will be ignored.
|
||||
function! ale#job#Stop(job_id) abort
|
||||
if !has_key(s:job_map, a:job_id)
|
||||
return
|
||||
endif
|
||||
|
||||
if has('nvim')
|
||||
" FIXME: NeoVim kills jobs on a timer, but will not kill any processes
|
||||
" which are child processes on Unix. Some work needs to be done to
|
||||
" kill child processes to stop long-running processes like pylint.
|
||||
silent! call jobstop(a:job_id)
|
||||
else
|
||||
let l:job = s:job_map[a:job_id].job
|
||||
|
||||
" We must close the channel for reading the buffer if it is open
|
||||
" when stopping a job. Otherwise, we will get errors in the status line.
|
||||
if ch_status(job_getchannel(l:job)) is# 'open'
|
||||
call ch_close_in(job_getchannel(l:job))
|
||||
endif
|
||||
|
||||
" Ask nicely for the job to stop.
|
||||
call job_stop(l:job)
|
||||
|
||||
if ale#job#IsRunning(l:job)
|
||||
" Set a 100ms delay for killing the job with SIGKILL.
|
||||
let s:job_kill_timers[timer_start(100, function('s:KillHandler'))] = l:job
|
||||
endif
|
||||
endif
|
||||
endfunction
|
474
sources_non_forked/ale/autoload/ale/linter.vim
Normal file
474
sources_non_forked/ale/autoload/ale/linter.vim
Normal file
@ -0,0 +1,474 @@
|
||||
call ale#Set('wrap_command_as_one_argument', 0)
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Linter registration and lazy-loading
|
||||
" Retrieves linters as requested by the engine, loading them if needed.
|
||||
|
||||
let s:linters = {}
|
||||
|
||||
" Default filetype aliases.
|
||||
" The user defined aliases will be merged with this Dictionary.
|
||||
let s:default_ale_linter_aliases = {
|
||||
\ 'Dockerfile': 'dockerfile',
|
||||
\ 'csh': 'sh',
|
||||
\ 'plaintex': 'tex',
|
||||
\ 'systemverilog': 'verilog',
|
||||
\ 'zsh': 'sh',
|
||||
\}
|
||||
|
||||
" Default linters to run for particular filetypes.
|
||||
" The user defined linter selections will be merged with this Dictionary.
|
||||
"
|
||||
" No linters are used for plaintext files by default.
|
||||
"
|
||||
" Only cargo is enabled for Rust by default.
|
||||
" rpmlint is disabled by default because it can result in code execution.
|
||||
"
|
||||
" NOTE: Update the g:ale_linters documentation when modifying this.
|
||||
let s:default_ale_linters = {
|
||||
\ 'csh': ['shell'],
|
||||
\ 'go': ['gofmt', 'golint', 'go vet'],
|
||||
\ 'help': [],
|
||||
\ 'perl': ['perlcritic'],
|
||||
\ 'python': ['flake8', 'mypy', 'pylint'],
|
||||
\ 'rust': ['cargo'],
|
||||
\ 'spec': [],
|
||||
\ 'text': [],
|
||||
\ 'zsh': ['shell'],
|
||||
\}
|
||||
|
||||
" Testing/debugging helper to unload all linters.
|
||||
function! ale#linter#Reset() abort
|
||||
let s:linters = {}
|
||||
endfunction
|
||||
|
||||
function! s:IsCallback(value) abort
|
||||
return type(a:value) == type('') || type(a:value) == type(function('type'))
|
||||
endfunction
|
||||
|
||||
function! s:IsBoolean(value) abort
|
||||
return type(a:value) == type(0) && (a:value == 0 || a:value == 1)
|
||||
endfunction
|
||||
|
||||
function! ale#linter#PreProcess(linter) abort
|
||||
if type(a:linter) != type({})
|
||||
throw 'The linter object must be a Dictionary'
|
||||
endif
|
||||
|
||||
let l:obj = {
|
||||
\ 'add_newline': get(a:linter, 'add_newline', 0),
|
||||
\ 'name': get(a:linter, 'name'),
|
||||
\ 'lsp': get(a:linter, 'lsp', ''),
|
||||
\}
|
||||
|
||||
if type(l:obj.name) != type('')
|
||||
throw '`name` must be defined to name the linter'
|
||||
endif
|
||||
|
||||
let l:needs_address = l:obj.lsp is# 'socket'
|
||||
let l:needs_executable = l:obj.lsp isnot# 'socket'
|
||||
let l:needs_command = l:obj.lsp isnot# 'socket'
|
||||
let l:needs_lsp_details = !empty(l:obj.lsp)
|
||||
|
||||
if empty(l:obj.lsp)
|
||||
let l:obj.callback = get(a:linter, 'callback')
|
||||
|
||||
if !s:IsCallback(l:obj.callback)
|
||||
throw '`callback` must be defined with a callback to accept output'
|
||||
endif
|
||||
endif
|
||||
|
||||
if index(['', 'socket', 'stdio', 'tsserver'], l:obj.lsp) < 0
|
||||
throw '`lsp` must be either `''lsp''` or `''tsserver''` if defined'
|
||||
endif
|
||||
|
||||
if !l:needs_executable
|
||||
if has_key(a:linter, 'executable')
|
||||
\|| has_key(a:linter, 'executable_callback')
|
||||
throw '`executable` and `executable_callback` cannot be used when lsp == ''socket'''
|
||||
endif
|
||||
elseif has_key(a:linter, 'executable_callback')
|
||||
let l:obj.executable_callback = a:linter.executable_callback
|
||||
|
||||
if !s:IsCallback(l:obj.executable_callback)
|
||||
throw '`executable_callback` must be a callback if defined'
|
||||
endif
|
||||
elseif has_key(a:linter, 'executable')
|
||||
let l:obj.executable = a:linter.executable
|
||||
|
||||
if type(l:obj.executable) != type('')
|
||||
throw '`executable` must be a string if defined'
|
||||
endif
|
||||
else
|
||||
throw 'Either `executable` or `executable_callback` must be defined'
|
||||
endif
|
||||
|
||||
if !l:needs_command
|
||||
if has_key(a:linter, 'command')
|
||||
\|| has_key(a:linter, 'command_callback')
|
||||
\|| has_key(a:linter, 'command_chain')
|
||||
throw '`command` and `command_callback` and `command_chain` cannot be used when lsp == ''socket'''
|
||||
endif
|
||||
elseif has_key(a:linter, 'command_chain')
|
||||
let l:obj.command_chain = a:linter.command_chain
|
||||
|
||||
if type(l:obj.command_chain) != type([])
|
||||
throw '`command_chain` must be a List'
|
||||
endif
|
||||
|
||||
if empty(l:obj.command_chain)
|
||||
throw '`command_chain` must contain at least one item'
|
||||
endif
|
||||
|
||||
let l:link_index = 0
|
||||
|
||||
for l:link in l:obj.command_chain
|
||||
let l:err_prefix = 'The `command_chain` item ' . l:link_index . ' '
|
||||
|
||||
if !s:IsCallback(get(l:link, 'callback'))
|
||||
throw l:err_prefix . 'must define a `callback` function'
|
||||
endif
|
||||
|
||||
if has_key(l:link, 'output_stream')
|
||||
if type(l:link.output_stream) != type('')
|
||||
\|| index(['stdout', 'stderr', 'both'], l:link.output_stream) < 0
|
||||
throw l:err_prefix . '`output_stream` flag must be '
|
||||
\ . "'stdout', 'stderr', or 'both'"
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(l:link, 'read_buffer') && !s:IsBoolean(l:link.read_buffer)
|
||||
throw l:err_prefix . 'value for `read_buffer` must be `0` or `1`'
|
||||
endif
|
||||
|
||||
let l:link_index += 1
|
||||
endfor
|
||||
elseif has_key(a:linter, 'command_callback')
|
||||
let l:obj.command_callback = a:linter.command_callback
|
||||
|
||||
if !s:IsCallback(l:obj.command_callback)
|
||||
throw '`command_callback` must be a callback if defined'
|
||||
endif
|
||||
elseif has_key(a:linter, 'command')
|
||||
let l:obj.command = a:linter.command
|
||||
|
||||
if type(l:obj.command) != type('')
|
||||
throw '`command` must be a string if defined'
|
||||
endif
|
||||
else
|
||||
throw 'Either `command`, `executable_callback`, `command_chain` '
|
||||
\ . 'must be defined'
|
||||
endif
|
||||
|
||||
if (
|
||||
\ has_key(a:linter, 'command')
|
||||
\ + has_key(a:linter, 'command_chain')
|
||||
\ + has_key(a:linter, 'command_callback')
|
||||
\) > 1
|
||||
throw 'Only one of `command`, `command_callback`, or `command_chain` '
|
||||
\ . 'should be set'
|
||||
endif
|
||||
|
||||
if !l:needs_address
|
||||
if has_key(a:linter, 'address_callback')
|
||||
throw '`address_callback` cannot be used when lsp != ''socket'''
|
||||
endif
|
||||
elseif has_key(a:linter, 'address_callback')
|
||||
let l:obj.address_callback = a:linter.address_callback
|
||||
|
||||
if !s:IsCallback(l:obj.address_callback)
|
||||
throw '`address_callback` must be a callback if defined'
|
||||
endif
|
||||
else
|
||||
throw '`address_callback` must be defined for getting the LSP address'
|
||||
endif
|
||||
|
||||
if l:needs_lsp_details
|
||||
let l:obj.language_callback = get(a:linter, 'language_callback')
|
||||
|
||||
if !s:IsCallback(l:obj.language_callback)
|
||||
throw '`language_callback` must be a callback for LSP linters'
|
||||
endif
|
||||
|
||||
let l:obj.project_root_callback = get(a:linter, 'project_root_callback')
|
||||
|
||||
if !s:IsCallback(l:obj.project_root_callback)
|
||||
throw '`project_root_callback` must be a callback for LSP linters'
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:obj.output_stream = get(a:linter, 'output_stream', 'stdout')
|
||||
|
||||
if type(l:obj.output_stream) != type('')
|
||||
\|| index(['stdout', 'stderr', 'both'], l:obj.output_stream) < 0
|
||||
throw "`output_stream` must be 'stdout', 'stderr', or 'both'"
|
||||
endif
|
||||
|
||||
" An option indicating that this linter should only be run against the
|
||||
" file on disk.
|
||||
let l:obj.lint_file = get(a:linter, 'lint_file', 0)
|
||||
|
||||
if !s:IsBoolean(l:obj.lint_file)
|
||||
throw '`lint_file` must be `0` or `1`'
|
||||
endif
|
||||
|
||||
" An option indicating that the buffer should be read.
|
||||
let l:obj.read_buffer = get(a:linter, 'read_buffer', !l:obj.lint_file)
|
||||
|
||||
if !s:IsBoolean(l:obj.read_buffer)
|
||||
throw '`read_buffer` must be `0` or `1`'
|
||||
endif
|
||||
|
||||
if l:obj.lint_file && l:obj.read_buffer
|
||||
throw 'Only one of `lint_file` or `read_buffer` can be `1`'
|
||||
endif
|
||||
|
||||
let l:obj.aliases = get(a:linter, 'aliases', [])
|
||||
|
||||
if type(l:obj.aliases) != type([])
|
||||
\|| len(filter(copy(l:obj.aliases), 'type(v:val) != type('''')')) > 0
|
||||
throw '`aliases` must be a List of String values'
|
||||
endif
|
||||
|
||||
return l:obj
|
||||
endfunction
|
||||
|
||||
function! ale#linter#Define(filetype, linter) abort
|
||||
if !has_key(s:linters, a:filetype)
|
||||
let s:linters[a:filetype] = []
|
||||
endif
|
||||
|
||||
let l:new_linter = ale#linter#PreProcess(a:linter)
|
||||
|
||||
call add(s:linters[a:filetype], l:new_linter)
|
||||
endfunction
|
||||
|
||||
function! ale#linter#GetAll(filetypes) abort
|
||||
let l:combined_linters = []
|
||||
|
||||
for l:filetype in a:filetypes
|
||||
" Load linter defintions from files if we haven't loaded them yet.
|
||||
if !has_key(s:linters, l:filetype)
|
||||
execute 'silent! runtime! ale_linters/' . l:filetype . '/*.vim'
|
||||
|
||||
" Always set an empty List for the loaded linters if we don't find
|
||||
" any. This will prevent us from executing the runtime command
|
||||
" many times, redundantly.
|
||||
if !has_key(s:linters, l:filetype)
|
||||
let s:linters[l:filetype] = []
|
||||
endif
|
||||
endif
|
||||
|
||||
call extend(l:combined_linters, get(s:linters, l:filetype, []))
|
||||
endfor
|
||||
|
||||
return l:combined_linters
|
||||
endfunction
|
||||
|
||||
function! s:GetAliasedFiletype(original_filetype) abort
|
||||
let l:buffer_aliases = get(b:, 'ale_linter_aliases', {})
|
||||
|
||||
" b:ale_linter_aliases can be set to a List.
|
||||
if type(l:buffer_aliases) is type([])
|
||||
return l:buffer_aliases
|
||||
endif
|
||||
|
||||
" Check for aliased filetypes first in a buffer variable,
|
||||
" then the global variable,
|
||||
" then in the default mapping,
|
||||
" otherwise use the original filetype.
|
||||
for l:dict in [
|
||||
\ l:buffer_aliases,
|
||||
\ g:ale_linter_aliases,
|
||||
\ s:default_ale_linter_aliases,
|
||||
\]
|
||||
if has_key(l:dict, a:original_filetype)
|
||||
return l:dict[a:original_filetype]
|
||||
endif
|
||||
endfor
|
||||
|
||||
return a:original_filetype
|
||||
endfunction
|
||||
|
||||
function! ale#linter#ResolveFiletype(original_filetype) abort
|
||||
let l:filetype = s:GetAliasedFiletype(a:original_filetype)
|
||||
|
||||
if type(l:filetype) != type([])
|
||||
return [l:filetype]
|
||||
endif
|
||||
|
||||
return l:filetype
|
||||
endfunction
|
||||
|
||||
function! s:GetLinterNames(original_filetype) abort
|
||||
let l:buffer_ale_linters = get(b:, 'ale_linters', {})
|
||||
|
||||
" b:ale_linters can be set to 'all'
|
||||
if l:buffer_ale_linters is# 'all'
|
||||
return 'all'
|
||||
endif
|
||||
|
||||
" b:ale_linters can be set to a List.
|
||||
if type(l:buffer_ale_linters) is type([])
|
||||
return l:buffer_ale_linters
|
||||
endif
|
||||
|
||||
" Try to get a buffer-local setting for the filetype
|
||||
if has_key(l:buffer_ale_linters, a:original_filetype)
|
||||
return l:buffer_ale_linters[a:original_filetype]
|
||||
endif
|
||||
|
||||
" Try to get a global setting for the filetype
|
||||
if has_key(g:ale_linters, a:original_filetype)
|
||||
return g:ale_linters[a:original_filetype]
|
||||
endif
|
||||
|
||||
" If the user has configured ALE to only enable linters explicitly, then
|
||||
" don't enable any linters by default.
|
||||
if g:ale_linters_explicit
|
||||
return []
|
||||
endif
|
||||
|
||||
" Try to get a default setting for the filetype
|
||||
if has_key(s:default_ale_linters, a:original_filetype)
|
||||
return s:default_ale_linters[a:original_filetype]
|
||||
endif
|
||||
|
||||
return 'all'
|
||||
endfunction
|
||||
|
||||
function! ale#linter#Get(original_filetypes) abort
|
||||
let l:possibly_duplicated_linters = []
|
||||
|
||||
" Handle dot-separated filetypes.
|
||||
for l:original_filetype in split(a:original_filetypes, '\.')
|
||||
let l:filetype = ale#linter#ResolveFiletype(l:original_filetype)
|
||||
let l:linter_names = s:GetLinterNames(l:original_filetype)
|
||||
let l:all_linters = ale#linter#GetAll(l:filetype)
|
||||
let l:filetype_linters = []
|
||||
|
||||
if type(l:linter_names) == type('') && l:linter_names is# 'all'
|
||||
let l:filetype_linters = l:all_linters
|
||||
elseif type(l:linter_names) == type([])
|
||||
" Select only the linters we or the user has specified.
|
||||
for l:linter in l:all_linters
|
||||
let l:name_list = [l:linter.name] + l:linter.aliases
|
||||
|
||||
for l:name in l:name_list
|
||||
if index(l:linter_names, l:name) >= 0
|
||||
call add(l:filetype_linters, l:linter)
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
endif
|
||||
|
||||
call extend(l:possibly_duplicated_linters, l:filetype_linters)
|
||||
endfor
|
||||
|
||||
let l:name_list = []
|
||||
let l:combined_linters = []
|
||||
|
||||
" Make sure we override linters so we don't get two with the same name,
|
||||
" like 'eslint' for both 'javascript' and 'typescript'
|
||||
"
|
||||
" Note that the reverse calls here modify the List variables.
|
||||
for l:linter in reverse(l:possibly_duplicated_linters)
|
||||
if index(l:name_list, l:linter.name) < 0
|
||||
call add(l:name_list, l:linter.name)
|
||||
call add(l:combined_linters, l:linter)
|
||||
endif
|
||||
endfor
|
||||
|
||||
return reverse(l:combined_linters)
|
||||
endfunction
|
||||
|
||||
" Given a buffer and linter, get the executable String for the linter.
|
||||
function! ale#linter#GetExecutable(buffer, linter) abort
|
||||
return has_key(a:linter, 'executable_callback')
|
||||
\ ? ale#util#GetFunction(a:linter.executable_callback)(a:buffer)
|
||||
\ : a:linter.executable
|
||||
endfunction
|
||||
|
||||
" Given a buffer and linter, get the command String for the linter.
|
||||
" The command_chain key is not supported.
|
||||
function! ale#linter#GetCommand(buffer, linter) abort
|
||||
return has_key(a:linter, 'command_callback')
|
||||
\ ? ale#util#GetFunction(a:linter.command_callback)(a:buffer)
|
||||
\ : a:linter.command
|
||||
endfunction
|
||||
|
||||
" Given a buffer and linter, get the address for connecting to the server.
|
||||
function! ale#linter#GetAddress(buffer, linter) abort
|
||||
return has_key(a:linter, 'address_callback')
|
||||
\ ? ale#util#GetFunction(a:linter.address_callback)(a:buffer)
|
||||
\ : a:linter.address
|
||||
endfunction
|
||||
|
||||
" Given a buffer, an LSP linter, and a callback to register for handling
|
||||
" messages, start up an LSP linter and get ready to receive errors or
|
||||
" completions.
|
||||
function! ale#linter#StartLSP(buffer, linter, callback) abort
|
||||
let l:command = ''
|
||||
let l:address = ''
|
||||
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
|
||||
|
||||
if empty(l:root) && a:linter.lsp isnot# 'tsserver'
|
||||
" If there's no project root, then we can't check files with LSP,
|
||||
" unless we are using tsserver, which doesn't use project roots.
|
||||
return {}
|
||||
endif
|
||||
|
||||
if a:linter.lsp is# 'socket'
|
||||
let l:address = ale#linter#GetAddress(a:buffer, a:linter)
|
||||
let l:conn_id = ale#lsp#ConnectToAddress(
|
||||
\ l:address,
|
||||
\ l:root,
|
||||
\ a:callback,
|
||||
\)
|
||||
else
|
||||
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
|
||||
|
||||
if !executable(l:executable)
|
||||
return {}
|
||||
endif
|
||||
|
||||
let l:command = ale#job#PrepareCommand(
|
||||
\ a:buffer,
|
||||
\ ale#linter#GetCommand(a:buffer, a:linter),
|
||||
\)
|
||||
let l:conn_id = ale#lsp#StartProgram(
|
||||
\ l:executable,
|
||||
\ l:command,
|
||||
\ l:root,
|
||||
\ a:callback,
|
||||
\)
|
||||
endif
|
||||
|
||||
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
|
||||
|
||||
if !l:conn_id
|
||||
if g:ale_history_enabled && !empty(l:command)
|
||||
call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command)
|
||||
endif
|
||||
|
||||
return {}
|
||||
endif
|
||||
|
||||
if ale#lsp#OpenDocumentIfNeeded(l:conn_id, a:buffer, l:root, l:language_id)
|
||||
if g:ale_history_enabled && !empty(l:command)
|
||||
call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
|
||||
endif
|
||||
endif
|
||||
|
||||
" The change message needs to be sent for tsserver before doing anything.
|
||||
if a:linter.lsp is# 'tsserver'
|
||||
call ale#lsp#Send(l:conn_id, ale#lsp#tsserver_message#Change(a:buffer))
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'connection_id': l:conn_id,
|
||||
\ 'command': l:command,
|
||||
\ 'project_root': l:root,
|
||||
\ 'language_id': l:language_id,
|
||||
\}
|
||||
endfunction
|
177
sources_non_forked/ale/autoload/ale/list.vim
Normal file
177
sources_non_forked/ale/autoload/ale/list.vim
Normal file
@ -0,0 +1,177 @@
|
||||
" Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
|
||||
" Description: Manages the loclist and quickfix lists
|
||||
|
||||
if !exists('s:timer_args')
|
||||
let s:timer_args = {}
|
||||
endif
|
||||
|
||||
" Return 1 if there is a buffer with buftype == 'quickfix' in bufffer list
|
||||
function! ale#list#IsQuickfixOpen() abort
|
||||
for l:buf in range(1, bufnr('$'))
|
||||
if getbufvar(l:buf, '&buftype') is# 'quickfix'
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Check if we should open the list, based on the save event being fired, and
|
||||
" that setting being on, or the setting just being set to `1`.
|
||||
function! s:ShouldOpen(buffer) abort
|
||||
let l:val = ale#Var(a:buffer, 'open_list')
|
||||
let l:saved = getbufvar(a:buffer, 'ale_save_event_fired', 0)
|
||||
|
||||
return l:val is 1 || (l:val is# 'on_save' && l:saved)
|
||||
endfunction
|
||||
|
||||
function! ale#list#GetCombinedList() abort
|
||||
let l:list = []
|
||||
|
||||
for l:info in values(g:ale_buffer_info)
|
||||
call extend(l:list, l:info.loclist)
|
||||
endfor
|
||||
|
||||
call sort(l:list, function('ale#util#LocItemCompareWithText'))
|
||||
call uniq(l:list, function('ale#util#LocItemCompareWithText'))
|
||||
|
||||
return l:list
|
||||
endfunction
|
||||
|
||||
function! s:FixList(buffer, list) abort
|
||||
let l:format = ale#Var(a:buffer, 'loclist_msg_format')
|
||||
let l:new_list = []
|
||||
|
||||
for l:item in a:list
|
||||
let l:fixed_item = copy(l:item)
|
||||
|
||||
let l:fixed_item.text = ale#GetLocItemMessage(l:item, l:format)
|
||||
|
||||
if l:item.bufnr == -1
|
||||
" If the buffer number is invalid, remove it.
|
||||
call remove(l:fixed_item, 'bufnr')
|
||||
endif
|
||||
|
||||
call add(l:new_list, l:fixed_item)
|
||||
endfor
|
||||
|
||||
return l:new_list
|
||||
endfunction
|
||||
|
||||
function! s:BufWinId(buffer) abort
|
||||
return exists('*bufwinid') ? bufwinid(str2nr(a:buffer)) : 0
|
||||
endfunction
|
||||
|
||||
function! s:SetListsImpl(timer_id, buffer, loclist) abort
|
||||
let l:title = expand('#' . a:buffer . ':p')
|
||||
|
||||
if g:ale_set_quickfix
|
||||
let l:quickfix_list = ale#list#GetCombinedList()
|
||||
|
||||
if has('nvim')
|
||||
call setqflist(s:FixList(a:buffer, l:quickfix_list), ' ', l:title)
|
||||
else
|
||||
call setqflist(s:FixList(a:buffer, l:quickfix_list))
|
||||
call setqflist([], 'r', {'title': l:title})
|
||||
endif
|
||||
elseif g:ale_set_loclist
|
||||
" If windows support is off, bufwinid() may not exist.
|
||||
" We'll set result in the current window, which might not be correct,
|
||||
" but it's better than nothing.
|
||||
let l:id = s:BufWinId(a:buffer)
|
||||
|
||||
if has('nvim')
|
||||
call setloclist(l:id, s:FixList(a:buffer, a:loclist), ' ', l:title)
|
||||
else
|
||||
call setloclist(l:id, s:FixList(a:buffer, a:loclist))
|
||||
call setloclist(l:id, [], 'r', {'title': l:title})
|
||||
endif
|
||||
endif
|
||||
|
||||
" 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
|
||||
" window will only be opened if the current buffer has problems.
|
||||
if s:ShouldOpen(a:buffer) && !empty(a:loclist)
|
||||
let l:winnr = winnr()
|
||||
let l:mode = mode()
|
||||
let l:reset_visual_selection = l:mode is? 'v' || l:mode is# "\<c-v>"
|
||||
let l:reset_character_selection = l:mode is? 's' || l:mode is# "\<c-s>"
|
||||
|
||||
" open windows vertically instead of default horizontally
|
||||
let l:open_type = ''
|
||||
if ale#Var(a:buffer, 'list_vertical') == 1
|
||||
let l:open_type = 'vert '
|
||||
endif
|
||||
if g:ale_set_quickfix
|
||||
if !ale#list#IsQuickfixOpen()
|
||||
silent! execute l:open_type . 'copen ' . str2nr(ale#Var(a:buffer, 'list_window_size'))
|
||||
endif
|
||||
elseif g:ale_set_loclist
|
||||
silent! execute l:open_type . 'lopen ' . str2nr(ale#Var(a:buffer, 'list_window_size'))
|
||||
endif
|
||||
|
||||
" If focus changed, restore it (jump to the last window).
|
||||
if l:winnr isnot# winnr()
|
||||
wincmd p
|
||||
endif
|
||||
|
||||
if l:reset_visual_selection || l:reset_character_selection
|
||||
" If we were in a selection mode before, select the last selection.
|
||||
normal! gv
|
||||
|
||||
if l:reset_character_selection
|
||||
" Switch back to Select mode, if we were in that.
|
||||
normal! "\<c-g>"
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
" If ALE isn't currently checking for more problems, close the window if
|
||||
" needed now. This check happens inside of this timer function, so
|
||||
" the window can be closed reliably.
|
||||
if !ale#engine#IsCheckingBuffer(a:buffer)
|
||||
call s:CloseWindowIfNeeded(a:buffer)
|
||||
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)
|
||||
" Update lists immediately if running a test synchronously, or if the
|
||||
" buffer was saved.
|
||||
"
|
||||
" The lists need to be updated immediately when saving a buffer so
|
||||
" that we can reliably close window automatically, if so configured.
|
||||
call s:SetListsImpl(-1, a:buffer, a:loclist)
|
||||
else
|
||||
call ale#util#StartPartialTimer(
|
||||
\ 0,
|
||||
\ function('s:SetListsImpl'),
|
||||
\ [a:buffer, a:loclist],
|
||||
\)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:CloseWindowIfNeeded(buffer) abort
|
||||
if ale#Var(a:buffer, 'keep_list_window_open') || !s:ShouldOpen(a:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
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
|
||||
endif
|
||||
else
|
||||
let l:win_id = s:BufWinId(a:buffer)
|
||||
|
||||
if g:ale_set_loclist && empty(getloclist(l:win_id))
|
||||
lclose
|
||||
endif
|
||||
endif
|
||||
" Ignore 'Cannot close last window' errors.
|
||||
catch /E444/
|
||||
endtry
|
||||
endfunction
|
87
sources_non_forked/ale/autoload/ale/loclist_jumping.vim
Normal file
87
sources_non_forked/ale/autoload/ale/loclist_jumping.vim
Normal file
@ -0,0 +1,87 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: This file implements functions for jumping around in a file
|
||||
" based on ALE's internal loclist.
|
||||
|
||||
" Search for the nearest line either before or after the current position
|
||||
" in the loclist. The argument 'wrap' can be passed to enable wrapping
|
||||
" around the end of the list.
|
||||
"
|
||||
" If there are no items or we have hit the end with wrapping off, an empty
|
||||
" List will be returned, otherwise a pair of [line_number, column_number] will
|
||||
" be returned.
|
||||
function! ale#loclist_jumping#FindNearest(direction, wrap) abort
|
||||
let l:buffer = bufnr('')
|
||||
let l:pos = getcurpos()
|
||||
let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []})
|
||||
" Copy the list and filter to only the items in this buffer.
|
||||
let l:loclist = filter(copy(l:info.loclist), 'v:val.bufnr == l:buffer')
|
||||
let l:search_item = {'bufnr': l:buffer, 'lnum': l:pos[1], 'col': l:pos[2]}
|
||||
|
||||
" When searching backwards, so we can find the next smallest match.
|
||||
if a:direction is# 'before'
|
||||
call reverse(l:loclist)
|
||||
endif
|
||||
|
||||
" Look for items before or after the current position.
|
||||
for l:item in l:loclist
|
||||
" Compare the cursor with a item where the column number is bounded,
|
||||
" such that it's possible for the cursor to actually be on the given
|
||||
" column number, without modifying the cursor number we return. This
|
||||
" will allow us to move through matches, but still let us move the
|
||||
" cursor to a line without changing the column, in some cases.
|
||||
let l:cmp_value = ale#util#LocItemCompare(
|
||||
\ {
|
||||
\ 'bufnr': l:buffer,
|
||||
\ 'lnum': l:item.lnum,
|
||||
\ 'col': min([
|
||||
\ max([l:item.col, 1]),
|
||||
\ max([len(getline(l:item.lnum)), 1]),
|
||||
\ ]),
|
||||
\ },
|
||||
\ l:search_item
|
||||
\)
|
||||
|
||||
if a:direction is# 'before' && l:cmp_value < 0
|
||||
return [l:item.lnum, l:item.col]
|
||||
endif
|
||||
|
||||
if a:direction is# 'after' && l:cmp_value > 0
|
||||
return [l:item.lnum, l:item.col]
|
||||
endif
|
||||
endfor
|
||||
|
||||
" If we found nothing, and the wrap option is set to 1, then we should
|
||||
" wrap around the list of warnings/errors
|
||||
if a:wrap && !empty(l:loclist)
|
||||
let l:item = l:loclist[0]
|
||||
|
||||
return [l:item.lnum, l:item.col]
|
||||
endif
|
||||
|
||||
return []
|
||||
endfunction
|
||||
|
||||
" As before, find the nearest match, but position the cursor at it.
|
||||
function! ale#loclist_jumping#Jump(direction, wrap) abort
|
||||
let l:nearest = ale#loclist_jumping#FindNearest(a:direction, a:wrap)
|
||||
|
||||
if !empty(l:nearest)
|
||||
call cursor(l:nearest)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#loclist_jumping#JumpToIndex(index) abort
|
||||
let l:buffer = bufnr('')
|
||||
let l:info = get(g:ale_buffer_info, l:buffer, {'loclist': []})
|
||||
let l:loclist = filter(copy(l:info.loclist), 'v:val.bufnr == l:buffer')
|
||||
|
||||
if empty(l:loclist)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:item = l:loclist[a:index]
|
||||
|
||||
if !empty(l:item)
|
||||
call cursor([l:item.lnum, l:item.col])
|
||||
endif
|
||||
endfunction
|
420
sources_non_forked/ale/autoload/ale/lsp.vim
Normal file
420
sources_non_forked/ale/autoload/ale/lsp.vim
Normal file
@ -0,0 +1,420 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Language Server Protocol client code
|
||||
|
||||
" A List of connections, used for tracking servers which have been connected
|
||||
" to, and programs which are run.
|
||||
let s:connections = []
|
||||
let g:ale_lsp_next_message_id = 1
|
||||
|
||||
function! s:NewConnection() abort
|
||||
" id: The job ID as a Number, or the server address as a string.
|
||||
" data: The message data received so far.
|
||||
" executable: An executable only set for program connections.
|
||||
" open_documents: A list of buffers we told the server we opened.
|
||||
" callback_list: A list of callbacks for handling LSP responses.
|
||||
let l:conn = {
|
||||
\ 'id': '',
|
||||
\ 'data': '',
|
||||
\ 'projects': {},
|
||||
\ 'open_documents': [],
|
||||
\ 'callback_list': [],
|
||||
\}
|
||||
|
||||
call add(s:connections, l:conn)
|
||||
|
||||
return l:conn
|
||||
endfunction
|
||||
|
||||
function! s:FindConnection(key, value) abort
|
||||
for l:conn in s:connections
|
||||
if has_key(l:conn, a:key) && get(l:conn, a:key) == a:value
|
||||
return l:conn
|
||||
endif
|
||||
endfor
|
||||
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#GetNextMessageID() abort
|
||||
" Use the current ID
|
||||
let l:id = g:ale_lsp_next_message_id
|
||||
|
||||
" Increment the ID variable.
|
||||
let g:ale_lsp_next_message_id += 1
|
||||
|
||||
" When the ID overflows, reset it to 1. By the time we hit the initial ID
|
||||
" again, the messages will be long gone.
|
||||
if g:ale_lsp_next_message_id < 1
|
||||
let g:ale_lsp_next_message_id = 1
|
||||
endif
|
||||
|
||||
return l:id
|
||||
endfunction
|
||||
|
||||
" TypeScript messages use a different format.
|
||||
function! s:CreateTSServerMessageData(message) abort
|
||||
let l:is_notification = a:message[0]
|
||||
|
||||
let l:obj = {
|
||||
\ 'seq': v:null,
|
||||
\ 'type': 'request',
|
||||
\ 'command': a:message[1][3:],
|
||||
\}
|
||||
|
||||
if !l:is_notification
|
||||
let l:obj.seq = ale#lsp#GetNextMessageID()
|
||||
endif
|
||||
|
||||
if len(a:message) > 2
|
||||
let l:obj.arguments = a:message[2]
|
||||
endif
|
||||
|
||||
let l:data = json_encode(l:obj) . "\n"
|
||||
return [l:is_notification ? 0 : l:obj.seq, l:data]
|
||||
endfunction
|
||||
|
||||
" Given a List of one or two items, [method_name] or [method_name, params],
|
||||
" return a List containing [message_id, message_data]
|
||||
function! ale#lsp#CreateMessageData(message) abort
|
||||
if a:message[1] =~# '^ts@'
|
||||
return s:CreateTSServerMessageData(a:message)
|
||||
endif
|
||||
|
||||
let l:is_notification = a:message[0]
|
||||
|
||||
let l:obj = {
|
||||
\ 'method': a:message[1],
|
||||
\ 'jsonrpc': '2.0',
|
||||
\}
|
||||
|
||||
if !l:is_notification
|
||||
let l:obj.id = ale#lsp#GetNextMessageID()
|
||||
endif
|
||||
|
||||
if len(a:message) > 2
|
||||
let l:obj.params = a:message[2]
|
||||
endif
|
||||
|
||||
let l:body = json_encode(l:obj)
|
||||
let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
|
||||
|
||||
return [l:is_notification ? 0 : l:obj.id, l:data]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#ReadMessageData(data) abort
|
||||
let l:response_list = []
|
||||
let l:remainder = a:data
|
||||
|
||||
while 1
|
||||
" Look for the end of the HTTP headers
|
||||
let l:body_start_index = matchend(l:remainder, "\r\n\r\n")
|
||||
|
||||
if l:body_start_index < 0
|
||||
" No header end was found yet.
|
||||
break
|
||||
endif
|
||||
|
||||
" Parse the Content-Length header.
|
||||
let l:header_data = l:remainder[:l:body_start_index - 4]
|
||||
let l:length_match = matchlist(
|
||||
\ l:header_data,
|
||||
\ '\vContent-Length: *(\d+)'
|
||||
\)
|
||||
|
||||
if empty(l:length_match)
|
||||
throw "Invalid JSON-RPC header:\n" . l:header_data
|
||||
endif
|
||||
|
||||
" Split the body and the remainder of the text.
|
||||
let l:remainder_start_index = l:body_start_index + str2nr(l:length_match[1])
|
||||
|
||||
if len(l:remainder) < l:remainder_start_index
|
||||
" We don't have enough data yet.
|
||||
break
|
||||
endif
|
||||
|
||||
let l:body = l:remainder[l:body_start_index : l:remainder_start_index - 1]
|
||||
let l:remainder = l:remainder[l:remainder_start_index :]
|
||||
|
||||
" Parse the JSON object and add it to the list.
|
||||
call add(l:response_list, json_decode(l:body))
|
||||
endwhile
|
||||
|
||||
return [l:remainder, l:response_list]
|
||||
endfunction
|
||||
|
||||
function! s:FindProjectWithInitRequestID(conn, init_request_id) abort
|
||||
for l:project_root in keys(a:conn.projects)
|
||||
let l:project = a:conn.projects[l:project_root]
|
||||
|
||||
if l:project.init_request_id == a:init_request_id
|
||||
return l:project
|
||||
endif
|
||||
endfor
|
||||
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
function! s:MarkProjectAsInitialized(conn, project) abort
|
||||
let a:project.initialized = 1
|
||||
|
||||
" After the server starts, send messages we had queued previously.
|
||||
for l:message_data in a:project.message_queue
|
||||
call s:SendMessageData(a:conn, l:message_data)
|
||||
endfor
|
||||
|
||||
" Remove the messages now.
|
||||
let a:conn.message_queue = []
|
||||
endfunction
|
||||
|
||||
function! s:HandleInitializeResponse(conn, response) abort
|
||||
let l:request_id = a:response.request_id
|
||||
let l:project = s:FindProjectWithInitRequestID(a:conn, l:request_id)
|
||||
|
||||
if !empty(l:project)
|
||||
call s:MarkProjectAsInitialized(a:conn, l:project)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
|
||||
let l:uninitialized_projects = []
|
||||
|
||||
for [l:key, l:value] in items(a:conn.projects)
|
||||
if l:value.initialized == 0
|
||||
call add(l:uninitialized_projects, [l:key, l:value])
|
||||
endif
|
||||
endfor
|
||||
|
||||
if empty(l:uninitialized_projects)
|
||||
return
|
||||
endif
|
||||
|
||||
if get(a:response, 'method', '') is# ''
|
||||
if has_key(get(a:response, 'result', {}), 'capabilities')
|
||||
for [l:dir, l:project] in l:uninitialized_projects
|
||||
call s:MarkProjectAsInitialized(a:conn, l:project)
|
||||
endfor
|
||||
endif
|
||||
elseif get(a:response, 'method', '') is# 'textDocument/publishDiagnostics'
|
||||
let l:filename = ale#path#FromURI(a:response.params.uri)
|
||||
|
||||
for [l:dir, l:project] in l:uninitialized_projects
|
||||
if l:filename[:len(l:dir) - 1] is# l:dir
|
||||
call s:MarkProjectAsInitialized(a:conn, l:project)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#HandleMessage(conn, message) abort
|
||||
let a:conn.data .= a:message
|
||||
|
||||
" Parse the objects now if we can, and keep the remaining text.
|
||||
let [a:conn.data, l:response_list] = ale#lsp#ReadMessageData(a:conn.data)
|
||||
|
||||
" Call our callbacks.
|
||||
for l:response in l:response_list
|
||||
if get(l:response, 'method', '') is# 'initialize'
|
||||
call s:HandleInitializeResponse(a:conn, l:response)
|
||||
else
|
||||
call ale#lsp#HandleOtherInitializeResponses(a:conn, l:response)
|
||||
|
||||
" Call all of the registered handlers with the response.
|
||||
for l:Callback in a:conn.callback_list
|
||||
call ale#util#GetFunction(l:Callback)(a:conn.id, l:response)
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:HandleChannelMessage(channel, message) abort
|
||||
let l:info = ch_info(a:channel)
|
||||
let l:address = l:info.hostname . l:info.address
|
||||
let l:conn = s:FindConnection('id', l:address)
|
||||
|
||||
call ale#lsp#HandleMessage(l:conn, a:message)
|
||||
endfunction
|
||||
|
||||
function! s:HandleCommandMessage(job_id, message) abort
|
||||
let l:conn = s:FindConnection('id', a:job_id)
|
||||
|
||||
call ale#lsp#HandleMessage(l:conn, a:message)
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#RegisterProject(conn, project_root) abort
|
||||
" Empty strings can't be used for Dictionary keys in NeoVim, due to E713.
|
||||
" This appears to be a nonsensical bug in NeoVim.
|
||||
let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root
|
||||
|
||||
if !has_key(a:conn.projects, l:key)
|
||||
" Tools without project roots are ready right away, like tsserver.
|
||||
let a:conn.projects[l:key] = {
|
||||
\ 'initialized': empty(a:project_root),
|
||||
\ 'init_request_id': 0,
|
||||
\ 'message_queue': [],
|
||||
\}
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#GetProject(conn, project_root) abort
|
||||
let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root
|
||||
|
||||
return get(a:conn.projects, l:key, {})
|
||||
endfunction
|
||||
|
||||
" Start a program for LSP servers which run with executables.
|
||||
"
|
||||
" The job ID will be returned for for the program if it ran, otherwise
|
||||
" 0 will be returned.
|
||||
function! ale#lsp#StartProgram(executable, command, project_root, callback) abort
|
||||
if !executable(a:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:conn = s:FindConnection('executable', a:executable)
|
||||
|
||||
" Get the current connection or a new one.
|
||||
let l:conn = !empty(l:conn) ? l:conn : s:NewConnection()
|
||||
let l:conn.executable = a:executable
|
||||
|
||||
if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id)
|
||||
let l:options = {
|
||||
\ 'mode': 'raw',
|
||||
\ 'out_cb': function('s:HandleCommandMessage'),
|
||||
\}
|
||||
let l:job_id = ale#job#Start(a:command, l:options)
|
||||
else
|
||||
let l:job_id = l:conn.id
|
||||
endif
|
||||
|
||||
if l:job_id <= 0
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:conn.id = l:job_id
|
||||
" Add the callback to the List if it's not there already.
|
||||
call uniq(sort(add(l:conn.callback_list, a:callback)))
|
||||
call ale#lsp#RegisterProject(l:conn, a:project_root)
|
||||
|
||||
return l:job_id
|
||||
endfunction
|
||||
|
||||
" Connect to an address and set up a callback for handling responses.
|
||||
function! ale#lsp#ConnectToAddress(address, project_root, callback) abort
|
||||
let l:conn = s:FindConnection('id', a:address)
|
||||
" Get the current connection or a new one.
|
||||
let l:conn = !empty(l:conn) ? l:conn : s:NewConnection()
|
||||
|
||||
if !has_key(l:conn, 'channel') || ch_status(l:conn.channel) isnot# 'open'
|
||||
let l:conn.channnel = ch_open(a:address, {
|
||||
\ 'mode': 'raw',
|
||||
\ 'waittime': 0,
|
||||
\ 'callback': function('s:HandleChannelMessage'),
|
||||
\})
|
||||
endif
|
||||
|
||||
if ch_status(l:conn.channnel) is# 'fail'
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:conn.id = a:address
|
||||
" Add the callback to the List if it's not there already.
|
||||
call uniq(sort(add(l:conn.callback_list, a:callback)))
|
||||
call ale#lsp#RegisterProject(l:conn, a:project_root)
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
" Stop all LSP connections, closing all jobs and channels, and removing any
|
||||
" queued messages.
|
||||
function! ale#lsp#StopAll() abort
|
||||
for l:conn in s:connections
|
||||
if has_key(l:conn, 'channel')
|
||||
call ch_close(l:conn.channel)
|
||||
else
|
||||
call ale#job#Stop(l:conn.id)
|
||||
endif
|
||||
endfor
|
||||
|
||||
let s:connections = []
|
||||
endfunction
|
||||
|
||||
function! s:SendMessageData(conn, data) abort
|
||||
if has_key(a:conn, 'executable')
|
||||
call ale#job#SendRaw(a:conn.id, a:data)
|
||||
elseif has_key(a:conn, 'channel') && ch_status(a:conn.channnel) is# 'open'
|
||||
" Send the message to the server
|
||||
call ch_sendraw(a:conn.channel, a:data)
|
||||
else
|
||||
return 0
|
||||
endif
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
" Send a message to an LSP server.
|
||||
" Notifications do not need to be handled.
|
||||
"
|
||||
" Returns -1 when a message is sent, but no response is expected
|
||||
" 0 when the message is not sent and
|
||||
" >= 1 with the message ID when a response is expected.
|
||||
function! ale#lsp#Send(conn_id, message, ...) abort
|
||||
let l:project_root = get(a:000, 0, '')
|
||||
|
||||
let l:conn = s:FindConnection('id', a:conn_id)
|
||||
|
||||
if empty(l:conn)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:project = ale#lsp#GetProject(l:conn, l:project_root)
|
||||
|
||||
if empty(l:project)
|
||||
return 0
|
||||
endif
|
||||
|
||||
" If we haven't initialized the server yet, then send the message for it.
|
||||
if !l:project.initialized
|
||||
" Only send the init message once.
|
||||
if !l:project.init_request_id
|
||||
let [l:init_id, l:init_data] = ale#lsp#CreateMessageData(
|
||||
\ ale#lsp#message#Initialize(l:project_root),
|
||||
\)
|
||||
|
||||
let l:project.init_request_id = l:init_id
|
||||
|
||||
call s:SendMessageData(l:conn, l:init_data)
|
||||
endif
|
||||
endif
|
||||
|
||||
let [l:id, l:data] = ale#lsp#CreateMessageData(a:message)
|
||||
|
||||
if l:project.initialized
|
||||
" Send the message now.
|
||||
call s:SendMessageData(l:conn, l:data)
|
||||
else
|
||||
" Add the message we wanted to send to a List to send later.
|
||||
call add(l:project.message_queue, l:data)
|
||||
endif
|
||||
|
||||
return l:id == 0 ? -1 : l:id
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#OpenDocumentIfNeeded(conn_id, buffer, project_root, language_id) abort
|
||||
let l:conn = s:FindConnection('id', a:conn_id)
|
||||
let l:opened = 0
|
||||
|
||||
if !empty(l:conn) && index(l:conn.open_documents, a:buffer) < 0
|
||||
if empty(a:language_id)
|
||||
let l:message = ale#lsp#tsserver_message#Open(a:buffer)
|
||||
else
|
||||
let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id)
|
||||
endif
|
||||
|
||||
call ale#lsp#Send(a:conn_id, l:message, a:project_root)
|
||||
call add(l:conn.open_documents, a:buffer)
|
||||
let l:opened = 1
|
||||
endif
|
||||
|
||||
return l:opened
|
||||
endfunction
|
118
sources_non_forked/ale/autoload/ale/lsp/message.vim
Normal file
118
sources_non_forked/ale/autoload/ale/lsp/message.vim
Normal file
@ -0,0 +1,118 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Language Server Protocol message implementations
|
||||
"
|
||||
" Messages in this movie will be returned in the format
|
||||
" [is_notification, method_name, params?]
|
||||
let g:ale_lsp_next_version_id = 1
|
||||
|
||||
" The LSP protocols demands that we send every change to a document, including
|
||||
" undo, with incrementing version numbers, so we'll just use one incrementing
|
||||
" ID for everything.
|
||||
function! ale#lsp#message#GetNextVersionID() abort
|
||||
" Use the current ID
|
||||
let l:id = g:ale_lsp_next_version_id
|
||||
|
||||
" Increment the ID variable.
|
||||
let g:ale_lsp_next_version_id += 1
|
||||
|
||||
" When the ID overflows, reset it to 1. By the time we hit the initial ID
|
||||
" again, the messages will be long gone.
|
||||
if g:ale_lsp_next_version_id < 1
|
||||
let g:ale_lsp_next_version_id = 1
|
||||
endif
|
||||
|
||||
return l:id
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#Initialize(root_path) abort
|
||||
" TODO: Define needed capabilities.
|
||||
return [0, 'initialize', {
|
||||
\ 'processId': getpid(),
|
||||
\ 'rootPath': a:root_path,
|
||||
\ 'capabilities': {},
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#Initialized() abort
|
||||
return [1, 'initialized']
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#Shutdown() abort
|
||||
return [0, 'shutdown']
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#Exit() abort
|
||||
return [1, 'exit']
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#DidOpen(buffer, language_id) abort
|
||||
let l:lines = getbufline(a:buffer, 1, '$')
|
||||
|
||||
return [1, 'textDocument/didOpen', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'languageId': a:language_id,
|
||||
\ 'version': ale#lsp#message#GetNextVersionID(),
|
||||
\ 'text': join(l:lines, "\n") . "\n",
|
||||
\ },
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#DidChange(buffer) abort
|
||||
let l:lines = getbufline(a:buffer, 1, '$')
|
||||
|
||||
" For changes, we simply send the full text of the document to the server.
|
||||
return [1, 'textDocument/didChange', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'version': ale#lsp#message#GetNextVersionID(),
|
||||
\ },
|
||||
\ 'contentChanges': [{'text': join(l:lines, "\n") . "\n"}]
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#DidSave(buffer) abort
|
||||
return [1, 'textDocument/didSave', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#DidClose(buffer) abort
|
||||
return [1, 'textDocument/didClose', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
let s:COMPLETION_TRIGGER_INVOKED = 1
|
||||
let s:COMPLETION_TRIGGER_CHARACTER = 2
|
||||
|
||||
function! ale#lsp#message#Completion(buffer, line, column, trigger_character) abort
|
||||
let l:message = [0, 'textDocument/completion', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column},
|
||||
\}]
|
||||
|
||||
if !empty(a:trigger_character)
|
||||
let l:message[2].context = {
|
||||
\ 'triggerKind': s:COMPLETION_TRIGGER_CHARACTER,
|
||||
\ 'triggerCharacter': a:trigger_character,
|
||||
\}
|
||||
endif
|
||||
|
||||
return l:message
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#Definition(buffer, line, column) abort
|
||||
return [0, 'textDocument/definition', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column},
|
||||
\}]
|
||||
endfunction
|
25
sources_non_forked/ale/autoload/ale/lsp/reset.vim
Normal file
25
sources_non_forked/ale/autoload/ale/lsp/reset.vim
Normal file
@ -0,0 +1,25 @@
|
||||
" Stop all LSPs and remove all of the data for them.
|
||||
function! ale#lsp#reset#StopAllLSPs() abort
|
||||
call ale#lsp#StopAll()
|
||||
|
||||
if exists('*ale#definition#ClearLSPData')
|
||||
" Clear the mapping for connections, etc.
|
||||
call ale#definition#ClearLSPData()
|
||||
endif
|
||||
|
||||
if exists('*ale#engine#ClearLSPData')
|
||||
" Clear the mapping for connections, etc.
|
||||
call ale#engine#ClearLSPData()
|
||||
|
||||
" Remove the problems for all of the LSP linters in every buffer.
|
||||
for l:buffer_string in keys(g:ale_buffer_info)
|
||||
let l:buffer = str2nr(l:buffer_string)
|
||||
|
||||
for l:linter in ale#linter#Get(getbufvar(l:buffer, '&filetype'))
|
||||
if !empty(l:linter.lsp)
|
||||
call ale#engine#HandleLoclist(l:linter.name, l:buffer, [])
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
74
sources_non_forked/ale/autoload/ale/lsp/response.vim
Normal file
74
sources_non_forked/ale/autoload/ale/lsp/response.vim
Normal file
@ -0,0 +1,74 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Parsing and transforming of LSP server responses.
|
||||
|
||||
" Constants for message severity codes.
|
||||
let s:SEVERITY_ERROR = 1
|
||||
let s:SEVERITY_WARNING = 2
|
||||
let s:SEVERITY_INFORMATION = 3
|
||||
let s:SEVERITY_HINT = 4
|
||||
|
||||
" Parse the message for textDocument/publishDiagnostics
|
||||
function! ale#lsp#response#ReadDiagnostics(response) abort
|
||||
let l:loclist = []
|
||||
|
||||
for l:diagnostic in a:response.params.diagnostics
|
||||
let l:severity = get(l:diagnostic, 'severity', 0)
|
||||
let l:loclist_item = {
|
||||
\ 'text': l:diagnostic.message,
|
||||
\ 'type': 'E',
|
||||
\ 'lnum': l:diagnostic.range.start.line + 1,
|
||||
\ 'col': l:diagnostic.range.start.character + 1,
|
||||
\ 'end_lnum': l:diagnostic.range.end.line + 1,
|
||||
\ 'end_col': l:diagnostic.range.end.character + 1,
|
||||
\}
|
||||
|
||||
if l:severity == s:SEVERITY_WARNING
|
||||
let l:loclist_item.type = 'W'
|
||||
elseif l:severity == s:SEVERITY_INFORMATION
|
||||
" TODO: Use 'I' here in future.
|
||||
let l:loclist_item.type = 'W'
|
||||
elseif l:severity == s:SEVERITY_HINT
|
||||
" TODO: Use 'H' here in future
|
||||
let l:loclist_item.type = 'W'
|
||||
endif
|
||||
|
||||
if has_key(l:diagnostic, 'code')
|
||||
let l:loclist_item.nr = l:diagnostic.code
|
||||
endif
|
||||
|
||||
call add(l:loclist, l:loclist_item)
|
||||
endfor
|
||||
|
||||
return l:loclist
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#response#ReadTSServerDiagnostics(response) abort
|
||||
let l:loclist = []
|
||||
|
||||
for l:diagnostic in a:response.body.diagnostics
|
||||
let l:loclist_item = {
|
||||
\ 'text': l:diagnostic.text,
|
||||
\ 'type': 'E',
|
||||
\ 'lnum': l:diagnostic.start.line,
|
||||
\ 'col': l:diagnostic.start.offset,
|
||||
\ 'end_lnum': l:diagnostic.end.line,
|
||||
\ 'end_col': l:diagnostic.end.offset,
|
||||
\}
|
||||
|
||||
if has_key(l:diagnostic, 'code')
|
||||
let l:loclist_item.nr = l:diagnostic.code
|
||||
endif
|
||||
|
||||
if get(l:diagnostic, 'category') is# 'warning'
|
||||
let l:loclist_item.type = 'W'
|
||||
endif
|
||||
|
||||
if get(l:diagnostic, 'category') is# 'suggestion'
|
||||
let l:loclist_item.type = 'I'
|
||||
endif
|
||||
|
||||
call add(l:loclist, l:loclist_item)
|
||||
endfor
|
||||
|
||||
return l:loclist
|
||||
endfunction
|
63
sources_non_forked/ale/autoload/ale/lsp/tsserver_message.vim
Normal file
63
sources_non_forked/ale/autoload/ale/lsp/tsserver_message.vim
Normal file
@ -0,0 +1,63 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: tsserver message implementations
|
||||
"
|
||||
" Messages in this movie will be returned in the format
|
||||
" [is_notification, command_name, params?]
|
||||
"
|
||||
" Every command must begin with the string 'ts@', which will be used to
|
||||
" detect the different message format for tsserver, and this string will
|
||||
" be removed from the actual command name,
|
||||
|
||||
function! ale#lsp#tsserver_message#Open(buffer) abort
|
||||
return [1, 'ts@open', {'file': expand('#' . a:buffer . ':p')}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#Close(buffer) abort
|
||||
return [1, 'ts@close', {'file': expand('#' . a:buffer . ':p')}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#Change(buffer) abort
|
||||
let l:lines = getbufline(a:buffer, 1, '$')
|
||||
|
||||
" We will always use a very high endLine number, so we can delete
|
||||
" lines from files. tsserver will gladly accept line numbers beyond the
|
||||
" end.
|
||||
return [1, 'ts@change', {
|
||||
\ 'file': expand('#' . a:buffer . ':p'),
|
||||
\ 'line': 1,
|
||||
\ 'offset': 1,
|
||||
\ 'endLine': 1073741824,
|
||||
\ 'endOffset': 1,
|
||||
\ 'insertString': join(l:lines, "\n") . "\n",
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#Geterr(buffer) abort
|
||||
return [1, 'ts@geterr', {'files': [expand('#' . a:buffer . ':p')]}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#Completions(buffer, line, column, prefix) abort
|
||||
return [0, 'ts@completions', {
|
||||
\ 'line': a:line,
|
||||
\ 'offset': a:column,
|
||||
\ 'file': expand('#' . a:buffer . ':p'),
|
||||
\ 'prefix': a:prefix,
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#CompletionEntryDetails(buffer, line, column, entry_names) abort
|
||||
return [0, 'ts@completionEntryDetails', {
|
||||
\ 'line': a:line,
|
||||
\ 'offset': a:column,
|
||||
\ 'file': expand('#' . a:buffer . ':p'),
|
||||
\ 'entryNames': a:entry_names,
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#Definition(buffer, line, column) abort
|
||||
return [0, 'ts@definition', {
|
||||
\ 'line': a:line,
|
||||
\ 'offset': a:column,
|
||||
\ 'file': expand('#' . a:buffer . ':p'),
|
||||
\}]
|
||||
endfunction
|
42
sources_non_forked/ale/autoload/ale/node.vim
Normal file
42
sources_non_forked/ale/autoload/ale/node.vim
Normal file
@ -0,0 +1,42 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Functions for working with Node executables.
|
||||
|
||||
call ale#Set('windows_node_executable_path', 'node.exe')
|
||||
|
||||
" Given a buffer number, a base variable name, and a list of paths to search
|
||||
" for in ancestor directories, detect the executable path for a Node program.
|
||||
"
|
||||
" The use_global and executable options for the relevant program will be used.
|
||||
function! ale#node#FindExecutable(buffer, base_var_name, path_list) abort
|
||||
if ale#Var(a:buffer, a:base_var_name . '_use_global')
|
||||
return ale#Var(a:buffer, a:base_var_name . '_executable')
|
||||
endif
|
||||
|
||||
for l:path in a:path_list
|
||||
let l:executable = ale#path#FindNearestFile(a:buffer, l:path)
|
||||
|
||||
if !empty(l:executable)
|
||||
return l:executable
|
||||
endif
|
||||
endfor
|
||||
|
||||
return ale#Var(a:buffer, a:base_var_name . '_executable')
|
||||
endfunction
|
||||
|
||||
" Create a executable string which executes a Node.js script command with a
|
||||
" Node.js executable if needed.
|
||||
"
|
||||
" The executable string should not be escaped before passing it to this
|
||||
" function, the executable string will be escaped when returned by this
|
||||
" function.
|
||||
"
|
||||
" The executable is only prefixed for Windows machines
|
||||
function! ale#node#Executable(buffer, executable) abort
|
||||
if ale#Has('win32') && a:executable =~? '\.js$'
|
||||
let l:node = ale#Var(a:buffer, 'windows_node_executable_path')
|
||||
|
||||
return ale#Escape(l:node) . ' ' . ale#Escape(a:executable)
|
||||
endif
|
||||
|
||||
return ale#Escape(a:executable)
|
||||
endfunction
|
192
sources_non_forked/ale/autoload/ale/path.vim
Normal file
192
sources_non_forked/ale/autoload/ale/path.vim
Normal file
@ -0,0 +1,192 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Functions for working with paths in the filesystem.
|
||||
|
||||
" simplify a path, and fix annoying issues with paths on Windows.
|
||||
"
|
||||
" Forward slashes are changed to back slashes so path equality works better.
|
||||
"
|
||||
" Paths starting with more than one forward slash are changed to only one
|
||||
" forward slash, to prevent the paths being treated as special MSYS paths.
|
||||
function! ale#path#Simplify(path) abort
|
||||
if has('unix')
|
||||
return substitute(simplify(a:path), '^//\+', '/', 'g') " no-custom-checks
|
||||
endif
|
||||
|
||||
let l:win_path = substitute(a:path, '/', '\\', 'g')
|
||||
|
||||
return substitute(simplify(l:win_path), '^\\\+', '\', 'g') " no-custom-checks
|
||||
endfunction
|
||||
|
||||
" Given a buffer and a filename, find the nearest file by searching upwards
|
||||
" through the paths relative to the given buffer.
|
||||
function! ale#path#FindNearestFile(buffer, filename) abort
|
||||
let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p')
|
||||
let l:buffer_filename = fnameescape(l:buffer_filename)
|
||||
|
||||
let l:relative_path = findfile(a:filename, l:buffer_filename . ';')
|
||||
|
||||
if !empty(l:relative_path)
|
||||
return fnamemodify(l:relative_path, ':p')
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Given a buffer and a directory name, find the nearest directory by searching upwards
|
||||
" through the paths relative to the given buffer.
|
||||
function! ale#path#FindNearestDirectory(buffer, directory_name) abort
|
||||
let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p')
|
||||
let l:buffer_filename = fnameescape(l:buffer_filename)
|
||||
|
||||
let l:relative_path = finddir(a:directory_name, l:buffer_filename . ';')
|
||||
|
||||
if !empty(l:relative_path)
|
||||
return fnamemodify(l:relative_path, ':p')
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Given a buffer, a string to search for, an a global fallback for when
|
||||
" the search fails, look for a file in parent paths, and if that fails,
|
||||
" use the global fallback path instead.
|
||||
function! ale#path#ResolveLocalPath(buffer, search_string, global_fallback) abort
|
||||
" Search for a locally installed file first.
|
||||
let l:path = ale#path#FindNearestFile(a:buffer, a:search_string)
|
||||
|
||||
" If the serach fails, try the global executable instead.
|
||||
if empty(l:path)
|
||||
let l:path = a:global_fallback
|
||||
endif
|
||||
|
||||
return l:path
|
||||
endfunction
|
||||
|
||||
" Output 'cd <directory> && '
|
||||
" This function can be used changing the directory for a linter command.
|
||||
function! ale#path#CdString(directory) abort
|
||||
return 'cd ' . ale#Escape(a:directory) . ' && '
|
||||
endfunction
|
||||
|
||||
" Output 'cd <buffer_filename_directory> && '
|
||||
" This function can be used changing the directory for a linter command.
|
||||
function! ale#path#BufferCdString(buffer) abort
|
||||
return ale#path#CdString(fnamemodify(bufname(a:buffer), ':p:h'))
|
||||
endfunction
|
||||
|
||||
" Return 1 if a path is an absolute path.
|
||||
function! ale#path#IsAbsolute(filename) abort
|
||||
if has('win32') && a:filename[:0] is# '\'
|
||||
return 1
|
||||
endif
|
||||
|
||||
" Check for /foo and C:\foo, etc.
|
||||
return a:filename[:0] is# '/' || a:filename[1:2] is# ':\'
|
||||
endfunction
|
||||
|
||||
let s:temp_dir = ale#path#Simplify(fnamemodify(tempname(), ':h'))
|
||||
|
||||
" Given a filename, return 1 if the file represents some temporary file
|
||||
" created by Vim.
|
||||
function! ale#path#IsTempName(filename) abort
|
||||
return ale#path#Simplify(a:filename)[:len(s:temp_dir) - 1] is# s:temp_dir
|
||||
endfunction
|
||||
|
||||
" Given a base directory, which must not have a trailing slash, and a
|
||||
" filename, which may have an absolute path a path relative to the base
|
||||
" directory, return the absolute path to the file.
|
||||
function! ale#path#GetAbsPath(base_directory, filename) abort
|
||||
if ale#path#IsAbsolute(a:filename)
|
||||
return ale#path#Simplify(a:filename)
|
||||
endif
|
||||
|
||||
let l:sep = has('win32') ? '\' : '/'
|
||||
|
||||
return ale#path#Simplify(a:base_directory . l:sep . a:filename)
|
||||
endfunction
|
||||
|
||||
" Given a buffer number and a relative or absolute path, return 1 if the
|
||||
" two paths represent the same file on disk.
|
||||
function! ale#path#IsBufferPath(buffer, complex_filename) abort
|
||||
" If the path is one of many different names for stdin, we have a match.
|
||||
if a:complex_filename is# '-'
|
||||
\|| a:complex_filename is# 'stdin'
|
||||
\|| a:complex_filename[:0] is# '<'
|
||||
return 1
|
||||
endif
|
||||
|
||||
let l:test_filename = ale#path#Simplify(a:complex_filename)
|
||||
|
||||
if l:test_filename[:1] is# './'
|
||||
let l:test_filename = l:test_filename[2:]
|
||||
endif
|
||||
|
||||
if l:test_filename[:1] is# '..'
|
||||
" Remove ../../ etc. from the front of the path.
|
||||
let l:test_filename = substitute(l:test_filename, '\v^(\.\.[/\\])+', '/', '')
|
||||
endif
|
||||
|
||||
" Use the basename for temporary files, as they are likely our files.
|
||||
if ale#path#IsTempName(l:test_filename)
|
||||
let l:test_filename = fnamemodify(l:test_filename, ':t')
|
||||
endif
|
||||
|
||||
let l:buffer_filename = expand('#' . a:buffer . ':p')
|
||||
|
||||
return l:buffer_filename is# l:test_filename
|
||||
\ || l:buffer_filename[-len(l:test_filename):] is# l:test_filename
|
||||
endfunction
|
||||
|
||||
" Given a path, return every component of the path, moving upwards.
|
||||
function! ale#path#Upwards(path) abort
|
||||
let l:pattern = has('win32') ? '\v/+|\\+' : '\v/+'
|
||||
let l:sep = has('win32') ? '\' : '/'
|
||||
let l:parts = split(ale#path#Simplify(a:path), l:pattern)
|
||||
let l:path_list = []
|
||||
|
||||
while !empty(l:parts)
|
||||
call add(l:path_list, join(l:parts, l:sep))
|
||||
let l:parts = l:parts[:-2]
|
||||
endwhile
|
||||
|
||||
if has('win32') && a:path =~# '^[a-zA-z]:\'
|
||||
" Add \ to C: for C:\, etc.
|
||||
let l:path_list[-1] .= '\'
|
||||
elseif a:path[0] is# '/'
|
||||
" If the path starts with /, even on Windows, add / and / to all paths.
|
||||
call map(l:path_list, '''/'' . v:val')
|
||||
call add(l:path_list, '/')
|
||||
endif
|
||||
|
||||
return l:path_list
|
||||
endfunction
|
||||
|
||||
" Convert a filesystem path to a file:// URI
|
||||
" relatives paths will not be prefixed with the protocol.
|
||||
" For Windows paths, the `:` in C:\ etc. will not be percent-encoded.
|
||||
function! ale#path#ToURI(path) abort
|
||||
let l:has_drive_letter = a:path[1:2] is# ':\'
|
||||
|
||||
return substitute(
|
||||
\ ((l:has_drive_letter || a:path[:0] is# '/') ? 'file://' : '')
|
||||
\ . (l:has_drive_letter ? '/' . a:path[:2] : '')
|
||||
\ . ale#uri#Encode(l:has_drive_letter ? a:path[3:] : a:path),
|
||||
\ '\\',
|
||||
\ '/',
|
||||
\ 'g',
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! ale#path#FromURI(uri) abort
|
||||
let l:i = len('file://')
|
||||
let l:encoded_path = a:uri[: l:i - 1] is# 'file://' ? a:uri[l:i :] : a:uri
|
||||
|
||||
let l:path = ale#uri#Decode(l:encoded_path)
|
||||
|
||||
" If the path is like /C:/foo/bar, it should be C:\foo\bar instead.
|
||||
if l:path =~# '^/[a-zA-Z]:'
|
||||
let l:path = substitute(l:path[1:], '/', '\\', 'g')
|
||||
endif
|
||||
|
||||
return l:path
|
||||
endfunction
|
44
sources_non_forked/ale/autoload/ale/pattern_options.vim
Normal file
44
sources_non_forked/ale/autoload/ale/pattern_options.vim
Normal file
@ -0,0 +1,44 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Set options in files based on regex patterns.
|
||||
|
||||
" These variables are used to cache the sorting of patterns below.
|
||||
let s:last_pattern_options = {}
|
||||
let s:sorted_items = []
|
||||
|
||||
function! s:CmpPatterns(left_item, right_item) abort
|
||||
if a:left_item[0] < a:right_item[0]
|
||||
return -1
|
||||
endif
|
||||
|
||||
if a:left_item[0] > a:right_item[0]
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! ale#pattern_options#SetOptions(buffer) abort
|
||||
if !g:ale_pattern_options_enabled || empty(g:ale_pattern_options)
|
||||
return
|
||||
endif
|
||||
|
||||
" The items will only be sorted whenever the patterns change.
|
||||
if g:ale_pattern_options != s:last_pattern_options
|
||||
let s:last_pattern_options = deepcopy(g:ale_pattern_options)
|
||||
" The patterns are sorted, so they are applied consistently.
|
||||
let s:sorted_items = sort(
|
||||
\ items(g:ale_pattern_options),
|
||||
\ function('s:CmpPatterns')
|
||||
\)
|
||||
endif
|
||||
|
||||
let l:filename = expand('#' . a:buffer . ':p')
|
||||
|
||||
for [l:pattern, l:options] in s:sorted_items
|
||||
if match(l:filename, l:pattern) >= 0
|
||||
for [l:key, l:value] in items(l:options)
|
||||
call setbufvar(a:buffer, l:key, l:value)
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
18
sources_non_forked/ale/autoload/ale/preview.vim
Normal file
18
sources_non_forked/ale/autoload/ale/preview.vim
Normal file
@ -0,0 +1,18 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Preview windows for showing whatever information in.
|
||||
|
||||
" Open a preview window and show some lines in it.
|
||||
function! ale#preview#Show(lines) abort
|
||||
silent pedit ALEPreviewWindow
|
||||
wincmd P
|
||||
setlocal modifiable
|
||||
setlocal noreadonly
|
||||
setlocal nobuflisted
|
||||
setlocal filetype=ale-preview
|
||||
setlocal buftype=nofile
|
||||
setlocal bufhidden=wipe
|
||||
:%d
|
||||
call setline(1, a:lines)
|
||||
setlocal nomodifiable
|
||||
setlocal readonly
|
||||
endfunction
|
104
sources_non_forked/ale/autoload/ale/python.vim
Normal file
104
sources_non_forked/ale/autoload/ale/python.vim
Normal file
@ -0,0 +1,104 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Functions for integrating with Python linters.
|
||||
|
||||
let s:sep = has('win32') ? '\' : '/'
|
||||
" bin is used for Unix virtualenv directories, and Scripts is for Windows.
|
||||
let s:bin_dir = has('unix') ? 'bin' : 'Scripts'
|
||||
let g:ale_virtualenv_dir_names = get(g:, 'ale_virtualenv_dir_names', [
|
||||
\ '.env',
|
||||
\ 'env',
|
||||
\ 've-py3',
|
||||
\ 've',
|
||||
\ 'virtualenv',
|
||||
\ 'venv',
|
||||
\])
|
||||
|
||||
function! ale#python#FindProjectRootIni(buffer) abort
|
||||
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
|
||||
if filereadable(l:path . '/MANIFEST.in')
|
||||
\|| filereadable(l:path . '/setup.cfg')
|
||||
\|| filereadable(l:path . '/pytest.ini')
|
||||
\|| filereadable(l:path . '/tox.ini')
|
||||
\|| filereadable(l:path . '/mypy.ini')
|
||||
\|| filereadable(l:path . '/pycodestyle.cfg')
|
||||
\|| filereadable(l:path . '/flake8.cfg')
|
||||
return l:path
|
||||
endif
|
||||
endfor
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Given a buffer number, find the project root directory for Python.
|
||||
" The root directory is defined as the first directory found while searching
|
||||
" upwards through paths, including the current directory, until a path
|
||||
" containing an init file (one from MANIFEST.in, setup.cfg, pytest.ini,
|
||||
" tox.ini) is found. If it is not possible to find the project root directory
|
||||
" via init file, then it will be defined as the first directory found
|
||||
" searching upwards through paths, including the current directory, until no
|
||||
" __init__.py files is found.
|
||||
function! ale#python#FindProjectRoot(buffer) abort
|
||||
let l:ini_root = ale#python#FindProjectRootIni(a:buffer)
|
||||
|
||||
if !empty(l:ini_root)
|
||||
return l:ini_root
|
||||
endif
|
||||
|
||||
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
|
||||
if !filereadable(l:path . '/__init__.py')
|
||||
return l:path
|
||||
endif
|
||||
endfor
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" Given a buffer number, find a virtualenv path for Python.
|
||||
function! ale#python#FindVirtualenv(buffer) abort
|
||||
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
|
||||
" Skip empty path components returned in MSYS.
|
||||
if empty(l:path)
|
||||
continue
|
||||
endif
|
||||
|
||||
for l:dirname in ale#Var(a:buffer, 'virtualenv_dir_names')
|
||||
let l:venv_dir = ale#path#Simplify(
|
||||
\ join([l:path, l:dirname], s:sep)
|
||||
\)
|
||||
let l:script_filename = ale#path#Simplify(
|
||||
\ join([l:venv_dir, s:bin_dir, 'activate'], s:sep)
|
||||
\)
|
||||
|
||||
if filereadable(l:script_filename)
|
||||
return l:venv_dir
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return $VIRTUAL_ENV
|
||||
endfunction
|
||||
|
||||
" Given a buffer number and a command name, find the path to the executable.
|
||||
" First search on a virtualenv for Python, if nothing is found, try the global
|
||||
" command. Returns an empty string if cannot find the executable
|
||||
function! ale#python#FindExecutable(buffer, base_var_name, path_list) abort
|
||||
if ale#Var(a:buffer, a:base_var_name . '_use_global')
|
||||
return ale#Var(a:buffer, a:base_var_name . '_executable')
|
||||
endif
|
||||
|
||||
let l:virtualenv = ale#python#FindVirtualenv(a:buffer)
|
||||
|
||||
if !empty(l:virtualenv)
|
||||
for l:path in a:path_list
|
||||
let l:ve_executable = ale#path#Simplify(
|
||||
\ join([l:virtualenv, s:bin_dir, l:path], s:sep)
|
||||
\)
|
||||
|
||||
if executable(l:ve_executable)
|
||||
return l:ve_executable
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
return ale#Var(a:buffer, a:base_var_name . '_executable')
|
||||
endfunction
|
22
sources_non_forked/ale/autoload/ale/ruby.vim
Normal file
22
sources_non_forked/ale/autoload/ale/ruby.vim
Normal file
@ -0,0 +1,22 @@
|
||||
" Author: Eddie Lebow https://github.com/elebow
|
||||
" Description: Functions for integrating with Ruby tools
|
||||
|
||||
" Find the nearest dir contining "app", "db", and "config", and assume it is
|
||||
" the root of a Rails app.
|
||||
function! ale#ruby#FindRailsRoot(buffer) abort
|
||||
for l:name in ['app', 'config', 'db']
|
||||
let l:dir = fnamemodify(
|
||||
\ ale#path#FindNearestDirectory(a:buffer, l:name),
|
||||
\ ':h:h'
|
||||
\)
|
||||
|
||||
if l:dir isnot# '.'
|
||||
\&& isdirectory(l:dir . '/app')
|
||||
\&& isdirectory(l:dir . '/config')
|
||||
\&& isdirectory(l:dir . '/db')
|
||||
return l:dir
|
||||
endif
|
||||
endfor
|
||||
|
||||
return ''
|
||||
endfunction
|
57
sources_non_forked/ale/autoload/ale/semver.vim
Normal file
57
sources_non_forked/ale/autoload/ale/semver.vim
Normal file
@ -0,0 +1,57 @@
|
||||
let s:version_cache = {}
|
||||
|
||||
" Reset the version cache used for parsing the version.
|
||||
function! ale#semver#ResetVersionCache() abort
|
||||
let s:version_cache = {}
|
||||
endfunction
|
||||
|
||||
" Given an executable name and some lines of output, which can be empty,
|
||||
" parse the version from the lines of output, or return the cached version
|
||||
" triple [major, minor, patch]
|
||||
"
|
||||
" If the version cannot be found, an empty List will be returned instead.
|
||||
function! ale#semver#GetVersion(executable, version_lines) abort
|
||||
let l:version = get(s:version_cache, a:executable, [])
|
||||
|
||||
for l:line in a:version_lines
|
||||
let l:match = matchlist(l:line, '\v(\d+)\.(\d+)\.(\d+)')
|
||||
|
||||
if !empty(l:match)
|
||||
let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[3] + 0]
|
||||
let s:version_cache[a:executable] = l:version
|
||||
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:version
|
||||
endfunction
|
||||
|
||||
" Return 1 if the semver version has been cached for a given executable.
|
||||
function! ale#semver#HasVersion(executable) abort
|
||||
return has_key(s:version_cache, a:executable)
|
||||
endfunction
|
||||
|
||||
" Given two triples of integers [major, minor, patch], compare the triples
|
||||
" and return 1 if the LHS is greater than or equal to the RHS.
|
||||
"
|
||||
" Pairs of [major, minor] can also be used for either argument.
|
||||
"
|
||||
" 0 will be returned if the LHS is an empty List.
|
||||
function! ale#semver#GTE(lhs, rhs) abort
|
||||
if empty(a:lhs)
|
||||
return 0
|
||||
endif
|
||||
|
||||
if a:lhs[0] > a:rhs[0]
|
||||
return 1
|
||||
elseif a:lhs[0] == a:rhs[0]
|
||||
if a:lhs[1] > a:rhs[1]
|
||||
return 1
|
||||
elseif a:lhs[1] == a:rhs[1]
|
||||
return get(a:lhs, 2) >= get(a:rhs, 2)
|
||||
endif
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
386
sources_non_forked/ale/autoload/ale/sign.vim
Normal file
386
sources_non_forked/ale/autoload/ale/sign.vim
Normal file
@ -0,0 +1,386 @@
|
||||
scriptencoding utf8
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Draws error and warning signs into signcolumn
|
||||
|
||||
if !hlexists('ALEErrorSign')
|
||||
highlight link ALEErrorSign error
|
||||
endif
|
||||
|
||||
if !hlexists('ALEStyleErrorSign')
|
||||
highlight link ALEStyleErrorSign ALEErrorSign
|
||||
endif
|
||||
|
||||
if !hlexists('ALEWarningSign')
|
||||
highlight link ALEWarningSign todo
|
||||
endif
|
||||
|
||||
if !hlexists('ALEStyleWarningSign')
|
||||
highlight link ALEStyleWarningSign ALEWarningSign
|
||||
endif
|
||||
|
||||
if !hlexists('ALEInfoSign')
|
||||
highlight link ALEInfoSign ALEWarningSign
|
||||
endif
|
||||
|
||||
if !hlexists('ALESignColumnWithErrors')
|
||||
highlight link ALESignColumnWithErrors error
|
||||
endif
|
||||
|
||||
if !hlexists('ALESignColumnWithoutErrors')
|
||||
function! s:SetSignColumnWithoutErrorsHighlight() abort
|
||||
redir => l:output
|
||||
silent highlight SignColumn
|
||||
redir end
|
||||
|
||||
let l:highlight_syntax = join(split(l:output)[2:])
|
||||
|
||||
let l:match = matchlist(l:highlight_syntax, '\vlinks to (.+)$')
|
||||
|
||||
if !empty(l:match)
|
||||
execute 'highlight link ALESignColumnWithoutErrors ' . l:match[1]
|
||||
elseif l:highlight_syntax isnot# 'cleared'
|
||||
execute 'highlight ALESignColumnWithoutErrors ' . l:highlight_syntax
|
||||
endif
|
||||
endfunction
|
||||
|
||||
call s:SetSignColumnWithoutErrorsHighlight()
|
||||
delfunction s:SetSignColumnWithoutErrorsHighlight
|
||||
endif
|
||||
|
||||
" Signs show up on the left for error markers.
|
||||
execute 'sign define ALEErrorSign text=' . g:ale_sign_error
|
||||
\ . ' texthl=ALEErrorSign linehl=ALEErrorLine'
|
||||
execute 'sign define ALEStyleErrorSign text=' . g:ale_sign_style_error
|
||||
\ . ' texthl=ALEStyleErrorSign linehl=ALEErrorLine'
|
||||
execute 'sign define ALEWarningSign text=' . g:ale_sign_warning
|
||||
\ . ' texthl=ALEWarningSign linehl=ALEWarningLine'
|
||||
execute 'sign define ALEStyleWarningSign text=' . g:ale_sign_style_warning
|
||||
\ . ' texthl=ALEStyleWarningSign linehl=ALEWarningLine'
|
||||
execute 'sign define ALEInfoSign text=' . g:ale_sign_info
|
||||
\ . ' texthl=ALEInfoSign linehl=ALEInfoLine'
|
||||
sign define ALEDummySign
|
||||
|
||||
let s:error_priority = 1
|
||||
let s:warning_priority = 2
|
||||
let s:info_priority = 3
|
||||
let s:style_error_priority = 4
|
||||
let s:style_warning_priority = 5
|
||||
|
||||
function! ale#sign#GetSignName(sublist) abort
|
||||
let l:priority = s:style_warning_priority
|
||||
|
||||
" Determine the highest priority item for the line.
|
||||
for l:item in a:sublist
|
||||
if l:item.type is# 'I'
|
||||
let l:item_priority = s:info_priority
|
||||
elseif l:item.type is# 'W'
|
||||
if get(l:item, 'sub_type', '') is# 'style'
|
||||
let l:item_priority = s:style_warning_priority
|
||||
else
|
||||
let l:item_priority = s:warning_priority
|
||||
endif
|
||||
else
|
||||
if get(l:item, 'sub_type', '') is# 'style'
|
||||
let l:item_priority = s:style_error_priority
|
||||
else
|
||||
let l:item_priority = s:error_priority
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:item_priority < l:priority
|
||||
let l:priority = l:item_priority
|
||||
endif
|
||||
endfor
|
||||
|
||||
if l:priority is# s:error_priority
|
||||
return 'ALEErrorSign'
|
||||
endif
|
||||
|
||||
if l:priority is# s:warning_priority
|
||||
return 'ALEWarningSign'
|
||||
endif
|
||||
|
||||
if l:priority is# s:style_error_priority
|
||||
return 'ALEStyleErrorSign'
|
||||
endif
|
||||
|
||||
if l:priority is# s:style_warning_priority
|
||||
return 'ALEStyleWarningSign'
|
||||
endif
|
||||
|
||||
if l:priority is# s:info_priority
|
||||
return 'ALEInfoSign'
|
||||
endif
|
||||
|
||||
" Use the error sign for invalid severities.
|
||||
return 'ALEErrorSign'
|
||||
endfunction
|
||||
|
||||
" Read sign data for a buffer to a list of lines.
|
||||
function! ale#sign#ReadSigns(buffer) abort
|
||||
redir => l:output
|
||||
silent execute 'sign place buffer=' . a:buffer
|
||||
redir end
|
||||
|
||||
return split(l:output, "\n")
|
||||
endfunction
|
||||
|
||||
" Given a list of lines for sign output, return a List of [line, id, group]
|
||||
function! ale#sign#ParseSigns(line_list) abort
|
||||
" Matches output like :
|
||||
" line=4 id=1 name=ALEErrorSign
|
||||
" строка=1 id=1000001 имя=ALEErrorSign
|
||||
" 行=1 識別子=1000001 名前=ALEWarningSign
|
||||
" línea=12 id=1000001 nombre=ALEWarningSign
|
||||
" riga=1 id=1000001, nome=ALEWarningSign
|
||||
let l:pattern = '\v^.*\=(\d+).*\=(\d+).*\=(ALE[a-zA-Z]+Sign)'
|
||||
let l:result = []
|
||||
let l:is_dummy_sign_set = 0
|
||||
|
||||
for l:line in a:line_list
|
||||
let l:match = matchlist(l:line, l:pattern)
|
||||
|
||||
if len(l:match) > 0
|
||||
if l:match[3] is# 'ALEDummySign'
|
||||
let l:is_dummy_sign_set = 1
|
||||
else
|
||||
call add(l:result, [
|
||||
\ str2nr(l:match[1]),
|
||||
\ str2nr(l:match[2]),
|
||||
\ l:match[3],
|
||||
\])
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
return [l:is_dummy_sign_set, l:result]
|
||||
endfunction
|
||||
|
||||
function! ale#sign#FindCurrentSigns(buffer) abort
|
||||
let l:line_list = ale#sign#ReadSigns(a:buffer)
|
||||
|
||||
return ale#sign#ParseSigns(l:line_list)
|
||||
endfunction
|
||||
|
||||
" Given a loclist, group the List into with one List per line.
|
||||
function! s:GroupLoclistItems(buffer, loclist) abort
|
||||
let l:grouped_items = []
|
||||
let l:last_lnum = -1
|
||||
|
||||
for l:obj in a:loclist
|
||||
if l:obj.bufnr != a:buffer
|
||||
continue
|
||||
endif
|
||||
|
||||
" Create a new sub-List when we hit a new line.
|
||||
if l:obj.lnum != l:last_lnum
|
||||
call add(l:grouped_items, [])
|
||||
endif
|
||||
|
||||
call add(l:grouped_items[-1], l:obj)
|
||||
let l:last_lnum = l:obj.lnum
|
||||
endfor
|
||||
|
||||
return l:grouped_items
|
||||
endfunction
|
||||
|
||||
function! s:UpdateLineNumbers(buffer, current_sign_list, loclist) abort
|
||||
let l:line_map = {}
|
||||
let l:line_numbers_changed = 0
|
||||
|
||||
for [l:line, l:sign_id, l:name] in a:current_sign_list
|
||||
let l:line_map[l:sign_id] = l:line
|
||||
endfor
|
||||
|
||||
for l:item in a:loclist
|
||||
if l:item.bufnr == a:buffer
|
||||
let l:lnum = get(l:line_map, get(l:item, 'sign_id', 0), 0)
|
||||
|
||||
if l:lnum && l:item.lnum != l:lnum
|
||||
let l:item.lnum = l:lnum
|
||||
let l:line_numbers_changed = 1
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
" When the line numbers change, sort the list again
|
||||
if l:line_numbers_changed
|
||||
call sort(a:loclist, 'ale#util#LocItemCompare')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:BuildSignMap(buffer, current_sign_list, grouped_items) abort
|
||||
let l:max_signs = ale#Var(a:buffer, 'max_signs')
|
||||
|
||||
if l:max_signs is 0
|
||||
let l:selected_grouped_items = []
|
||||
elseif type(l:max_signs) is type(0) && l:max_signs > 0
|
||||
let l:selected_grouped_items = a:grouped_items[:l:max_signs - 1]
|
||||
else
|
||||
let l:selected_grouped_items = a:grouped_items
|
||||
endif
|
||||
|
||||
let l:sign_map = {}
|
||||
let l:sign_offset = g:ale_sign_offset
|
||||
|
||||
for [l:line, l:sign_id, l:name] in a:current_sign_list
|
||||
let l:sign_info = get(l:sign_map, l:line, {
|
||||
\ 'current_id_list': [],
|
||||
\ 'current_name_list': [],
|
||||
\ 'new_id': 0,
|
||||
\ 'new_name': '',
|
||||
\ 'items': [],
|
||||
\})
|
||||
|
||||
" Increment the sign offset for new signs, by the maximum sign ID.
|
||||
if l:sign_id > l:sign_offset
|
||||
let l:sign_offset = l:sign_id
|
||||
endif
|
||||
|
||||
" Remember the sign names and IDs in separate Lists, so they are easy
|
||||
" to work with.
|
||||
call add(l:sign_info.current_id_list, l:sign_id)
|
||||
call add(l:sign_info.current_name_list, l:name)
|
||||
|
||||
let l:sign_map[l:line] = l:sign_info
|
||||
endfor
|
||||
|
||||
for l:group in l:selected_grouped_items
|
||||
let l:line = l:group[0].lnum
|
||||
let l:sign_info = get(l:sign_map, l:line, {
|
||||
\ 'current_id_list': [],
|
||||
\ 'current_name_list': [],
|
||||
\ 'new_id': 0,
|
||||
\ 'new_name': '',
|
||||
\ 'items': [],
|
||||
\})
|
||||
|
||||
let l:sign_info.new_name = ale#sign#GetSignName(l:group)
|
||||
let l:sign_info.items = l:group
|
||||
|
||||
let l:index = index(
|
||||
\ l:sign_info.current_name_list,
|
||||
\ l:sign_info.new_name
|
||||
\)
|
||||
|
||||
if l:index >= 0
|
||||
" We have a sign with this name already, so use the same ID.
|
||||
let l:sign_info.new_id = l:sign_info.current_id_list[l:index]
|
||||
else
|
||||
" This sign name replaces the previous name, so use a new ID.
|
||||
let l:sign_info.new_id = l:sign_offset + 1
|
||||
let l:sign_offset += 1
|
||||
endif
|
||||
|
||||
let l:sign_map[l:line] = l:sign_info
|
||||
endfor
|
||||
|
||||
return l:sign_map
|
||||
endfunction
|
||||
|
||||
function! ale#sign#GetSignCommands(buffer, was_sign_set, sign_map) abort
|
||||
let l:command_list = []
|
||||
let l:is_dummy_sign_set = a:was_sign_set
|
||||
|
||||
" Set the dummy sign if we need to.
|
||||
" The dummy sign is needed to keep the sign column open while we add
|
||||
" and remove signs.
|
||||
if !l:is_dummy_sign_set && (!empty(a:sign_map) || g:ale_sign_column_always)
|
||||
call add(l:command_list, 'sign place '
|
||||
\ . g:ale_sign_offset
|
||||
\ . ' line=1 name=ALEDummySign buffer='
|
||||
\ . a:buffer
|
||||
\)
|
||||
let l:is_dummy_sign_set = 1
|
||||
endif
|
||||
|
||||
" Place new items first.
|
||||
for [l:line_str, l:info] in items(a:sign_map)
|
||||
if l:info.new_id
|
||||
" Save the sign IDs we are setting back on our loclist objects.
|
||||
" These IDs will be used to preserve items which are set many times.
|
||||
for l:item in l:info.items
|
||||
let l:item.sign_id = l:info.new_id
|
||||
endfor
|
||||
|
||||
if index(l:info.current_id_list, l:info.new_id) < 0
|
||||
call add(l:command_list, 'sign place '
|
||||
\ . (l:info.new_id)
|
||||
\ . ' line=' . l:line_str
|
||||
\ . ' name=' . (l:info.new_name)
|
||||
\ . ' buffer=' . a:buffer
|
||||
\)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Remove signs without new IDs.
|
||||
for l:info in values(a:sign_map)
|
||||
for l:current_id in l:info.current_id_list
|
||||
if l:current_id isnot l:info.new_id
|
||||
call add(l:command_list, 'sign unplace '
|
||||
\ . l:current_id
|
||||
\ . ' buffer=' . a:buffer
|
||||
\)
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
" Remove the dummy sign to close the sign column if we need to.
|
||||
if l:is_dummy_sign_set && !g:ale_sign_column_always
|
||||
call add(l:command_list, 'sign unplace '
|
||||
\ . g:ale_sign_offset
|
||||
\ . ' buffer=' . a:buffer
|
||||
\)
|
||||
endif
|
||||
|
||||
return l:command_list
|
||||
endfunction
|
||||
|
||||
" This function will set the signs which show up on the left.
|
||||
function! ale#sign#SetSigns(buffer, loclist) abort
|
||||
if !bufexists(str2nr(a:buffer))
|
||||
" Stop immediately when attempting to set signs for a buffer which
|
||||
" does not exist.
|
||||
return
|
||||
endif
|
||||
|
||||
" Find the current markers
|
||||
let [l:is_dummy_sign_set, l:current_sign_list] =
|
||||
\ ale#sign#FindCurrentSigns(a:buffer)
|
||||
|
||||
" Update the line numbers for items from before which may have moved.
|
||||
call s:UpdateLineNumbers(a:buffer, l:current_sign_list, a:loclist)
|
||||
|
||||
" Group items after updating the line numbers.
|
||||
let l:grouped_items = s:GroupLoclistItems(a:buffer, a:loclist)
|
||||
|
||||
" Build a map of current and new signs, with the lines as the keys.
|
||||
let l:sign_map = s:BuildSignMap(
|
||||
\ a:buffer,
|
||||
\ l:current_sign_list,
|
||||
\ l:grouped_items,
|
||||
\)
|
||||
|
||||
let l:command_list = ale#sign#GetSignCommands(
|
||||
\ a:buffer,
|
||||
\ l:is_dummy_sign_set,
|
||||
\ l:sign_map,
|
||||
\)
|
||||
|
||||
" Change the sign column color if the option is on.
|
||||
if g:ale_change_sign_column_color && !empty(a:loclist)
|
||||
highlight clear SignColumn
|
||||
highlight link SignColumn ALESignColumnWithErrors
|
||||
endif
|
||||
|
||||
for l:command in l:command_list
|
||||
silent! execute l:command
|
||||
endfor
|
||||
|
||||
" Reset the sign column color when there are no more errors.
|
||||
if g:ale_change_sign_column_color && empty(a:loclist)
|
||||
highlight clear SignColumn
|
||||
highlight link SignColumn ALESignColumnWithoutErrors
|
||||
endif
|
||||
endfunction
|
112
sources_non_forked/ale/autoload/ale/statusline.vim
Normal file
112
sources_non_forked/ale/autoload/ale/statusline.vim
Normal file
@ -0,0 +1,112 @@
|
||||
" Author: KabbAmine <amine.kabb@gmail.com>
|
||||
" Description: Statusline related function(s)
|
||||
|
||||
function! s:CreateCountDict() abort
|
||||
" Keys 0 and 1 are for backwards compatibility.
|
||||
" The count object used to be a List of [error_count, warning_count].
|
||||
return {
|
||||
\ '0': 0,
|
||||
\ '1': 0,
|
||||
\ 'error': 0,
|
||||
\ 'warning': 0,
|
||||
\ 'info': 0,
|
||||
\ 'style_error': 0,
|
||||
\ 'style_warning': 0,
|
||||
\ 'total': 0,
|
||||
\}
|
||||
endfunction
|
||||
|
||||
" Update the buffer error/warning count with data from loclist.
|
||||
function! ale#statusline#Update(buffer, loclist) abort
|
||||
if !exists('g:ale_buffer_info') || !has_key(g:ale_buffer_info, a:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:loclist = filter(copy(a:loclist), 'v:val.bufnr == a:buffer')
|
||||
let l:count = s:CreateCountDict()
|
||||
let l:count.total = len(l:loclist)
|
||||
|
||||
for l:entry in l:loclist
|
||||
if l:entry.type is# 'W'
|
||||
if get(l:entry, 'sub_type', '') is# 'style'
|
||||
let l:count.style_warning += 1
|
||||
else
|
||||
let l:count.warning += 1
|
||||
endif
|
||||
elseif l:entry.type is# 'I'
|
||||
let l:count.info += 1
|
||||
elseif get(l:entry, 'sub_type', '') is# 'style'
|
||||
let l:count.style_error += 1
|
||||
else
|
||||
let l:count.error += 1
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Set keys for backwards compatibility.
|
||||
let l:count[0] = l:count.error + l:count.style_error
|
||||
let l:count[1] = l:count.total - l:count[0]
|
||||
|
||||
let g:ale_buffer_info[a:buffer].count = l:count
|
||||
endfunction
|
||||
|
||||
" Get the counts for the buffer, and update the counts if needed.
|
||||
function! s:GetCounts(buffer) abort
|
||||
if !exists('g:ale_buffer_info') || !has_key(g:ale_buffer_info, a:buffer)
|
||||
return s:CreateCountDict()
|
||||
endif
|
||||
|
||||
" Cache is cold, so manually ask for an update.
|
||||
if !has_key(g:ale_buffer_info[a:buffer], 'count')
|
||||
call ale#statusline#Update(a:buffer, g:ale_buffer_info[a:buffer].loclist)
|
||||
endif
|
||||
|
||||
return g:ale_buffer_info[a:buffer].count
|
||||
endfunction
|
||||
|
||||
" Returns a Dictionary with counts for use in third party integrations.
|
||||
function! ale#statusline#Count(buffer) abort
|
||||
" The Dictionary is copied here before exposing it to other plugins.
|
||||
return copy(s:GetCounts(a:buffer))
|
||||
endfunction
|
||||
|
||||
" This is the historical format setting which could be configured before.
|
||||
function! s:StatusForListFormat() abort
|
||||
let [l:error_format, l:warning_format, l:no_errors] = g:ale_statusline_format
|
||||
let l:counts = s:GetCounts(bufnr(''))
|
||||
|
||||
" Build strings based on user formatting preferences.
|
||||
let l:errors = l:counts[0] ? printf(l:error_format, l:counts[0]) : ''
|
||||
let l:warnings = l:counts[1] ? printf(l:warning_format, l:counts[1]) : ''
|
||||
|
||||
" Different formats based on the combination of errors and warnings.
|
||||
if empty(l:errors) && empty(l:warnings)
|
||||
let l:res = l:no_errors
|
||||
elseif !empty(l:errors) && !empty(l:warnings)
|
||||
let l:res = printf('%s %s', l:errors, l:warnings)
|
||||
else
|
||||
let l:res = empty(l:errors) ? l:warnings : l:errors
|
||||
endif
|
||||
|
||||
return l:res
|
||||
endfunction
|
||||
|
||||
" Returns a formatted string that can be integrated in the statusline.
|
||||
"
|
||||
" This function is deprecated, and should not be used. Use the airline plugin
|
||||
" instead, or write your own status function with ale#statusline#Count()
|
||||
function! ale#statusline#Status() abort
|
||||
if !get(g:, 'ale_deprecation_ale_statusline_status', 0)
|
||||
execute 'echom ''ale#statusline#Status() is deprecated, use ale#statusline#Count() to write your own function.'''
|
||||
let g:ale_deprecation_ale_statusline_status = 1
|
||||
endif
|
||||
|
||||
if !exists('g:ale_statusline_format')
|
||||
return 'OK'
|
||||
endif
|
||||
|
||||
if type(g:ale_statusline_format) == type([])
|
||||
return s:StatusForListFormat()
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
54
sources_non_forked/ale/autoload/ale/test.vim
Normal file
54
sources_non_forked/ale/autoload/ale/test.vim
Normal file
@ -0,0 +1,54 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Functions for making testing ALE easier.
|
||||
"
|
||||
" This file should not typically be loaded during the normal execution of ALE.
|
||||
|
||||
" Change the directory for checking things in particular test directories
|
||||
"
|
||||
" This function will set the g:dir variable, which represents the working
|
||||
" directory after changing the path. This variable allows a test to change
|
||||
" directories, and then switch back to a directory at the start of the test
|
||||
" run.
|
||||
"
|
||||
" This function should be run in a Vader Before: block.
|
||||
function! ale#test#SetDirectory(docker_path) abort
|
||||
if a:docker_path[:len('/testplugin/') - 1] isnot# '/testplugin/'
|
||||
throw 'docker_path must start with /testplugin/!'
|
||||
endif
|
||||
|
||||
" Try to switch directory, which will fail when running tests directly,
|
||||
" and not through the Docker image.
|
||||
silent! execute 'cd ' . fnameescape(a:docker_path)
|
||||
let g:dir = getcwd() " no-custom-checks
|
||||
endfunction
|
||||
|
||||
" When g:dir is defined, switch back to the directory we saved, and then
|
||||
" delete that variable.
|
||||
"
|
||||
" The filename will be reset to dummy.txt
|
||||
"
|
||||
" This function should be run in a Vader After: block.
|
||||
function! ale#test#RestoreDirectory() abort
|
||||
call ale#test#SetFilename('dummy.txt')
|
||||
silent execute 'cd ' . fnameescape(g:dir)
|
||||
unlet! g:dir
|
||||
endfunction
|
||||
|
||||
" Change the filename for the current buffer using a relative path to
|
||||
" the script without running autocmd commands.
|
||||
"
|
||||
" If a g:dir variable is set, it will be used as the path to the directory
|
||||
" containing the test file.
|
||||
function! ale#test#SetFilename(path) abort
|
||||
let l:dir = get(g:, 'dir', '')
|
||||
|
||||
if empty(l:dir)
|
||||
let l:dir = getcwd() " no-custom-checks
|
||||
endif
|
||||
|
||||
let l:full_path = ale#path#IsAbsolute(a:path)
|
||||
\ ? a:path
|
||||
\ : l:dir . '/' . a:path
|
||||
|
||||
silent! noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path))
|
||||
endfunction
|
193
sources_non_forked/ale/autoload/ale/toggle.vim
Normal file
193
sources_non_forked/ale/autoload/ale/toggle.vim
Normal file
@ -0,0 +1,193 @@
|
||||
function! ale#toggle#InitAuGroups() abort
|
||||
" This value used to be a Boolean as a Number, and is now a String.
|
||||
let l:text_changed = '' . g:ale_lint_on_text_changed
|
||||
|
||||
augroup ALEPatternOptionsGroup
|
||||
autocmd!
|
||||
autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions(str2nr(expand('<abuf>')))
|
||||
augroup END
|
||||
|
||||
augroup ALERunOnTextChangedGroup
|
||||
autocmd!
|
||||
if g:ale_enabled
|
||||
if l:text_changed is? 'always' || l:text_changed is# '1'
|
||||
autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay)
|
||||
elseif l:text_changed is? 'normal'
|
||||
autocmd TextChanged * call ale#Queue(g:ale_lint_delay)
|
||||
elseif l:text_changed is? 'insert'
|
||||
autocmd TextChangedI * call ale#Queue(g:ale_lint_delay)
|
||||
endif
|
||||
endif
|
||||
augroup END
|
||||
|
||||
augroup ALERunOnEnterGroup
|
||||
autocmd!
|
||||
if g:ale_enabled
|
||||
" Handle everything that needs to happen when buffers are entered.
|
||||
autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand('<abuf>')))
|
||||
endif
|
||||
if g:ale_enabled && g:ale_lint_on_enter
|
||||
autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand('<abuf>')))
|
||||
" Track when the file is changed outside of Vim.
|
||||
autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>')))
|
||||
endif
|
||||
augroup END
|
||||
|
||||
augroup ALERunOnFiletypeChangeGroup
|
||||
autocmd!
|
||||
if g:ale_enabled && g:ale_lint_on_filetype_changed
|
||||
" Only start linting if the FileType actually changes after
|
||||
" opening a buffer. The FileType will fire when buffers are opened.
|
||||
autocmd FileType * call ale#events#FileTypeEvent(
|
||||
\ str2nr(expand('<abuf>')),
|
||||
\ expand('<amatch>')
|
||||
\)
|
||||
endif
|
||||
augroup END
|
||||
|
||||
augroup ALERunOnSaveGroup
|
||||
autocmd!
|
||||
autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand('<abuf>')))
|
||||
augroup END
|
||||
|
||||
augroup ALERunOnInsertLeave
|
||||
autocmd!
|
||||
if g:ale_enabled && g:ale_lint_on_insert_leave
|
||||
autocmd InsertLeave * call ale#Queue(0)
|
||||
endif
|
||||
augroup END
|
||||
|
||||
augroup ALECursorGroup
|
||||
autocmd!
|
||||
if g:ale_enabled && g:ale_echo_cursor
|
||||
autocmd CursorMoved,CursorHold * call ale#cursor#EchoCursorWarningWithDelay()
|
||||
" Look for a warning to echo as soon as we leave Insert mode.
|
||||
" The script's position variable used when moving the cursor will
|
||||
" not be changed here.
|
||||
autocmd InsertLeave * call ale#cursor#EchoCursorWarning()
|
||||
endif
|
||||
augroup END
|
||||
|
||||
if !g:ale_enabled
|
||||
augroup! ALERunOnTextChangedGroup
|
||||
augroup! ALERunOnEnterGroup
|
||||
augroup! ALERunOnInsertLeave
|
||||
augroup! ALECursorGroup
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:EnablePreamble() abort
|
||||
" Set pattern options again, if enabled.
|
||||
if g:ale_pattern_options_enabled
|
||||
call ale#pattern_options#SetOptions(bufnr(''))
|
||||
endif
|
||||
|
||||
" Lint immediately, including running linters against the file.
|
||||
call ale#Queue(0, 'lint_file')
|
||||
endfunction
|
||||
|
||||
function! s:DisablePostamble() abort
|
||||
" Remove highlights for the current buffer now.
|
||||
if g:ale_set_highlights
|
||||
call ale#highlight#UpdateHighlights()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:CleanupEveryBuffer() abort
|
||||
for l:key in keys(g:ale_buffer_info)
|
||||
" The key could be a filename or a buffer number, so try and
|
||||
" convert it to a number. We need a number for the other
|
||||
" functions.
|
||||
let l:buffer = str2nr(l:key)
|
||||
|
||||
if l:buffer > 0
|
||||
" Stop all jobs and clear the results for everything, and delete
|
||||
" all of the data we stored for the buffer.
|
||||
call ale#engine#Cleanup(l:buffer)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#Toggle() abort
|
||||
let g:ale_enabled = !get(g:, 'ale_enabled')
|
||||
|
||||
if g:ale_enabled
|
||||
call s:EnablePreamble()
|
||||
|
||||
if g:ale_set_balloons
|
||||
call ale#balloon#Enable()
|
||||
endif
|
||||
else
|
||||
call s:CleanupEveryBuffer()
|
||||
call s:DisablePostamble()
|
||||
|
||||
if has('balloon_eval')
|
||||
call ale#balloon#Disable()
|
||||
endif
|
||||
endif
|
||||
|
||||
call ale#toggle#InitAuGroups()
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#Enable() abort
|
||||
if !g:ale_enabled
|
||||
" Set pattern options again, if enabled.
|
||||
if g:ale_pattern_options_enabled
|
||||
call ale#pattern_options#SetOptions(bufnr(''))
|
||||
endif
|
||||
|
||||
call ale#toggle#Toggle()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#Disable() abort
|
||||
if g:ale_enabled
|
||||
call ale#toggle#Toggle()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#Reset() abort
|
||||
call s:CleanupEveryBuffer()
|
||||
call ale#highlight#UpdateHighlights()
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#ToggleBuffer(buffer) abort
|
||||
" Get the new value for the toggle.
|
||||
let l:enabled = !getbufvar(a:buffer, 'ale_enabled', 1)
|
||||
|
||||
" Disabling ALE globally removes autocmd events, so we cannot enable
|
||||
" linting locally when linting is disabled globally
|
||||
if l:enabled && !g:ale_enabled
|
||||
execute 'echom ''ALE cannot be enabled locally when disabled globally'''
|
||||
return
|
||||
endif
|
||||
|
||||
call setbufvar(a:buffer, 'ale_enabled', l:enabled)
|
||||
|
||||
if l:enabled
|
||||
call s:EnablePreamble()
|
||||
else
|
||||
" Stop all jobs and clear the results for everything, and delete
|
||||
" all of the data we stored for the buffer.
|
||||
call ale#engine#Cleanup(a:buffer)
|
||||
call s:DisablePostamble()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#EnableBuffer(buffer) abort
|
||||
" ALE is enabled by default for all buffers.
|
||||
if !getbufvar(a:buffer, 'ale_enabled', 1)
|
||||
call ale#toggle#ToggleBuffer(a:buffer)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#DisableBuffer(buffer) abort
|
||||
if getbufvar(a:buffer, 'ale_enabled', 1)
|
||||
call ale#toggle#ToggleBuffer(a:buffer)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#toggle#ResetBuffer(buffer) abort
|
||||
call ale#engine#Cleanup(a:buffer)
|
||||
call ale#highlight#UpdateHighlights()
|
||||
endfunction
|
18
sources_non_forked/ale/autoload/ale/uri.vim
Normal file
18
sources_non_forked/ale/autoload/ale/uri.vim
Normal file
@ -0,0 +1,18 @@
|
||||
" This probably doesn't handle Unicode characters well.
|
||||
function! ale#uri#Encode(value) abort
|
||||
return substitute(
|
||||
\ a:value,
|
||||
\ '\([^a-zA-Z0-9\\/$\-_.!*''(),]\)',
|
||||
\ '\=printf(''%%%02x'', char2nr(submatch(1)))',
|
||||
\ 'g'
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! ale#uri#Decode(value) abort
|
||||
return substitute(
|
||||
\ a:value,
|
||||
\ '%\(\x\x\)',
|
||||
\ '\=nr2char(''0x'' . submatch(1))',
|
||||
\ 'g'
|
||||
\)
|
||||
endfunction
|
313
sources_non_forked/ale/autoload/ale/util.vim
Normal file
313
sources_non_forked/ale/autoload/ale/util.vim
Normal file
@ -0,0 +1,313 @@
|
||||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: Contains miscellaneous functions
|
||||
|
||||
" A wrapper function for mode() so we can test calls for it.
|
||||
function! ale#util#Mode(...) abort
|
||||
return call('mode', a:000)
|
||||
endfunction
|
||||
|
||||
" A wrapper function for feedkeys so we can test calls for it.
|
||||
function! ale#util#FeedKeys(...) abort
|
||||
return call('feedkeys', a:000)
|
||||
endfunction
|
||||
|
||||
if !exists('g:ale#util#nul_file')
|
||||
" A null file for sending output to nothing.
|
||||
let g:ale#util#nul_file = '/dev/null'
|
||||
|
||||
if has('win32')
|
||||
let g:ale#util#nul_file = 'nul'
|
||||
endif
|
||||
endif
|
||||
|
||||
" Return the number of lines for a given buffer.
|
||||
function! ale#util#GetLineCount(buffer) abort
|
||||
return len(getbufline(a:buffer, 1, '$'))
|
||||
endfunction
|
||||
|
||||
function! ale#util#GetFunction(string_or_ref) abort
|
||||
if type(a:string_or_ref) == type('')
|
||||
return function(a:string_or_ref)
|
||||
endif
|
||||
|
||||
return a:string_or_ref
|
||||
endfunction
|
||||
|
||||
" Compare two loclist items for ALE, sorted by their buffers, filenames, and
|
||||
" line numbers and column numbers.
|
||||
function! ale#util#LocItemCompare(left, right) abort
|
||||
if a:left.bufnr < a:right.bufnr
|
||||
return -1
|
||||
endif
|
||||
|
||||
if a:left.bufnr > a:right.bufnr
|
||||
return 1
|
||||
endif
|
||||
|
||||
if a:left.bufnr == -1
|
||||
if a:left.filename < a:right.filename
|
||||
return -1
|
||||
endif
|
||||
|
||||
if a:left.filename > a:right.filename
|
||||
return 1
|
||||
endif
|
||||
endif
|
||||
|
||||
if a:left.lnum < a:right.lnum
|
||||
return -1
|
||||
endif
|
||||
|
||||
if a:left.lnum > a:right.lnum
|
||||
return 1
|
||||
endif
|
||||
|
||||
if a:left.col < a:right.col
|
||||
return -1
|
||||
endif
|
||||
|
||||
if a:left.col > a:right.col
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Compare two loclist items, including the text for the items.
|
||||
"
|
||||
" This function can be used for de-duplicating lists.
|
||||
function! ale#util#LocItemCompareWithText(left, right) abort
|
||||
let l:cmp_value = ale#util#LocItemCompare(a:left, a:right)
|
||||
|
||||
if l:cmp_value
|
||||
return l:cmp_value
|
||||
endif
|
||||
|
||||
if a:left.text < a:right.text
|
||||
return -1
|
||||
endif
|
||||
|
||||
if a:left.text > a:right.text
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" This function will perform a binary search and a small sequential search
|
||||
" on the list to find the last problem in the buffer and line which is
|
||||
" on or before the column. The index of the problem will be returned.
|
||||
"
|
||||
" -1 will be returned if nothing can be found.
|
||||
function! ale#util#BinarySearch(loclist, buffer, line, column) abort
|
||||
let l:min = 0
|
||||
let l:max = len(a:loclist) - 1
|
||||
|
||||
while 1
|
||||
if l:max < l:min
|
||||
return -1
|
||||
endif
|
||||
|
||||
let l:mid = (l:min + l:max) / 2
|
||||
let l:item = a:loclist[l:mid]
|
||||
|
||||
" Binary search for equal buffers, equal lines, then near columns.
|
||||
if l:item.bufnr < a:buffer
|
||||
let l:min = l:mid + 1
|
||||
elseif l:item.bufnr > a:buffer
|
||||
let l:max = l:mid - 1
|
||||
elseif l:item.lnum < a:line
|
||||
let l:min = l:mid + 1
|
||||
elseif l:item.lnum > a:line
|
||||
let l:max = l:mid - 1
|
||||
else
|
||||
" This part is a small sequential search.
|
||||
let l:index = l:mid
|
||||
|
||||
" Search backwards to find the first problem on the line.
|
||||
while l:index > 0
|
||||
\&& a:loclist[l:index - 1].bufnr == a:buffer
|
||||
\&& a:loclist[l:index - 1].lnum == a:line
|
||||
let l:index -= 1
|
||||
endwhile
|
||||
|
||||
" Find the last problem on or before this column.
|
||||
while l:index < l:max
|
||||
\&& a:loclist[l:index + 1].bufnr == a:buffer
|
||||
\&& a:loclist[l:index + 1].lnum == a:line
|
||||
\&& a:loclist[l:index + 1].col <= a:column
|
||||
let l:index += 1
|
||||
endwhile
|
||||
|
||||
return l:index
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
" A function for testing if a function is running inside a sandbox.
|
||||
" See :help sandbox
|
||||
function! ale#util#InSandbox() abort
|
||||
try
|
||||
function! s:SandboxCheck() abort
|
||||
endfunction
|
||||
catch /^Vim\%((\a\+)\)\=:E48/
|
||||
" E48 is the sandbox error.
|
||||
return 1
|
||||
endtry
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Get the number of milliseconds since some vague, but consistent, point in
|
||||
" the past.
|
||||
"
|
||||
" This function can be used for timing execution, etc.
|
||||
"
|
||||
" The time will be returned as a Number.
|
||||
function! ale#util#ClockMilliseconds() abort
|
||||
return float2nr(reltimefloat(reltime()) * 1000)
|
||||
endfunction
|
||||
|
||||
" Given a single line, or a List of lines, and a single pattern, or a List
|
||||
" of patterns, return all of the matches for the lines(s) from the given
|
||||
" patterns, using matchlist().
|
||||
"
|
||||
" Only the first pattern which matches a line will be returned.
|
||||
function! ale#util#GetMatches(lines, patterns) abort
|
||||
let l:matches = []
|
||||
let l:lines = type(a:lines) == type([]) ? a:lines : [a:lines]
|
||||
let l:patterns = type(a:patterns) == type([]) ? a:patterns : [a:patterns]
|
||||
|
||||
for l:line in l:lines
|
||||
for l:pattern in l:patterns
|
||||
let l:match = matchlist(l:line, l:pattern)
|
||||
|
||||
if !empty(l:match)
|
||||
call add(l:matches, l:match)
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return l:matches
|
||||
endfunction
|
||||
|
||||
function! s:LoadArgCount(function) abort
|
||||
let l:Function = a:function
|
||||
|
||||
redir => l:output
|
||||
silent! function Function
|
||||
redir END
|
||||
|
||||
if !exists('l:output')
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:match = matchstr(split(l:output, "\n")[0], '\v\([^)]+\)')[1:-2]
|
||||
let l:arg_list = filter(split(l:match, ', '), 'v:val isnot# ''...''')
|
||||
|
||||
return len(l:arg_list)
|
||||
endfunction
|
||||
|
||||
" Given the name of a function, a Funcref, or a lambda, return the number
|
||||
" of named arguments for a function.
|
||||
function! ale#util#FunctionArgCount(function) abort
|
||||
let l:Function = ale#util#GetFunction(a:function)
|
||||
let l:count = s:LoadArgCount(l:Function)
|
||||
|
||||
" If we failed to get the count, forcibly load the autoload file, if the
|
||||
" function is an autoload function. autoload functions aren't normally
|
||||
" defined until they are called.
|
||||
if l:count == 0
|
||||
let l:function_name = matchlist(string(l:Function), 'function([''"]\(.\+\)[''"])')[1]
|
||||
|
||||
if l:function_name =~# '#'
|
||||
execute 'runtime autoload/' . join(split(l:function_name, '#')[:-2], '/') . '.vim'
|
||||
let l:count = s:LoadArgCount(l:Function)
|
||||
endif
|
||||
endif
|
||||
|
||||
return l:count
|
||||
endfunction
|
||||
|
||||
" Escape a string so the characters in it will be safe for use inside of PCRE
|
||||
" or RE2 regular expressions without characters having special meanings.
|
||||
function! ale#util#EscapePCRE(unsafe_string) abort
|
||||
return substitute(a:unsafe_string, '\([\-\[\]{}()*+?.^$|]\)', '\\\1', 'g')
|
||||
endfunction
|
||||
|
||||
" Escape a string so that it can be used as a literal string inside an evaled
|
||||
" vim command.
|
||||
function! ale#util#EscapeVim(unsafe_string) abort
|
||||
return "'" . substitute(a:unsafe_string, "'", "''", 'g') . "'"
|
||||
endfunction
|
||||
|
||||
|
||||
" Given a String or a List of String values, try and decode the string(s)
|
||||
" as a JSON value which can be decoded with json_decode. If the JSON string
|
||||
" is invalid, the default argument value will be returned instead.
|
||||
"
|
||||
" This function is useful in code where the data can't be trusted to be valid
|
||||
" JSON, and where throwing exceptions is mostly just irritating.
|
||||
function! ale#util#FuzzyJSONDecode(data, default) abort
|
||||
if empty(a:data)
|
||||
return a:default
|
||||
endif
|
||||
|
||||
let l:str = type(a:data) == type('') ? a:data : join(a:data, '')
|
||||
|
||||
try
|
||||
let l:result = json_decode(l:str)
|
||||
|
||||
" Vim 8 only uses the value v:none for decoding blank strings.
|
||||
if !has('nvim') && l:result is v:none
|
||||
return a:default
|
||||
endif
|
||||
|
||||
return l:result
|
||||
catch /E474/
|
||||
return a:default
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Write a file, including carriage return characters for DOS files.
|
||||
"
|
||||
" The buffer number is required for determining the fileformat setting for
|
||||
" the buffer.
|
||||
function! ale#util#Writefile(buffer, lines, filename) abort
|
||||
let l:corrected_lines = getbufvar(a:buffer, '&fileformat') is# 'dos'
|
||||
\ ? map(copy(a:lines), 'v:val . "\r"')
|
||||
\ : a:lines
|
||||
|
||||
call writefile(l:corrected_lines, a:filename) " no-custom-checks
|
||||
endfunction
|
||||
|
||||
if !exists('s:patial_timers')
|
||||
let s:partial_timers = {}
|
||||
endif
|
||||
|
||||
function! s:ApplyPartialTimer(timer_id) abort
|
||||
let [l:Callback, l:args] = remove(s:partial_timers, a:timer_id)
|
||||
call call(l:Callback, [a:timer_id] + l:args)
|
||||
endfunction
|
||||
|
||||
" Given a delay, a callback, a List of arguments, start a timer with
|
||||
" timer_start() and call the callback provided with [timer_id] + args.
|
||||
"
|
||||
" The timer must not be stopped with timer_stop().
|
||||
" Use ale#util#StopPartialTimer() instead, which can stop any timer, and will
|
||||
" clear any arguments saved for executing callbacks later.
|
||||
function! ale#util#StartPartialTimer(delay, callback, args) abort
|
||||
let l:timer_id = timer_start(a:delay, function('s:ApplyPartialTimer'))
|
||||
let s:partial_timers[l:timer_id] = [a:callback, a:args]
|
||||
|
||||
return l:timer_id
|
||||
endfunction
|
||||
|
||||
function! ale#util#StopPartialTimer(timer_id) abort
|
||||
call timer_stop(a:timer_id)
|
||||
|
||||
if has_key(s:partial_timers, a:timer_id)
|
||||
call remove(s:partial_timers, a:timer_id)
|
||||
endif
|
||||
endfunction
|
Reference in New Issue
Block a user