1
0
mirror of https://github.com/amix/vimrc synced 2025-06-16 01:25:00 +08:00

Updated plugins

This commit is contained in:
Amir Salihefendic
2018-06-14 12:31:12 +02:00
parent 7288aee801
commit 3e3297af67
273 changed files with 11821 additions and 5377 deletions

View File

@ -2,6 +2,11 @@
" Description: Primary code path for the plugin
" Manages execution of linters when requested by autocommands
" Strings used for severity in the echoed message
let g:ale_echo_msg_error_str = get(g:, 'ale_echo_msg_error_str', 'Error')
let g:ale_echo_msg_info_str = get(g:, 'ale_echo_msg_info_str', 'Info')
let g:ale_echo_msg_warning_str = get(g:, 'ale_echo_msg_warning_str', 'Warning')
let s:lint_timer = -1
let s:queued_buffer_number = -1
let s:should_lint_file_for_buffer = {}
@ -32,8 +37,8 @@ function! ale#CallWithCooldown(timestamp_key, func, arglist) abort
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')
function! ale#FileTooLarge(buffer) abort
let l:max = getbufvar(a:buffer, 'ale_maximum_file_size', get(g:, 'ale_maximum_file_size', 0))
return l:max > 0 ? (line2byte(line('$') + 1) > l:max) : 0
endfunction
@ -46,39 +51,47 @@ 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.
" Do nothing if ALE is disabled.
if !getbufvar(a:buffer, 'ale_enabled', get(g:, 'ale_enabled', 0))
return 1
endif
" 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
let l:filetype = getbufvar(a:buffer, '&filetype')
" Do nothing when there's no filetype.
if l:filetype is# ''
return 1
endif
" Do nothing if running from command mode
" Do nothing for blacklisted files.
if index(get(g:, 'ale_filetype_blacklist', []), l: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')
" Do nothing for directories.
if l:filename is# '.'
return 1
endif
" Do nothing if running in the sandbox
" 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()
if ale#FileTooLarge(a:buffer)
return 1
endif

View File

@ -0,0 +1,77 @@
function! ale#autocmd#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

View File

@ -12,7 +12,21 @@ function! ale#balloon#MessageForPos(bufnr, lnum, col) abort
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 : ''
" Show the diagnostics message if found, 'Hover' output otherwise
if l:index >= 0
return l:loclist[l:index].text
elseif exists('*balloon_show') || getbufvar(
\ a:bufnr,
\ 'ale_set_balloons_legacy_echo',
\ get(g:, 'ale_set_balloons_legacy_echo', 0)
\)
" Request LSP/tsserver hover information, but only if this version of
" Vim supports the balloon_show function, or if we turned a legacy
" setting on.
call ale#hover#Show(a:bufnr, a:lnum, a:col, {'called_from_balloonexpr': 1})
endif
return ''
endfunction
function! ale#balloon#Expr() abort
@ -20,9 +34,22 @@ function! ale#balloon#Expr() abort
endfunction
function! ale#balloon#Disable() abort
set noballooneval balloonexpr=
set noballooneval noballoonevalterm
set balloonexpr=
endfunction
function! ale#balloon#Enable() abort
set ballooneval balloonexpr=ale#balloon#Expr()
if !has('balloon_eval') && !has('balloon_eval_term')
return
endif
if has('balloon_eval')
set ballooneval
endif
if has('balloon_eval_term')
set balloonevalterm
endif
set balloonexpr=ale#balloon#Expr()
endfunction

View File

@ -1,6 +1,10 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Completion support for LSP linters
let g:ale_completion_delay = get(g:, 'ale_completion_delay', 100)
let g:ale_completion_excluded_words = get(g:, 'ale_completion_excluded_words', [])
let g:ale_completion_max_suggestions = get(g:, 'ale_completion_max_suggestions', 50)
let s:timer_id = -1
let s:last_done_pos = []
@ -28,6 +32,8 @@ let s:LSP_COMPLETION_REFERENCE_KIND = 18
" 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]*$|\.$',
\ 'typescript': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$|''$|"$',
\ 'rust': '\v[a-zA-Z$_][a-zA-Z$_0-9]*$|\.$|::$',
\}
" Regular expressions for finding the start column to replace with completion.
@ -38,6 +44,8 @@ let s:omni_start_map = {
" A map of exact characters for triggering LSP completions.
let s:trigger_character_map = {
\ '<default>': ['.'],
\ 'typescript': ['.', '''', '"'],
\ 'rust': ['.', '::'],
\}
function! s:GetFiletypeValue(map, filetype) abort
@ -74,33 +82,49 @@ function! ale#completion#GetTriggerCharacter(filetype, prefix) abort
return ''
endfunction
function! ale#completion#Filter(suggestions, prefix) abort
function! ale#completion#Filter(buffer, suggestions, prefix) abort
let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words')
" For completing...
" foo.
" ^
" We need to include all of the given suggestions.
if a:prefix is# '.'
return a:suggestions
let l:filtered_suggestions = a:suggestions
else
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
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)
if !empty(l:excluded_words)
" Copy the List if needed. We don't want to modify the argument.
" We shouldn't make a copy if we don't need to.
if l:filtered_suggestions is a:suggestions
let l:filtered_suggestions = copy(a:suggestions)
endif
endfor
" Remove suggestions with words in the exclusion List.
call filter(
\ l:filtered_suggestions,
\ 'index(l:excluded_words, type(v:val) is type('''') ? v:val : v:val.word) < 0',
\)
endif
return l:filtered_suggestions
endfunction
@ -178,7 +202,9 @@ function! ale#completion#ParseTSServerCompletions(response) abort
endfunction
function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
let l:buffer = bufnr('')
let l:results = []
let l:names_with_details = []
for l:suggestion in a:response.body
let l:displayParts = []
@ -212,10 +238,44 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
\})
endfor
let l:names = getbufvar(l:buffer, 'ale_tsserver_completion_names', [])
if !empty(l:names) && len(l:names) != len(l:results)
let l:names_with_details = map(copy(l:results), 'v:val.word')
let l:missing_names = filter(
\ copy(l:names),
\ 'index(l:names_with_details, v:val) < 0',
\)
for l:name in l:missing_names
call add(l:results, {
\ 'word': l:name,
\ 'kind': 'v',
\ 'icase': 1,
\ 'menu': '',
\ 'info': '',
\})
endfor
endif
return l:results
endfunction
function! ale#completion#NullFilter(buffer, item) abort
return 1
endfunction
function! ale#completion#ParseLSPCompletions(response) abort
let l:buffer = bufnr('')
let l:info = get(b:, 'ale_completion_info', {})
let l:Filter = get(l:info, 'completion_filter', v:null)
if l:Filter is v:null
let l:Filter = function('ale#completion#NullFilter')
else
let l:Filter = ale#util#GetFunction(l:Filter)
endif
let l:item_list = []
if type(get(a:response, 'result')) is type([])
@ -228,6 +288,16 @@ function! ale#completion#ParseLSPCompletions(response) abort
let l:results = []
for l:item in l:item_list
if !call(l:Filter, [l:buffer, l:item])
continue
endif
let l:word = matchstr(l:item.label, '\v^[^(]+')
if empty(l:word)
continue
endif
" See :help complete-items for Vim completion kinds
if l:item.kind is s:LSP_COMPLETION_METHOD_KIND
let l:kind = 'm'
@ -244,14 +314,18 @@ function! ale#completion#ParseLSPCompletions(response) abort
endif
call add(l:results, {
\ 'word': l:item.label,
\ 'word': l:word,
\ 'kind': l:kind,
\ 'icase': 1,
\ 'menu': l:item.detail,
\ 'info': l:item.documentation,
\ 'menu': get(l:item, 'detail', ''),
\ 'info': get(l:item, 'documentation', ''),
\})
endfor
if has_key(l:info, 'prefix')
return ale#completion#Filter(l:buffer, l:results, l:info.prefix)
endif
return l:results
endfunction
@ -264,19 +338,25 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort
return
endif
let l:buffer = bufnr('')
let l:command = get(a:response, 'command', '')
if l:command is# 'completions'
let l:names = ale#completion#Filter(
\ l:buffer,
\ ale#completion#ParseTSServerCompletions(a:response),
\ b:ale_completion_info.prefix,
\)[: g:ale_completion_max_suggestions - 1]
" We need to remember some names for tsserver, as it doesn't send
" details back for everything we send.
call setbufvar(l:buffer, 'ale_tsserver_completion_names', l:names)
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(''),
\ l:buffer,
\ b:ale_completion_info.line,
\ b:ale_completion_info.column,
\ l:names,
@ -349,6 +429,10 @@ function! s:GetLSPCompletions(linter) abort
if l:request_id
let b:ale_completion_info.conn_id = l:id
let b:ale_completion_info.request_id = l:request_id
if has_key(a:linter, 'completion_filter')
let b:ale_completion_info.completion_filter = a:linter.completion_filter
endif
endif
endfunction
@ -378,10 +462,7 @@ function! ale#completion#GetCompletions() abort
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
call s:GetLSPCompletions(l:linter)
endif
endfor
endfunction

View File

@ -0,0 +1,3 @@
function! ale#completion#python#CompletionItemFilter(buffer, item) abort
return a:item.label !~# '\v^__[a-z_]+__'
endfunction

View File

@ -1,6 +1,11 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Echoes lint message for the current line, if any
" Controls the milliseconds delay before echoing a message.
let g:ale_echo_delay = get(g:, 'ale_echo_delay', 10)
" A string format for the echoed message.
let g:ale_echo_msg_format = get(g:, 'ale_echo_msg_format', '%code: %%s')
let s:cursor_timer = -1
let s:last_pos = [0, 0, 0]

View File

@ -51,6 +51,7 @@ let s:global_variable_list = [
\ 'ale_sign_warning',
\ 'ale_statusline_format',
\ 'ale_type_map',
\ 'ale_use_global_executables',
\ 'ale_warn_about_trailing_blank_lines',
\ 'ale_warn_about_trailing_whitespace',
\]
@ -167,6 +168,30 @@ function! s:EchoLinterAliases(all_linters) abort
endfor
endfunction
function! s:EchoLSPErrorMessages(all_linter_names) abort
let l:lsp_error_messages = get(g:, 'ale_lsp_error_messages', {})
let l:header_echoed = 0
for l:linter_name in a:all_linter_names
let l:error_list = get(l:lsp_error_messages, l:linter_name, [])
if !empty(l:error_list)
if !l:header_echoed
call s:Echo(' LSP Error Messages:')
call s:Echo('')
endif
call s:Echo('(Errors for ' . l:linter_name . ')')
for l:message in l:error_list
for l:line in split(l:message, "\n")
call s:Echo(l:line)
endfor
endfor
endif
endfor
endfunction
function! ale#debugging#Info() abort
let l:filetype = &filetype
@ -199,6 +224,7 @@ function! ale#debugging#Info() abort
call s:Echo(' Global Variables:')
call s:Echo('')
call s:EchoGlobalVariables()
call s:EchoLSPErrorMessages(l:all_names)
call s:Echo(' Command History:')
call s:Echo('')
call s:EchoCommandHistory()
@ -211,3 +237,14 @@ function! ale#debugging#InfoToClipboard() abort
call s:Echo('ALEInfo copied to your clipboard')
endfunction
function! ale#debugging#InfoToFile(filename) abort
let l:expanded_filename = expand(a:filename)
redir => l:output
silent call ale#debugging#Info()
redir END
call writefile(split(l:output, "\n"), l:expanded_filename)
call s:Echo('ALEInfo written to ' . l:expanded_filename)
endfunction

View File

@ -13,37 +13,21 @@ 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
if get(a:response, 'success', v:false) is v:true && !empty(a:response.body)
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)
call ale#util#Open(l:filename, l:line, l:column, l:options)
endif
endif
endfunction
@ -67,7 +51,7 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort
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)
call ale#util#Open(l:filename, l:line, l:column, l:options)
break
endfor
endif

View File

@ -2,6 +2,9 @@
" Description: Backend execution and job management
" Executes linters in the background, using NeoVim or Vim 8 jobs
" Remapping of linter problems.
let g:ale_type_map = get(g:, 'ale_type_map', {})
" Stores information for each job including:
"
" linter: The linter dictionary for the job.
@ -44,7 +47,7 @@ function! ale#engine#IsExecutable(buffer, executable) abort
" 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
if l:result || get(g:, 'ale_cache_executable_check_failures', 0)
let s:executable_cache_map[a:executable] = l:result
endif
@ -81,6 +84,11 @@ function! ale#engine#ClearLSPData() abort
let s:lsp_linter_map = {}
endfunction
" Just for tests.
function! ale#engine#SetLSPLinterMap(replacement_map) abort
let s:lsp_linter_map = a:replacement_map
endfunction
" This function is documented and part of the public API.
"
" Return 1 if ALE is busy checking a given buffer
@ -93,11 +101,13 @@ 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 ale#engine#InitBufferInfo(a:buffer)
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 ale#engine#InitBufferInfo(a:buffer)
call add(g:ale_buffer_info[a:buffer].temporary_directory_list, a:directory)
endfunction
@ -270,20 +280,38 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort
call ale#engine#HandleLoclist('tsserver', l:buffer, l:loclist)
endfunction
function! s:HandleLSPErrorMessage(error_message) abort
execute 'echoerr ''Error from LSP:'''
function! s:HandleLSPErrorMessage(linter_name, response) abort
if !g:ale_history_enabled || !g:ale_history_log_output
return
endif
for l:line in split(a:error_message, "\n")
execute 'echoerr l:line'
endfor
if empty(a:linter_name)
return
endif
let l:message = ale#lsp#response#GetErrorMessage(a:response)
if empty(l:message)
return
endif
" This global variable is set here so we don't load the debugging.vim file
" until someone uses :ALEInfo.
let g:ale_lsp_error_messages = get(g:, 'ale_lsp_error_messages', {})
if !has_key(g:ale_lsp_error_messages, a:linter_name)
let g:ale_lsp_error_messages[a:linter_name] = []
endif
call add(g:ale_lsp_error_messages[a:linter_name], l:message)
endfunction
function! ale#engine#HandleLSPResponse(conn_id, response) abort
let l:method = get(a:response, 'method', '')
let l:linter_name = get(s:lsp_linter_map, a:conn_id, '')
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)
call s:HandleLSPErrorMessage(l:linter_name, a:response)
elseif l:method is# 'textDocument/publishDiagnostics'
call s:HandleLSPDiagnostics(a:conn_id, a:response)
elseif get(a:response, 'type', '') is# 'event'
@ -339,6 +367,7 @@ function! ale#engine#SetResults(buffer, loclist) abort
" Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd <nomodeline> User ALELintPost
" remove in 2.0
" Old DEPRECATED name; call it for backwards compatibility.
silent doautocmd <nomodeline> User ALELint
endif
@ -393,7 +422,7 @@ function! ale#engine#FixLocList(buffer, linter_name, loclist) abort
\ '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),
\ 'vcol': 0,
\ 'type': get(l:old_item, 'type', 'E'),
\ 'nr': get(l:old_item, 'nr', -1),
\ 'linter_name': a:linter_name,
@ -453,6 +482,20 @@ function! ale#engine#FixLocList(buffer, linter_name, loclist) abort
" 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
elseif get(l:old_item, 'vcol', 0)
" Convert virtual column positions to byte positions.
" The positions will be off if the buffer has changed recently.
let l:line = getbufline(a:buffer, l:item.lnum)[0]
let l:item.col = ale#util#Col(l:line, l:item.col)
if has_key(l:item, 'end_col')
let l:end_line = get(l:item, 'end_lnum', l:line) != l:line
\ ? getbufline(a:buffer, l:item.end_lnum)[0]
\ : l:line
let l:item.end_col = ale#util#Col(l:end_line, l:item.end_col)
endif
endif
call add(l:new_loclist, l:item)
@ -572,6 +615,8 @@ function! s:RunJob(options) abort
\ 'output': [],
\ 'next_chain_index': l:next_chain_index,
\}
silent doautocmd <nomodeline> User ALEJobStarted
endif
if g:ale_history_enabled

View File

@ -19,7 +19,7 @@ function! ale#events#SaveEvent(buffer) abort
endif
if ale#Var(a:buffer, 'fix_on_save')
let l:will_fix = ale#fix#Fix('save_file')
let l:will_fix = ale#fix#Fix(a:buffer, 'save_file')
let l:should_lint = l:should_lint && !l:will_fix
endif

View File

@ -28,8 +28,6 @@ function! ale#fix#ApplyQueuedFixes() abort
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)
@ -39,6 +37,8 @@ function! ale#fix#ApplyQueuedFixes() abort
call winrestview(l:save)
endif
call setline(1, l:data.output)
if l:data.should_save
if empty(&buftype)
noautocmd :w!
@ -356,14 +356,16 @@ function! s:RunFixer(options) abort
call ale#fix#ApplyFixes(l:buffer, l:input)
endfunction
function! s:GetCallbacks() abort
if type(get(b:, 'ale_fixers')) is type([])
function! s:GetCallbacks(buffer, linters) abort
if len(a:linters)
let l:callback_list = a:linters
elseif 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:fixers = ale#Var(a:buffer, 'fixers')
let l:callback_list = []
for l:sub_type in split(&filetype, '\.')
@ -422,19 +424,13 @@ 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'
function! ale#fix#Fix(buffer, fixing_flag, ...) abort
if a:fixing_flag isnot# '' && a:fixing_flag isnot# 'save_file'
throw "fixing_flag must be either '' or 'save_file'"
endif
try
let l:callback_list = s:GetCallbacks()
let l:callback_list = s:GetCallbacks(a:buffer, a:000)
catch /E700\|BADNAME/
let l:function_name = join(split(split(v:exception, ':')[3]))
let l:echo_message = printf(
@ -447,29 +443,27 @@ function! ale#fix#Fix(...) abort
endtry
if empty(l:callback_list)
if l:fixing_flag is# ''
if a: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)
call ale#fix#RemoveManagedFiles(a:buffer)
call ale#fix#InitBufferData(a:buffer, a:fixing_flag)
silent doautocmd <nomodeline> User ALEFixPre
call s:RunFixer({
\ 'buffer': l:buffer,
\ 'input': g:ale_fix_buffer_data[l:buffer].lines_before,
\ 'buffer': a:buffer,
\ 'input': g:ale_fix_buffer_data[a:buffer].lines_before,
\ 'callback_index': 0,
\ 'callback_list': l:callback_list,
\})

View File

@ -17,6 +17,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['python'],
\ 'description': 'Fix PEP8 issues with autopep8.',
\ },
\ 'black': {
\ 'function': 'ale#fixers#black#Fix',
\ 'suggested_filetypes': ['python'],
\ 'description': 'Fix PEP8 issues with black.',
\ },
\ 'prettier_standard': {
\ 'function': 'ale#fixers#prettier_standard#Fix',
\ 'suggested_filetypes': ['javascript'],
@ -90,6 +95,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['ruby'],
\ 'description': 'Fix ruby files with rufo',
\ },
\ 'scalafmt': {
\ 'function': 'ale#fixers#scalafmt#Fix',
\ 'suggested_filetypes': ['scala'],
\ 'description': 'Fix Scala files using scalafmt',
\ },
\ 'standard': {
\ 'function': 'ale#fixers#standard#Fix',
\ 'suggested_filetypes': ['javascript'],
@ -180,6 +190,21 @@ let s:default_registry = {
\ 'suggested_filetypes': ['json'],
\ 'description': 'Fix JSON files with jq.',
\ },
\ 'perltidy': {
\ 'function': 'ale#fixers#perltidy#Fix',
\ 'suggested_filetypes': ['perl'],
\ 'description': 'Fix Perl files with perltidy.',
\ },
\ 'xo': {
\ 'function': 'ale#fixers#xo#Fix',
\ 'suggested_filetypes': ['javascript'],
\ 'description': 'Fix JavaScript files using xo --fix.',
\ },
\ 'qmlfmt': {
\ 'function': 'ale#fixers#qmlfmt#Fix',
\ 'suggested_filetypes': ['qml'],
\ 'description': 'Fix QML files with qmlfmt.',
\ },
\}
" Reset the function registry to the default entries.
@ -272,6 +297,14 @@ function! s:ShouldSuggestForType(suggested_filetypes, type_list) abort
return 0
endfunction
function! s:IsGenericFixer(suggested_filetypes) abort
if empty(a:suggested_filetypes)
return 1
endif
return 0
endfunction
function! s:FormatEntry(key, entry) abort
let l:aliases_str = ''
@ -291,6 +324,27 @@ function! s:FormatEntry(key, entry) abort
\)
endfunction
" Get list of applicable fixers for filetype, including generic fixers
function! ale#fix#registry#GetApplicableFixers(filetype) abort
let l:type_list = split(a:filetype, '\.')
let l:fixer_name_list = []
for l:key in sort(keys(s:entries))
let l:suggested_filetypes = s:entries[l:key].suggested_filetypes
if s:IsGenericFixer(l:suggested_filetypes) || s:ShouldSuggestForType(l:suggested_filetypes, l:type_list)
call add(l:fixer_name_list, l:key)
endif
endfor
return l:fixer_name_list
endfunction
" Function that returns autocomplete candidates for ALEFix command
function! ale#fix#registry#CompleteFixers(ArgLead, CmdLine, CursorPos) abort
return filter(ale#fix#registry#GetApplicableFixers(&filetype), 'v:val =~? a:ArgLead')
endfunction
" Suggest functions to use from the registry.
function! ale#fix#registry#Suggest(filetype) abort
let l:type_list = split(a:filetype, '\.')
@ -310,7 +364,7 @@ function! ale#fix#registry#Suggest(filetype) abort
let l:generic_fixer_list = []
for l:key in sort(keys(s:entries))
if empty(s:entries[l:key].suggested_filetypes)
if s:IsGenericFixer(s:entries[l:key].suggested_filetypes)
call add(
\ l:generic_fixer_list,
\ s:FormatEntry(l:key, s:entries[l:key]),

View File

@ -2,7 +2,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_autopep8_options', '')
function! ale#fixers#autopep8#Fix(buffer) abort

View File

@ -0,0 +1,26 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Fixing Python files with black.
"
call ale#Set('python_black_executable', 'black')
call ale#Set('python_black_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_black_options', '')
function! ale#fixers#black#Fix(buffer) abort
let l:executable = ale#python#FindExecutable(
\ a:buffer,
\ 'python_black',
\ ['black'],
\)
if !executable(l:executable)
return 0
endif
let l:options = ale#Var(a:buffer, 'python_black_options')
return {
\ 'command': ale#Escape(l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' -',
\}
endfunction

View File

@ -1,4 +1,4 @@
" Author: eborden <evan@evan-borden.com>
" Author: eborden <evan@evan-borden.com>, ifyouseewendy <ifyouseewendy@gmail.com>, aspidiets <emarshall85@gmail.com>
" Description: Integration of brittany with ALE.
call ale#Set('haskell_brittany_executable', 'brittany')
@ -8,6 +8,7 @@ function! ale#fixers#brittany#Fix(buffer) abort
return {
\ 'command': ale#Escape(l:executable)
\ . ' --write-mode inplace'
\ . ' %t',
\ 'read_temporary_file': 1,
\}

View File

@ -3,7 +3,7 @@ scriptencoding utf-8
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('c_clangformat_options', '')
function! ale#fixers#clangformat#GetExecutable(buffer) abort

View File

@ -2,7 +2,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('elm_format_options', '--yes')
function! ale#fixers#elm_format#GetExecutable(buffer) abort

View File

@ -3,7 +3,7 @@
call ale#Set('json_fixjson_executable', 'fixjson')
call ale#Set('json_fixjson_options', '')
call ale#Set('json_fixjson_use_global', 0)
call ale#Set('json_fixjson_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#fixjson#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'json_fixjson', [

View File

@ -2,7 +2,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('google_java_format_options', '')
function! ale#fixers#google_java_format#Fix(buffer) abort

View File

@ -2,7 +2,7 @@
" Description: Fixing Python imports with isort.
call ale#Set('python_isort_executable', 'isort')
call ale#Set('python_isort_use_global', 0)
call ale#Set('python_isort_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#isort#Fix(buffer) abort
let l:executable = ale#python#FindExecutable(

View File

@ -0,0 +1,18 @@
" Author: kfly8 <kentafly88@gmail.com>
" Description: Integration of perltidy with ALE.
call ale#Set('perl_perltidy_executable', 'perltidy')
call ale#Set('perl_perltidy_options', '')
function! ale#fixers#perltidy#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'perl_perltidy_executable')
let l:options = ale#Var(a:buffer, 'perl_perltidy_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ' -b'
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . ' %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@ -2,7 +2,8 @@
" 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)
call ale#Set('php_cs_fixer_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('php_cs_fixer_options', '')
function! ale#fixers#php_cs_fixer#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'php_cs_fixer', [
@ -14,10 +15,9 @@ 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',
\ 'command': ale#Escape(l:executable)
\ . ' ' . ale#Var(a:buffer, 'php_cs_fixer_options')
\ . ' fix %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@ -3,7 +3,7 @@
call ale#Set('php_phpcbf_standard', '')
call ale#Set('php_phpcbf_executable', 'phpcbf')
call ale#Set('php_phpcbf_use_global', 0)
call ale#Set('php_phpcbf_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#phpcbf#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'php_phpcbf', [

View File

@ -3,7 +3,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('javascript_prettier_options', '')
function! ale#fixers#prettier#GetExecutable(buffer) abort

View File

@ -4,7 +4,7 @@
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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('javascript_prettier_eslint_options', '')
endfunction

View File

@ -2,7 +2,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('javascript_prettier_standard_options', '')
function! ale#fixers#prettier_standard#GetExecutable(buffer) abort

View File

@ -0,0 +1,11 @@
call ale#Set('qml_qmlfmt_executable', 'qmlfmt')
function! ale#fixers#qmlfmt#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'qml_qmlfmt_executable')
endfunction
function! ale#fixers#qmlfmt#Fix(buffer) abort
return {
\ 'command': ale#Escape(ale#fixers#qmlfmt#GetExecutable(a:buffer)),
\}
endfunction

View File

@ -0,0 +1,26 @@
" Author: Jeffrey Lau https://github.com/zoonfafer
" Description: Integration of Scalafmt with ALE.
call ale#Set('scala_scalafmt_executable', 'scalafmt')
call ale#Set('scala_scalafmt_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('scala_scalafmt_options', '')
function! ale#fixers#scalafmt#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'scala_scalafmt_executable')
let l:options = ale#Var(a:buffer, 'scala_scalafmt_options')
let l:exec_args = l:executable =~? 'ng$'
\ ? ' scalafmt'
\ : ''
return ale#Escape(l:executable) . l:exec_args
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . ' %t'
endfunction
function! ale#fixers#scalafmt#Fix(buffer) abort
return {
\ 'command': ale#fixers#scalafmt#GetCommand(a:buffer),
\ 'read_temporary_file': 1,
\}
endfunction

View File

@ -2,7 +2,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('javascript_standard_options', '')
function! ale#fixers#standard#GetExecutable(buffer) abort

View File

@ -2,7 +2,7 @@
" Description: Fixing files with stylelint.
call ale#Set('stylelint_executable', 'stylelint')
call ale#Set('stylelint_use_global', 0)
call ale#Set('stylelint_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#stylelint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'stylelint', [

View File

@ -2,7 +2,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('swift_swiftformat_options', '')
function! ale#fixers#swiftformat#GetExecutable(buffer) abort

View File

@ -0,0 +1,23 @@
" Author: Albert Marquez - https://github.com/a-marquez
" Description: Fixing files with XO.
call ale#Set('javascript_xo_executable', 'xo')
call ale#Set('javascript_xo_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('javascript_xo_options', '')
function! ale#fixers#xo#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_xo', [
\ 'node_modules/xo/cli.js',
\ 'node_modules/.bin/xo',
\])
endfunction
function! ale#fixers#xo#Fix(buffer) abort
let l:executable = ale#fixers#xo#GetExecutable(a:buffer)
return {
\ 'command': ale#node#Executable(a:buffer, l:executable)
\ . ' --fix %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@ -2,7 +2,7 @@
" Description: Fixing Python files with yapf.
call ale#Set('python_yapf_executable', 'yapf')
call ale#Set('python_yapf_use_global', 0)
call ale#Set('python_yapf_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#yapf#Fix(buffer) abort
let l:executable = ale#python#FindExecutable(

View File

@ -5,7 +5,7 @@ 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('javascript_eslint_suppress_eslintignore', 0)
call ale#Set('javascript_eslint_suppress_missing_config', 0)

View File

@ -48,7 +48,7 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
let l:item = {
\ 'lnum': str2nr(l:match[2]),
\ 'type': l:match[4] is# 'error' ? 'E' : 'W',
\ 'type': (l:match[4] is# 'error' || l:match[4] is# 'fatal error') ? 'E' : 'W',
\ 'text': s:RemoveUnicodeQuotes(l:match[5]),
\}

View File

@ -0,0 +1,8 @@
" Author: KabbAmine - https://github.com/KabbAmine,
" Ben Falconer <ben@falconers.me.uk>
function! ale#handlers#sasslint#GetCommand(buffer) abort
return ale#path#BufferCdString(a:buffer)
\ . ale#Escape('sass-lint')
\ . ' -v -q -f compact %t'
endfunction

View File

@ -0,0 +1,37 @@
" Author: Nils Leuzinger - https://github.com/PawkyPenguin
" Description: Scala linting handlers for scalac-like compilers.
function! ale#handlers#scala#HandleScalacLintFormat(buffer, lines) abort
" Matches patterns line the following:
"
" /var/folders/5q/20rgxx3x1s34g3m14n5bq0x80000gn/T/vv6pSsy/0:26: error: expected class or object definition
let l:pattern = '^.\+:\(\d\+\): \(\w\+\): \(.\+\)'
let l:output = []
let l:ln = 0
for l:line in a:lines
let l:ln = l:ln + 1
let l:match = matchlist(l:line, l:pattern)
if len(l:match) == 0
continue
endif
let l:text = l:match[3]
let l:type = l:match[2] is# 'error' ? 'E' : 'W'
let l:col = 0
if l:ln + 1 < len(a:lines)
let l:col = stridx(a:lines[l:ln + 1], '^')
endif
call add(l:output, {
\ 'lnum': l:match[1] + 0,
\ 'col': l:col + 1,
\ 'text': l:text,
\ 'type': l:type,
\})
endfor
return l:output
endfunction

View File

@ -2,7 +2,7 @@
" 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_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('textlint_options', '')
function! ale#handlers#textlint#GetExecutable(buffer) abort

View File

@ -4,7 +4,7 @@
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)
call ale#Set('writegood_use_global', get(g:, 'ale_use_global_executables', 0))
endfunction
" Reset the options so the tests can test how they are set.

View File

@ -1,6 +1,9 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Tools for managing command history
" A flag for controlling the maximum size of the command history to store.
let g:ale_max_buffer_history_size = get(g:, 'ale_max_buffer_history_size', 20)
" 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', []))

View File

@ -0,0 +1,152 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Hover support for LSP linters.
let s:hover_map = {}
" Used to get the hover map in tests.
function! ale#hover#GetMap() abort
return deepcopy(s:hover_map)
endfunction
" Used to set the hover map in tests.
function! ale#hover#SetMap(map) abort
let s:hover_map = a:map
endfunction
function! ale#hover#ClearLSPData() abort
let s:hover_map = {}
endfunction
function! ale#hover#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') is# 'quickinfo'
\&& has_key(s:hover_map, a:response.request_seq)
let l:options = remove(s:hover_map, a:response.request_seq)
if get(a:response, 'success', v:false) is v:true
\&& get(a:response, 'body', v:null) isnot v:null
if get(l:options, 'hover_from_balloonexpr', 0)
\&& exists('*balloon_show')
\&& ale#Var(l:options.buffer, 'set_balloons')
call balloon_show(a:response.body.displayString)
else
call ale#util#ShowMessage(a:response.body.displayString)
endif
endif
endif
endfunction
function! ale#hover#HandleLSPResponse(conn_id, response) abort
if has_key(a:response, 'id')
\&& has_key(s:hover_map, a:response.id)
let l:options = remove(s:hover_map, a:response.id)
" If the call did __not__ come from balloonexpr...
if !get(l:options, 'hover_from_balloonexpr', 0)
let l:buffer = bufnr('')
let [l:line, l:column] = getcurpos()[1:2]
let l:end = len(getline(l:line))
if l:buffer isnot l:options.buffer
\|| l:line isnot l:options.line
\|| min([l:column, l:end]) isnot min([l:options.column, l:end])
" ... Cancel display the message if the cursor has moved.
return
endif
endif
" The result can be a Dictionary item, a List of the same, or null.
let l:result = get(a:response, 'result', v:null)
if l:result is v:null
return
endif
let l:result = l:result.contents
if type(l:result) is type('')
" The result can be just a string.
let l:result = [l:result]
endif
if type(l:result) is type({})
" If the result is an object, then it's markup content.
let l:result = [l:result.value]
endif
if type(l:result) is type([])
" Replace objects with text values.
call map(l:result, 'type(v:val) is type('''') ? v:val : v:val.value')
let l:str = join(l:result, "\n")
let l:str = substitute(l:str, '^\s*\(.\{-}\)\s*$', '\1', '')
if !empty(l:str)
if get(l:options, 'hover_from_balloonexpr', 0)
\&& exists('*balloon_show')
\&& ale#Var(l:options.buffer, 'set_balloons')
call balloon_show(l:str)
else
call ale#util#ShowMessage(l:str)
endif
endif
endif
endif
endfunction
function! s:ShowDetails(linter, buffer, line, column, opt) abort
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse')
let l:lsp_details = ale#linter#StartLSP(a: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:column = a:column
let l:message = ale#lsp#tsserver_message#Quickinfo(
\ a:buffer,
\ a:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" hover position probably won't make sense.
call ale#lsp#Send(l:id, ale#lsp#message#DidChange(a:buffer), l:root)
let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])])
let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column)
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
let s:hover_map[l:request_id] = {
\ 'buffer': a:buffer,
\ 'line': a:line,
\ 'column': l:column,
\ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0),
\}
endfunction
" Obtain Hover information for the specified position
" Pass optional arguments in the dictionary opt.
" Currently, only one key/value is useful:
" - called_from_balloonexpr, this flag marks if we want the result from this
" ale#hover#Show to display in a balloon if possible
"
" Currently, the callbacks displays the info from hover :
" - in the balloon if opt.called_from_balloonexpr and balloon_show is detected
" - as status message otherwise
function! ale#hover#Show(buffer, line, col, opt) abort
for l:linter in ale#linter#Get(getbufvar(a:buffer, '&filetype'))
if !empty(l:linter.lsp)
call s:ShowDetails(l:linter, a:buffer, a:line, a:col, a:opt)
endif
endfor
endfunction

View File

@ -8,6 +8,9 @@
" ale#job#IsRunning(job_id) -> 1 if running, 0 otherwise.
" ale#job#Stop(job_id)
" A setting for wrapping commands.
let g:ale_command_wrapper = get(g:, 'ale_command_wrapper', '')
if !has_key(s:, 'job_map')
let s:job_map = {}
endif
@ -119,7 +122,7 @@ function! s:VimCloseCallback(channel) abort
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)
call ale#util#GetFunction(l:info.exit_cb)(l:job_id, get(l:info, 'exit_code', 1))
endif
finally
" Automatically forget about the job after it's done.
@ -208,7 +211,7 @@ function! ale#job#PrepareCommand(buffer, command) abort
return 'cmd /s/c "' . l:command . '"'
endif
if &shell =~? 'fish$'
if &shell =~? 'fish$\|pwsh$'
return ['/bin/sh', '-c', l:command]
endif

View File

@ -3,15 +3,19 @@ call ale#Set('wrap_command_as_one_argument', 0)
" Description: Linter registration and lazy-loading
" Retrieves linters as requested by the engine, loading them if needed.
let s:runtime_loaded_map = {}
let s:linters = {}
" Default filetype aliases.
" The user defined aliases will be merged with this Dictionary.
"
" NOTE: Update the g:ale_linter_aliases documentation when modifying this.
let s:default_ale_linter_aliases = {
\ 'Dockerfile': 'dockerfile',
\ 'csh': 'sh',
\ 'plaintex': 'tex',
\ 'systemverilog': 'verilog',
\ 'vimwiki': 'markdown',
\ 'zsh': 'sh',
\}
@ -38,6 +42,7 @@ let s:default_ale_linters = {
" Testing/debugging helper to unload all linters.
function! ale#linter#Reset() abort
let s:runtime_loaded_map = {}
let s:linters = {}
endfunction
@ -49,6 +54,10 @@ function! s:IsBoolean(value) abort
return type(a:value) == type(0) && (a:value == 0 || a:value == 1)
endfunction
function! s:LanguageGetter(buffer) dict abort
return l:self.language
endfunction
function! ale#linter#PreProcess(linter) abort
if type(a:linter) != type({})
throw 'The linter object must be a Dictionary'
@ -183,10 +192,26 @@ function! ale#linter#PreProcess(linter) abort
endif
if l:needs_lsp_details
let l:obj.language_callback = get(a:linter, 'language_callback')
if has_key(a:linter, 'language')
if has_key(a:linter, 'language_callback')
throw 'Only one of `language` or `language_callback` '
\ . 'should be set'
endif
if !s:IsCallback(l:obj.language_callback)
throw '`language_callback` must be a callback for LSP linters'
let l:obj.language = get(a:linter, 'language')
if type(l:obj.language) != type('')
throw '`language` must be a string'
endif
" Make 'language_callback' return the 'language' value.
let l:obj.language_callback = function('s:LanguageGetter')
else
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
endif
let l:obj.project_root_callback = get(a:linter, 'project_root_callback')
@ -194,6 +219,29 @@ function! ale#linter#PreProcess(linter) abort
if !s:IsCallback(l:obj.project_root_callback)
throw '`project_root_callback` must be a callback for LSP linters'
endif
if has_key(a:linter, 'completion_filter')
let l:obj.completion_filter = a:linter.completion_filter
if !s:IsCallback(l:obj.completion_filter)
throw '`completion_filter` must be a callback'
endif
endif
if has_key(a:linter, 'initialization_options_callback')
if has_key(a:linter, 'initialization_options')
throw 'Only one of `initialization_options` or '
\ . '`initialization_options_callback` should be set'
endif
let l:obj.initialization_options_callback = a:linter.initialization_options_callback
if !s:IsCallback(l:obj.initialization_options_callback)
throw '`initialization_options_callback` must be a callback if defined'
endif
elseif has_key(a:linter, 'initialization_options')
let l:obj.initialization_options = a:linter.initialization_options
endif
endif
let l:obj.output_stream = get(a:linter, 'output_stream', 'stdout')
@ -242,20 +290,20 @@ function! ale#linter#Define(filetype, linter) abort
call add(s:linters[a:filetype], l:new_linter)
endfunction
" Prevent any linters from being loaded for a given filetype.
function! ale#linter#PreventLoading(filetype) abort
let s:runtime_loaded_map[a:filetype] = 1
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)
" Load linters from runtimepath if we haven't done that yet.
if !has_key(s:runtime_loaded_map, 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
let s:runtime_loaded_map[l:filetype] = 1
endif
call extend(l:combined_linters, get(s:linters, l:filetype, []))
@ -418,12 +466,20 @@ function! ale#linter#StartLSP(buffer, linter, callback) abort
return {}
endif
let l:initialization_options = {}
if has_key(a:linter, 'initialization_options_callback')
let l:initialization_options = ale#util#GetFunction(a:linter.initialization_options_callback)(a:buffer)
elseif has_key(a:linter, 'initialization_options')
let l:initialization_options = a:linter.initialization_options
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,
\ l:initialization_options,
\)
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
@ -441,6 +497,7 @@ function! ale#linter#StartLSP(buffer, linter, callback) abort
\ l:command,
\ l:root,
\ a:callback,
\ l:initialization_options,
\)
endif

View File

@ -1,6 +1,19 @@
" Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
" Description: Manages the loclist and quickfix lists
" This flag dictates if ale open the configured loclist
let g:ale_open_list = get(g:, 'ale_open_list', 0)
" This flag dictates if ale keeps open loclist even if there is no error in loclist
let g:ale_keep_list_window_open = get(g:, 'ale_keep_list_window_open', 0)
" This flag dictates that quickfix windows should be opened vertically
let g:ale_list_vertical = get(g:, 'ale_list_vertical', 0)
" The window size to set for the quickfix and loclist windows
let g:ale_list_window_size = get(g:, 'ale_list_window_size', 10)
" A string format for the loclist messages.
let g:ale_loclist_msg_format = get(g:, 'ale_loclist_msg_format',
\ get(g:, 'ale_echo_msg_format', '%code: %%s')
\)
if !exists('s:timer_args')
let s:timer_args = {}
endif

View File

@ -6,7 +6,7 @@
let s:connections = []
let g:ale_lsp_next_message_id = 1
function! s:NewConnection() abort
function! s:NewConnection(initialization_options) 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.
@ -18,6 +18,7 @@ function! s:NewConnection() abort
\ 'projects': {},
\ 'open_documents': [],
\ 'callback_list': [],
\ 'initialization_options': a:initialization_options,
\}
call add(s:connections, l:conn)
@ -207,6 +208,11 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
endfunction
function! ale#lsp#HandleMessage(conn, message) abort
if type(a:message) != type('')
" Ignore messages that aren't strings.
return
endif
let a:conn.data .= a:message
" Parse the objects now if we can, and keep the remaining text.
@ -266,7 +272,7 @@ endfunction
"
" 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
function! ale#lsp#StartProgram(executable, command, project_root, callback, initialization_options) abort
if !executable(a:executable)
return 0
endif
@ -274,7 +280,7 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback) abor
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 = !empty(l:conn) ? l:conn : s:NewConnection(a:initialization_options)
let l:conn.executable = a:executable
if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id)
@ -300,10 +306,10 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback) abor
endfunction
" Connect to an address and set up a callback for handling responses.
function! ale#lsp#ConnectToAddress(address, project_root, callback) abort
function! ale#lsp#ConnectToAddress(address, project_root, callback, initialization_options) 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()
let l:conn = !empty(l:conn) ? l:conn : s:NewConnection(a:initialization_options)
if !has_key(l:conn, 'channel') || ch_status(l:conn.channel) isnot# 'open'
let l:conn.channnel = ch_open(a:address, {
@ -378,7 +384,7 @@ function! ale#lsp#Send(conn_id, message, ...) abort
" 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),
\ ale#lsp#message#Initialize(l:project_root, l:conn.initialization_options),
\)
let l:project.init_request_id = l:init_id

View File

@ -24,12 +24,15 @@ function! ale#lsp#message#GetNextVersionID() abort
return l:id
endfunction
function! ale#lsp#message#Initialize(root_path) abort
function! ale#lsp#message#Initialize(root_path, initialization_options) abort
" TODO: Define needed capabilities.
" NOTE: rootPath is deprecated in favour of rootUri
return [0, 'initialize', {
\ 'processId': getpid(),
\ 'rootPath': a:root_path,
\ 'capabilities': {},
\ 'initializationOptions': a:initialization_options,
\ 'rootUri': ale#path#ToURI(a:root_path),
\}]
endfunction
@ -116,3 +119,22 @@ function! ale#lsp#message#Definition(buffer, line, column) abort
\ 'position': {'line': a:line - 1, 'character': a:column},
\}]
endfunction
function! ale#lsp#message#References(buffer, line, column) abort
return [0, 'textDocument/references', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column},
\ 'context': {'includeDeclaration': v:false},
\}]
endfunction
function! ale#lsp#message#Hover(buffer, line, column) abort
return [0, 'textDocument/hover', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column},
\}]
endfunction

View File

@ -1,6 +1,20 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Parsing and transforming of LSP server responses.
" Constants for error codes.
" Defined by JSON RPC
let s:PARSE_ERROR = -32700
let s:INVALID_REQUEST = -32600
let s:METHOD_NOT_FOUND = -32601
let s:INVALID_PARAMS = -32602
let s:INTERNAL_ERROR = -32603
let s:SERVER_ERROR_START = -32099
let s:SERVER_ERROR_END = -32000
let s:SERVER_NOT_INITIALIZED = -32002
let s:UNKNOWN_ERROR_CODE = -32001
" Defined by the protocol.
let s:REQUEST_CANCELLED = -32800
" Constants for message severity codes.
let s:SEVERITY_ERROR = 1
let s:SEVERITY_WARNING = 2
@ -72,3 +86,31 @@ function! ale#lsp#response#ReadTSServerDiagnostics(response) abort
return l:loclist
endfunction
function! ale#lsp#response#GetErrorMessage(response) abort
if type(get(a:response, 'error', 0)) isnot type({})
return ''
endif
let l:code = get(a:response.error, 'code')
" Only report things for these error codes.
if l:code isnot s:INVALID_PARAMS && l:code isnot s:INTERNAL_ERROR
return ''
endif
let l:message = get(a:response.error, 'message', '')
if empty(l:message)
return ''
endif
" Include the traceback as details, if it's there.
let l:traceback = get(get(a:response.error, 'data', {}), 'traceback', [])
if type(l:traceback) is type([]) && !empty(l:traceback)
let l:message .= "\n" . join(l:traceback, "\n")
endif
return l:message
endfunction

View File

@ -61,3 +61,19 @@ function! ale#lsp#tsserver_message#Definition(buffer, line, column) abort
\ 'file': expand('#' . a:buffer . ':p'),
\}]
endfunction
function! ale#lsp#tsserver_message#References(buffer, line, column) abort
return [0, 'ts@references', {
\ 'line': a:line,
\ 'offset': a:column,
\ 'file': expand('#' . a:buffer . ':p'),
\}]
endfunction
function! ale#lsp#tsserver_message#Quickinfo(buffer, line, column) abort
return [0, 'ts@quickinfo', {
\ 'line': a:line,
\ 'offset': a:column,
\ 'file': expand('#' . a:buffer . ':p'),
\}]
endfunction

View File

@ -1,6 +1,11 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Set options in files based on regex patterns.
" A dictionary mapping regular expression patterns to arbitrary buffer
" variables to be set. Useful for configuring ALE based on filename patterns.
let g:ale_pattern_options = get(g:, 'ale_pattern_options', {})
let g:ale_pattern_options_enabled = get(g:, 'ale_pattern_options_enabled', !empty(g:ale_pattern_options))
" These variables are used to cache the sorting of patterns below.
let s:last_pattern_options = {}
let s:sorted_items = []
@ -18,7 +23,8 @@ function! s:CmpPatterns(left_item, right_item) abort
endfunction
function! ale#pattern_options#SetOptions(buffer) abort
if !g:ale_pattern_options_enabled || empty(g:ale_pattern_options)
if !get(g:, 'ale_pattern_options_enabled', 0)
\|| empty(get(g:, 'ale_pattern_options', 0))
return
endif

View File

@ -2,13 +2,16 @@
" Description: Preview windows for showing whatever information in.
" Open a preview window and show some lines in it.
function! ale#preview#Show(lines) abort
" An optional second argument can set an alternative filetype for the window.
function! ale#preview#Show(lines, ...) abort
let l:filetype = get(a:000, 0, 'ale-preview')
silent pedit ALEPreviewWindow
wincmd P
setlocal modifiable
setlocal noreadonly
setlocal nobuflisted
setlocal filetype=ale-preview
let &l:filetype = l:filetype
setlocal buftype=nofile
setlocal bufhidden=wipe
:%d
@ -16,3 +19,50 @@ function! ale#preview#Show(lines) abort
setlocal nomodifiable
setlocal readonly
endfunction
" Show a location selection preview window, given some items.
" Each item should have 'filename', 'line', and 'column' keys.
function! ale#preview#ShowSelection(item_list) abort
let l:lines = []
" Create lines to display to users.
for l:item in a:item_list
call add(
\ l:lines,
\ l:item.filename
\ . ':' . l:item.line
\ . ':' . l:item.column,
\)
endfor
call ale#preview#Show(l:lines, 'ale-preview-selection')
let b:ale_preview_item_list = a:item_list
endfunction
function! s:Open(open_in_tab) abort
let l:item_list = get(b:, 'ale_preview_item_list', [])
let l:item = get(l:item_list, getcurpos()[1] - 1, {})
if empty(l:item)
return
endif
if !a:open_in_tab
:q!
endif
call ale#util#Open(
\ l:item.filename,
\ l:item.line,
\ l:item.column,
\ {'open_in_tab': a:open_in_tab},
\)
endfunction
function! ale#preview#OpenSelectionInBuffer() abort
call s:Open(0)
endfunction
function! ale#preview#OpenSelectionInTab() abort
call s:Open(1)
endfunction

View File

@ -15,6 +15,7 @@ let g:ale_virtualenv_dir_names = get(g:, 'ale_virtualenv_dir_names', [
function! ale#python#FindProjectRootIni(buffer) abort
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
" If you change this, update ale-python-root documentation.
if filereadable(l:path . '/MANIFEST.in')
\|| filereadable(l:path . '/setup.cfg')
\|| filereadable(l:path . '/pytest.ini')

View File

@ -0,0 +1,111 @@
let s:references_map = {}
" Used to get the references map in tests.
function! ale#references#GetMap() abort
return deepcopy(s:references_map)
endfunction
" Used to set the references map in tests.
function! ale#references#SetMap(map) abort
let s:references_map = a:map
endfunction
function! ale#references#ClearLSPData() abort
let s:references_map = {}
endfunction
function! ale#references#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') is# 'references'
\&& has_key(s:references_map, a:response.request_seq)
call remove(s:references_map, a:response.request_seq)
if get(a:response, 'success', v:false) is v:true
let l:item_list = []
for l:response_item in a:response.body.refs
call add(l:item_list, {
\ 'filename': l:response_item.file,
\ 'line': l:response_item.start.line,
\ 'column': l:response_item.start.offset,
\})
endfor
if empty(l:item_list)
call ale#util#Execute('echom ''No references found.''')
else
call ale#preview#ShowSelection(l:item_list)
endif
endif
endif
endfunction
function! ale#references#HandleLSPResponse(conn_id, response) abort
if has_key(a:response, 'id')
\&& has_key(s:references_map, a:response.id)
call remove(s:references_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', [])
let l:item_list = []
for l:response_item in l:result
call add(l:item_list, {
\ 'filename': ale#path#FromURI(l:response_item.uri),
\ 'line': l:response_item.range.start.line + 1,
\ 'column': l:response_item.range.start.character + 1,
\})
endfor
if empty(l:item_list)
call ale#util#Execute('echom ''No references found.''')
else
call ale#preview#ShowSelection(l:item_list)
endif
endif
endfunction
function! s:FindReferences(linter) abort
let l:buffer = bufnr('')
let [l:line, l:column] = getcurpos()[1:2]
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#references#HandleTSServerResponse')
\ : function('ale#references#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#References(
\ l:buffer,
\ l:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" references 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))])
let l:message = ale#lsp#message#References(l:buffer, l:line, l:column)
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
let s:references_map[l:request_id] = {}
endfunction
function! ale#references#Find() abort
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
call s:FindReferences(l:linter)
endif
endfor
endfunction

View File

@ -2,6 +2,25 @@ scriptencoding utf8
" Author: w0rp <devw0rp@gmail.com>
" Description: Draws error and warning signs into signcolumn
" This flag can be set to some integer to control the maximum number of signs
" that ALE will set.
let g:ale_max_signs = get(g:, 'ale_max_signs', -1)
" This flag can be set to 1 to enable changing the sign column colors when
" there are errors.
let g:ale_change_sign_column_color = get(g:, 'ale_change_sign_column_color', 0)
" These variables dictate what signs are used to indicate errors and warnings.
let g:ale_sign_error = get(g:, 'ale_sign_error', '>>')
let g:ale_sign_style_error = get(g:, 'ale_sign_style_error', g:ale_sign_error)
let g:ale_sign_warning = get(g:, 'ale_sign_warning', '--')
let g:ale_sign_style_warning = get(g:, 'ale_sign_style_warning', g:ale_sign_warning)
let g:ale_sign_info = get(g:, 'ale_sign_info', g:ale_sign_warning)
" This variable sets an offset which can be set for sign IDs.
" This ID can be changed depending on what IDs are set for other plugins.
" The dummy sign will use the ID exactly equal to the offset.
let g:ale_sign_offset = get(g:, 'ale_sign_offset', 1000000)
" This flag can be set to 1 to keep sign gutter always open
let g:ale_sign_column_always = get(g:, 'ale_sign_column_always', 0)
if !hlexists('ALEErrorSign')
highlight link ALEErrorSign error
endif
@ -60,55 +79,35 @@ 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
let l:priority = g:ale#util#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
let l:item_priority = ale#util#GetItemPriority(l:item)
if l:item_priority < l:priority
if l:item_priority > l:priority
let l:priority = l:item_priority
endif
endfor
if l:priority is# s:error_priority
if l:priority is# g:ale#util#error_priority
return 'ALEErrorSign'
endif
if l:priority is# s:warning_priority
if l:priority is# g:ale#util#warning_priority
return 'ALEWarningSign'
endif
if l:priority is# s:style_error_priority
if l:priority is# g:ale#util#style_error_priority
return 'ALEStyleErrorSign'
endif
if l:priority is# s:style_warning_priority
if l:priority is# g:ale#util#style_warning_priority
return 'ALEStyleWarningSign'
endif
if l:priority is# s:info_priority
if l:priority is# g:ale#util#info_priority
return 'ALEInfoSign'
endif

View File

@ -1,6 +1,14 @@
" Author: KabbAmine <amine.kabb@gmail.com>
" Description: Statusline related function(s)
" remove in 2.0
"
" A deprecated setting for ale#statusline#Status()
" See :help ale#statusline#Count() for getting status reports.
let g:ale_statusline_format = get(g:, 'ale_statusline_format',
\ ['%d error(s)', '%d warning(s)', 'OK']
\)
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].
@ -90,6 +98,8 @@ function! s:StatusForListFormat() abort
return l:res
endfunction
" remove in 2.0
"
" Returns a formatted string that can be integrated in the statusline.
"
" This function is deprecated, and should not be used. Use the airline plugin

View File

@ -1,84 +1,6 @@
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
if get(g:, 'ale_pattern_options_enabled', 0)
call ale#pattern_options#SetOptions(bufnr(''))
endif
@ -126,16 +48,11 @@ function! ale#toggle#Toggle() abort
endif
endif
call ale#toggle#InitAuGroups()
call ale#autocmd#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

View File

@ -11,6 +11,25 @@ function! ale#util#FeedKeys(...) abort
return call('feedkeys', a:000)
endfunction
" Show a message in as small a window as possible.
"
" Vim 8 does not support echoing long messages from asynchronous callbacks,
" but NeoVim does. Small messages can be echoed in Vim 8, and larger messages
" have to be shown in preview windows.
function! ale#util#ShowMessage(string) abort
" We have to assume the user is using a monospace font.
if has('nvim') || (a:string !~? "\n" && len(a:string) < &columns)
execute 'echo a:string'
else
call ale#preview#Show(split(a:string, "\n"))
endif
endfunction
" A wrapper function for execute, so we can test executing some commands.
function! ale#util#Execute(expr) abort
execute a:expr
endfunction
if !exists('g:ale#util#nul_file')
" A null file for sending output to nothing.
let g:ale#util#nul_file = '/dev/null'
@ -33,6 +52,42 @@ function! ale#util#GetFunction(string_or_ref) abort
return a:string_or_ref
endfunction
function! ale#util#Open(filename, line, column, options) abort
if get(a:options, 'open_in_tab', 0)
call ale#util#Execute('tabedit ' . fnameescape(a:filename))
else
call ale#util#Execute('edit ' . fnameescape(a:filename))
endif
call cursor(a:line, a:column)
endfunction
let g:ale#util#error_priority = 5
let g:ale#util#warning_priority = 4
let g:ale#util#info_priority = 3
let g:ale#util#style_error_priority = 2
let g:ale#util#style_warning_priority = 1
function! ale#util#GetItemPriority(item) abort
if a:item.type is# 'I'
return g:ale#util#info_priority
endif
if a:item.type is# 'W'
if get(a:item, 'sub_type', '') is# 'style'
return g:ale#util#style_warning_priority
endif
return g:ale#util#warning_priority
endif
if get(a:item, 'sub_type', '') is# 'style'
return g:ale#util#style_error_priority
endif
return g:ale#util#error_priority
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
@ -70,6 +125,23 @@ function! ale#util#LocItemCompare(left, right) abort
return 1
endif
" When either of the items lacks a problem type, then the two items should
" be considered equal. This is important for loclist jumping.
if !has_key(a:left, 'type') || !has_key(a:right, 'type')
return 0
endif
let l:left_priority = ale#util#GetItemPriority(a:left)
let l:right_priority = ale#util#GetItemPriority(a:right)
if l:left_priority < l:right_priority
return -1
endif
if l:left_priority > l:right_priority
return 1
endif
return 0
endfunction
@ -139,6 +211,17 @@ function! ale#util#BinarySearch(loclist, buffer, line, column) abort
let l:index += 1
endwhile
" Scan forwards to find the last item on the column for the item
" we found, which will have the most serious problem.
let l:item_column = a:loclist[l:index].col
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 == l:item_column
let l:index += 1
endwhile
return l:index
endif
endwhile
@ -287,8 +370,10 @@ if !exists('s:patial_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)
if has_key(s:partial_timers, a:timer_id)
let [l:Callback, l:args] = remove(s:partial_timers, a:timer_id)
call call(l:Callback, [a:timer_id] + l:args)
endif
endfunction
" Given a delay, a callback, a List of arguments, start a timer with
@ -311,3 +396,13 @@ function! ale#util#StopPartialTimer(timer_id) abort
call remove(s:partial_timers, a:timer_id)
endif
endfunction
" Given a possibly multi-byte string and a 1-based character position on a
" line, return the 1-based byte position on that line.
function! ale#util#Col(str, chr) abort
if a:chr < 2
return a:chr
endif
return strlen(join(split(a:str, '\zs')[0:a:chr - 2], '')) + 1
endfunction