1
0
mirror of https://github.com/amix/vimrc synced 2025-09-15 16:45:00 +08:00

Update Ale.

This commit is contained in:
Kurtis Moxley
2022-05-19 21:16:38 +08:00
parent 0071859401
commit dd26bc4697
1324 changed files with 56041 additions and 437 deletions

View File

@ -157,7 +157,7 @@ function! ale#Queue(delay, ...) abort
endif
endfunction
let s:current_ale_version = [3, 1, 0]
let s:current_ale_version = [3, 2, 0]
" A function used to check for ALE features in files outside of the project.
function! ale#Has(feature) abort

View File

@ -16,13 +16,12 @@ endfunction
function! ale#code_action#HandleCodeAction(code_action, options) abort
let l:current_buffer = bufnr('')
let l:changes = a:code_action.changes
let l:should_save = get(a:options, 'should_save')
for l:file_code_edit in l:changes
call ale#code_action#ApplyChanges(
\ l:file_code_edit.fileName,
\ l:file_code_edit.textChanges,
\ l:should_save,
\ a:options,
\)
endfor
endfunction
@ -63,29 +62,29 @@ function! s:ChangeCmp(left, right) abort
return 0
endfunction
function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
let l:current_buffer = bufnr('')
function! ale#code_action#ApplyChanges(filename, changes, options) abort
let l:should_save = get(a:options, 'should_save')
let l:conn_id = get(a:options, 'conn_id')
let l:orig_buffer = bufnr('')
" The buffer is used to determine the fileformat, if available.
let l:buffer = bufnr(a:filename)
let l:is_current_buffer = l:buffer > 0 && l:buffer == l:current_buffer
if l:buffer > 0
let l:lines = getbufline(l:buffer, 1, '$')
" Add empty line if there's trailing newline, like readfile() does.
if getbufvar(l:buffer, '&eol')
let l:lines += ['']
endif
else
let l:lines = readfile(a:filename, 'b')
if l:buffer != l:orig_buffer
call ale#util#Execute('silent edit ' . a:filename)
let l:buffer = bufnr('')
endif
if l:is_current_buffer
let l:pos = getpos('.')[1:2]
else
let l:pos = [1, 1]
let l:lines = getbufline(l:buffer, 1, '$')
" Add empty line if there's trailing newline, like readfile() does.
if getbufvar(l:buffer, '&eol')
let l:lines += ['']
endif
let l:pos = getpos('.')[1:2]
" Changes have to be sorted so we apply them from bottom-to-top
for l:code_edit in reverse(sort(copy(a:changes), function('s:ChangeCmp')))
let l:line = l:code_edit.start.line
@ -155,46 +154,25 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
endif
endfor
if l:buffer > 0
" Make sure ale#util#{Writefile,SetBufferContents} add trailing
" newline if and only if it should be added.
if l:lines[-1] is# '' && getbufvar(l:buffer, '&eol')
call remove(l:lines, -1)
else
call setbufvar(l:buffer, '&eol', 0)
endif
elseif exists('+fixeol') && &fixeol && l:lines[-1] is# ''
" Not in buffer, ale#util#Writefile can't check &eol and always adds
" newline if &fixeol: remove to prevent double trailing newline.
" Make sure to add a trailing newline if and only if it should be added.
if l:lines[-1] is# '' && getbufvar(l:buffer, '&eol')
call remove(l:lines, -1)
endif
if a:should_save || l:buffer < 0
call ale#util#Writefile(l:buffer, l:lines, a:filename)
else
call ale#util#SetBufferContents(l:buffer, l:lines)
call setbufvar(l:buffer, '&eol', 0)
endif
if l:is_current_buffer
if a:should_save
call ale#util#Execute(':e!')
endif
call ale#util#SetBufferContents(l:buffer, l:lines)
call setpos('.', [0, l:pos[0], l:pos[1], 0])
call ale#lsp#NotifyForChanges(l:conn_id, l:buffer)
if l:should_save
call ale#util#Execute('silent w!')
endif
if a:should_save && l:buffer > 0 && !l:is_current_buffer
" Set up a one-time use event that will delete itself to reload the
" buffer next time it's entered to view the changes made to it.
execute 'augroup ALECodeActionReloadGroup' . l:buffer
autocmd!
call setpos('.', [0, l:pos[0], l:pos[1], 0])
execute printf(
\ 'autocmd BufEnter <buffer=%d>'
\ . ' call ale#code_action#ReloadBuffer()',
\ l:buffer
\)
augroup END
if l:orig_buffer != l:buffer && bufexists(l:orig_buffer)
call ale#util#Execute('silent buf ' . string(l:orig_buffer))
endif
endfunction
@ -300,7 +278,7 @@ function! ale#code_action#BuildChangesList(changes_map) abort
endfor
call add(l:changes, {
\ 'fileName': ale#path#FromURI(l:file_name),
\ 'fileName': ale#util#ToResource(l:file_name),
\ 'textChanges': l:text_changes,
\})
endfor

View File

@ -391,8 +391,8 @@ function! s:OnReady(
\ 'character': l:nearest_error.col - 1,
\ },
\ 'end': {
\ 'line': l:nearest_error.end_lnum - 1,
\ 'character': l:nearest_error.end_col,
\ 'line': get(l:nearest_error, 'end_lnum', 1) - 1,
\ 'character': get(l:nearest_error, 'end_col', 0)
\ },
\ },
\ },
@ -457,7 +457,7 @@ function! s:ExecuteGetCodeFix(linter, range, MenuCallback) abort
let [l:end_line, l:end_column] = getpos("'>")[1:2]
endif
let l:column = min([l:column, len(getline(l:line))])
let l:column = max([min([l:column, len(getline(l:line))]), 1])
let l:end_column = min([l:end_column, len(getline(l:end_line))])
let l:Callback = function(

View File

@ -16,7 +16,7 @@ onoremap <silent> <Plug>(ale_show_completion_menu) <Nop>
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 g:ale_completion_autoimport = get(g:, 'ale_completion_autoimport', 0)
let g:ale_completion_autoimport = get(g:, 'ale_completion_autoimport', 1)
let g:ale_completion_tsserver_remove_warnings = get(g:, 'ale_completion_tsserver_remove_warnings', 0)
let s:timer_id = -1
@ -581,7 +581,7 @@ function! ale#completion#ParseLSPCompletions(response) abort
continue
endif
if get(l:item, 'insertTextFormat') is s:LSP_INSERT_TEXT_FORMAT_PLAIN
if get(l:item, 'insertTextFormat', s:LSP_INSERT_TEXT_FORMAT_PLAIN) is s:LSP_INSERT_TEXT_FORMAT_PLAIN
\&& type(get(l:item, 'textEdit')) is v:t_dict
let l:text = l:item.textEdit.newText
elseif type(get(l:item, 'insertText')) is v:t_string
@ -776,7 +776,8 @@ function! s:OnReady(linter, lsp_details) abort
if a:linter.lsp is# 'tsserver'
if get(g:, 'ale_completion_tsserver_autoimport') is 1
execute 'echom `g:ale_completion_tsserver_autoimport` is deprecated. Use `g:ale_completion_autoimport` instead.'''
" no-custom-checks
echom '`g:ale_completion_tsserver_autoimport` is deprecated. Use `g:ale_completion_autoimport` instead.'
endif
let l:message = ale#lsp#tsserver_message#Completions(
@ -911,7 +912,8 @@ function! ale#completion#Import() abort
endif
let [l:line, l:column] = getpos('.')[1:2]
let l:column = searchpos('\V' . escape(l:word, '/\'), 'bn', l:line)[1]
let l:column = searchpos('\V' . escape(l:word, '/\'), 'bnc', l:line)[1]
let l:column = l:column + len(l:word) - 1
if l:column isnot 0
let l:started = ale#completion#GetCompletions('ale-import', {

View File

@ -10,12 +10,21 @@ let g:ale_echo_msg_format = get(g:, 'ale_echo_msg_format', '%code: %%s')
let s:cursor_timer = -1
" A wrapper for echon so we can test messages we echo in Vader tests.
function! ale#cursor#Echom(message) abort
" no-custom-checks
exec "norm! :echom a:message\n"
endfunction
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')
" Convert indentation groups into single spaces for better legibility when
" put on a single line
let l:message = substitute(l:message, ' \+', ' ', 'g')
" We need to remember the setting for shortmess and reset it again.
let l:shortmess_options = &l:shortmess
@ -27,7 +36,7 @@ function! ale#cursor#TruncatedEcho(original_message) abort
silent! setlocal shortmess+=T
try
exec "norm! :echomsg l:message\n"
call ale#cursor#Echom(l:message)
catch /^Vim\%((\a\+)\)\=:E523/
" Fallback into manual truncate (#1987)
let l:winwidth = winwidth(0)
@ -87,7 +96,9 @@ function! ale#cursor#EchoCursorWarning(...) abort
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'
"
" no-custom-checks
echo
let l:info.echoed = 0
endif
endif
@ -150,7 +161,8 @@ function! s:ShowCursorDetailForItem(loc, options) abort
" Clear the echo message if we manually displayed details.
if !l:stay_here
execute 'echo'
" no-custom-checks
echo
endif
endif
endfunction

View File

@ -62,7 +62,8 @@ let s:global_variable_list = [
\]
function! s:Echo(message) abort
execute 'echo a:message'
" no-custom-checks
echo a:message
endfunction
function! s:GetLinterVariables(filetype, exclude_linter_names) abort

View File

@ -68,18 +68,27 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort
for l:item in l:result
if has_key(l:item, 'targetUri')
" LocationLink items use targetUri
let l:filename = ale#path#FromURI(l:item.targetUri)
let l:uri = l:item.targetUri
let l:line = l:item.targetRange.start.line + 1
let l:column = l:item.targetRange.start.character + 1
else
" LocationLink items use uri
let l:filename = ale#path#FromURI(l:item.uri)
let l:uri = l:item.uri
let l:line = l:item.range.start.line + 1
let l:column = l:item.range.start.character + 1
endif
call ale#definition#UpdateTagStack()
call ale#util#Open(l:filename, l:line, l:column, l:options)
let l:uri_handler = ale#uri#GetURIHandler(l:uri)
if l:uri_handler is# v:null
let l:filename = ale#path#FromFileURI(l:uri)
call ale#util#Open(l:filename, l:line, l:column, l:options)
else
call l:uri_handler.OpenURILink(l:uri, l:line, l:column, l:options, a:conn_id)
endif
break
endfor
endif
@ -112,6 +121,12 @@ function! s:OnReady(line, column, options, capability, linter, lsp_details) abor
\ a:line,
\ a:column
\)
elseif a:capability is# 'implementation'
let l:message = ale#lsp#tsserver_message#Implementation(
\ l:buffer,
\ a:line,
\ a:column
\)
endif
else
" Send a message saying the buffer has changed first, or the
@ -125,6 +140,8 @@ function! s:OnReady(line, column, options, capability, linter, lsp_details) abor
let l:message = ale#lsp#message#Definition(l:buffer, a:line, a:column)
elseif a:capability is# 'typeDefinition'
let l:message = ale#lsp#message#TypeDefinition(l:buffer, a:line, a:column)
elseif a:capability is# 'implementation'
let l:message = ale#lsp#message#Implementation(l:buffer, a:line, a:column)
else
" XXX: log here?
return
@ -166,6 +183,14 @@ function! ale#definition#GoToType(options) abort
endfor
endfunction
function! ale#definition#GoToImpl(options) abort
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
call s:GoToLSPDefinition(l:linter, a:options, 'implementation')
endif
endfor
endfunction
function! ale#definition#GoToCommandHandler(command, ...) abort
let l:options = {}
@ -191,6 +216,8 @@ function! ale#definition#GoToCommandHandler(command, ...) abort
if a:command is# 'type'
call ale#definition#GoToType(l:options)
elseif a:command is# 'implementation'
call ale#definition#GoToImpl(l:options)
else
call ale#definition#GoTo(l:options)
endif

View File

@ -347,6 +347,12 @@ function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist)
if has_key(l:old_item, 'end_lnum')
let l:item.end_lnum = str2nr(l:old_item.end_lnum)
" When the error ends after the end of the file, put it at the
" end. This is only done for the current buffer.
if l:item.bufnr == a:buffer && l:item.end_lnum > l:last_line_number
let l:item.end_lnum = l:last_line_number
endif
endif
if has_key(l:old_item, 'sub_type')

View File

@ -156,4 +156,10 @@ function! ale#events#Init() abort
endif
endif
augroup END
augroup AleURISchemes
autocmd!
autocmd BufNewFile,BufReadPre jdt://** call ale#uri#jdt#ReadJDTLink(expand('<amatch>'))
augroup END
endfunction

View File

@ -0,0 +1,133 @@
" Author: Dalius Dobravolskas <dalius.dobravolskas@gmail.com>
" Description: Rename file support for tsserver
let s:filerename_map = {}
" Used to get the rename map in tests.
function! ale#filerename#GetMap() abort
return deepcopy(s:filerename_map)
endfunction
" Used to set the rename map in tests.
function! ale#filerename#SetMap(map) abort
let s:filerename_map = a:map
endfunction
function! ale#filerename#ClearLSPData() abort
let s:filerename_map = {}
endfunction
function! s:message(message) abort
call ale#util#Execute('echom ' . string(a:message))
endfunction
function! ale#filerename#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') isnot# 'getEditsForFileRename'
return
endif
if !has_key(s:filerename_map, a:response.request_seq)
return
endif
let l:options = remove(s:filerename_map, a:response.request_seq)
let l:old_name = l:options.old_name
let l:new_name = l:options.new_name
if get(a:response, 'success', v:false) is v:false
let l:message = get(a:response, 'message', 'unknown')
call s:message('Error renaming file "' . l:old_name . '" to "' . l:new_name
\ . '". Reason: ' . l:message)
return
endif
let l:changes = a:response.body
if empty(l:changes)
call s:message('No changes while renaming "' . l:old_name . '" to "' . l:new_name . '"')
else
call ale#code_action#HandleCodeAction(
\ {
\ 'description': 'filerename',
\ 'changes': l:changes,
\ },
\ {
\ 'should_save': 1,
\ },
\)
endif
silent! noautocmd execute 'saveas ' . l:new_name
call delete(l:old_name)
endfunction
function! s:OnReady(options, linter, lsp_details) abort
let l:id = a:lsp_details.connection_id
if !ale#lsp#HasCapability(l:id, 'filerename')
return
endif
let l:buffer = a:lsp_details.buffer
let l:Callback = function('ale#filerename#HandleTSServerResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
let l:message = ale#lsp#tsserver_message#GetEditsForFileRename(
\ a:options.old_name,
\ a:options.new_name,
\)
let l:request_id = ale#lsp#Send(l:id, l:message)
let s:filerename_map[l:request_id] = a:options
endfunction
function! s:ExecuteFileRename(linter, options) abort
let l:buffer = bufnr('')
let l:Callback = function('s:OnReady', [a:options])
call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
endfunction
function! ale#filerename#Execute() abort
let l:lsp_linters = []
for l:linter in ale#linter#Get(&filetype)
if l:linter.lsp is# 'tsserver'
call add(l:lsp_linters, l:linter)
endif
endfor
if empty(l:lsp_linters)
call s:message('No active tsserver LSPs')
return
endif
let l:buffer = bufnr('')
let l:old_name = expand('#' . l:buffer . ':p')
let l:new_name = ale#util#Input('New file name: ', l:old_name, 'file')
if l:old_name is# l:new_name
call s:message('New file name matches old file name')
return
endif
if empty(l:new_name)
call s:message('New name cannot be empty!')
return
endif
for l:lsp_linter in l:lsp_linters
call s:ExecuteFileRename(l:lsp_linter, {
\ 'old_name': l:old_name,
\ 'new_name': l:new_name,
\})
endfor
endfunction

View File

@ -77,7 +77,8 @@ function! ale#fix#ApplyFixes(buffer, output) abort
call remove(g:ale_fix_buffer_data, a:buffer)
if !l:data.ignore_file_changed_errors
execute 'echoerr ''The file was changed before fixing finished'''
" no-custom-checks
echoerr 'The file was changed before fixing finished'
endif
return
@ -358,7 +359,8 @@ function! ale#fix#Fix(buffer, fixing_flag, ...) abort
\ 'There is no fixer named `%s`. Check :ALEFixSuggest',
\ l:function_name,
\)
execute 'echom l:echo_message'
" no-custom-checks
echom l:echo_message
endif
return 0
@ -366,7 +368,8 @@ function! ale#fix#Fix(buffer, fixing_flag, ...) abort
if empty(l:callback_list)
if a:fixing_flag is# ''
execute 'echom ''No fixers have been defined. Try :ALEFixSuggest'''
" no-custom-checks
echom 'No fixers have been defined. Try :ALEFixSuggest'
endif
return 0

View File

@ -37,6 +37,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['python'],
\ 'description': 'Fix PEP8 issues with black.',
\ },
\ 'buf-format': {
\ 'function': 'ale#fixers#buf_format#Fix',
\ 'suggested_filetypes': ['proto'],
\ 'description': 'Fix .proto files with buf format.',
\ },
\ 'buildifier': {
\ 'function': 'ale#fixers#buildifier#Fix',
\ 'suggested_filetypes': ['bzl'],
@ -191,6 +196,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['ruby'],
\ 'description': 'Fix ruby files with standardrb --fix',
\ },
\ 'statix': {
\ 'function': 'ale#fixers#statix#Fix',
\ 'suggested_filetypes': ['nix'],
\ 'description': 'Fix common Nix antipatterns with statix fix',
\ },
\ 'stylelint': {
\ 'function': 'ale#fixers#stylelint#Fix',
\ 'suggested_filetypes': ['css', 'sass', 'scss', 'sugarss', 'stylus'],
@ -246,6 +256,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['go'],
\ 'description': 'Fix Go files with go fmt.',
\ },
\ 'gofumpt': {
\ 'function': 'ale#fixers#gofumpt#Fix',
\ 'suggested_filetypes': ['go'],
\ 'description': 'Fix Go files with gofumpt, a stricter go fmt.',
\ },
\ 'goimports': {
\ 'function': 'ale#fixers#goimports#Fix',
\ 'suggested_filetypes': ['go'],
@ -421,6 +436,16 @@ let s:default_registry = {
\ 'suggested_filetypes': ['hcl', 'terraform'],
\ 'description': 'Fix tf and hcl files with terraform fmt.',
\ },
\ 'packer': {
\ 'function': 'ale#fixers#packer#Fix',
\ 'suggested_filetypes': ['hcl', 'packer'],
\ 'description': 'Fix Packer HCL files with packer fmt.',
\ },
\ 'crystal': {
\ 'function': 'ale#fixers#crystal#Fix',
\ 'suggested_filetypes': ['cr'],
\ 'description': 'Fix cr (crystal).',
\ },
\ 'ktlint': {
\ 'function': 'ale#fixers#ktlint#Fix',
\ 'suggested_filetypes': ['kt', 'kotlin'],
@ -481,6 +506,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['lua'],
\ 'description': 'Fix Lua files with luafmt.',
\ },
\ 'dprint': {
\ 'function': 'ale#fixers#dprint#Fix',
\ 'suggested_filetypes': ['javascript', 'typescript', 'json', 'markdown'],
\ 'description': 'Pluggable and configurable code formatting platform',
\ },
\ 'stylua': {
\ 'function': 'ale#fixers#stylua#Fix',
\ 'suggested_filetypes': ['lua'],
@ -501,10 +531,20 @@ let s:default_registry = {
\ 'suggested_filetypes': ['pascal'],
\ 'description': 'Fix Pascal files with ptop.',
\ },
\ 'opafmt': {
\ 'function': 'ale#fixers#opafmt#Fix',
\ 'suggested_filetypes': ['rego'],
\ 'description': 'Fix rego files with opa fmt.',
\ },
\ 'vfmt': {
\ 'function': 'ale#fixers#vfmt#Fix',
\ 'suggested_filetypes': ['v'],
\ 'description': 'A formatter for V source code.',
\ },
\ 'zigfmt': {
\ 'function': 'ale#fixers#zigfmt#Fix',
\ 'suggested_filetypes': ['zig'],
\ 'description': 'Official formatter for Zig',
\ }
\}

View File

@ -0,0 +1,12 @@
" Author: Alex McKinney <alexmckinney01@gmail.com>
" Description: Run buf format.
call ale#Set('proto_buf_format_executable', 'buf')
function! ale#fixers#buf_format#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'proto_buf_format_executable')
return {
\ 'command': ale#Escape(l:executable) . ' format %t',
\}
endfunction

View File

@ -0,0 +1,14 @@
call ale#Set('crystal_format_executable', 'crystal')
call ale#Set('crystal_format_options', '')
function! ale#fixers#crystal#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'crystal_format_executable')
let l:options = ale#Var(a:buffer, 'crystal_format_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ' tool format'
\ . ale#Pad(l:options)
\ . ' -'
\}
endfunction

View File

@ -0,0 +1,29 @@
call ale#Set('dprint_executable', 'dprint')
call ale#Set('dprint_executable_override', 0)
call ale#Set('dprint_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('dprint_options', '')
call ale#Set('dprint_config', 'dprint.json')
function! ale#fixers#dprint#Fix(buffer) abort
let l:executable = ale#path#FindExecutable(a:buffer, 'dprint', ['dprint'])
let l:executable_override = ale#Var(a:buffer, 'dprint_executable_override')
if !executable(l:executable) && !l:executable_override
return 0
endif
let l:options = ale#Var(a:buffer, 'dprint_options')
let l:config = ale#path#FindNearestFile(a:buffer, ale#Var(a:buffer, 'dprint_config'))
if !empty(l:config)
let l:options = l:options . ' -c ' . ale#Escape(l:config)
endif
let l:options = l:options . ' --stdin %s'
return {
\ 'command': ale#Escape(l:executable)
\ . ' fmt '
\ . l:options
\}
endfunction

View File

@ -0,0 +1,17 @@
" Author: David Houston <houstdav000>
" Description: A stricter gofmt implementation.
call ale#Set('go_gofumpt_executable', 'gofumpt')
call ale#Set('go_gofumpt_options', '')
function! ale#fixers#gofumpt#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'go_gofumpt_executable')
let l:options = ale#Var(a:buffer, 'go_gofumpt_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ale#Pad(l:options)
\ . ' -w -- %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@ -0,0 +1,15 @@
" Description: Fixer for rego files
call ale#Set('opa_fmt_executable', 'opa')
call ale#Set('opa_fmt_options', '')
function! ale#fixers#opafmt#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'opa_fmt_executable')
let l:options = ale#Var(a:buffer, 'opa_fmt_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ' fmt'
\ . (empty(l:options) ? '' : ' ' . l:options)
\}
endfunction

View File

@ -0,0 +1,17 @@
" Author: Zhuoyun Wei <wzyboy@wzyboy.org>
" Description: Fixer for Packer HCL files
call ale#Set('packer_fmt_executable', 'packer')
call ale#Set('packer_fmt_options', '')
function! ale#fixers#packer#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'packer_fmt_executable')
let l:options = ale#Var(a:buffer, 'packer_fmt_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ' fmt'
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . ' -'
\}
endfunction

View File

@ -0,0 +1,17 @@
" Author: David Houston <houstdav000>
" Description: Provide statix fix as a fixer for simple Nix antipatterns.
call ale#Set('nix_statix_fix_executable', 'statix')
call ale#Set('nix_statix_fix_options', '')
function! ale#fixers#statix#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'nix_statix_fix_executable')
let l:options = ale#Var(a:buffer, 'nix_statix_fix_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ale#Pad('fix')
\ . ale#Pad('--stdin')
\ . ale#Pad(l:options),
\}
endfunction

View File

@ -4,13 +4,30 @@
call ale#Set('c_uncrustify_executable', 'uncrustify')
call ale#Set('c_uncrustify_options', '')
let s:languages = {
\ 'c': 'C',
\ 'cpp': 'CPP',
\ 'cs': 'CS',
\ 'objc': 'OC',
\ 'objcpp': 'OC+',
\ 'd': 'D',
\ 'java': 'JAVA',
\ 'vala': 'VALA',
\ 'p': 'PAWN',
\}
function! ale#fixers#uncrustify#Language(buffer) abort
return get(s:languages, &filetype, 'C')
endfunction
function! ale#fixers#uncrustify#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'c_uncrustify_executable')
let l:options = ale#Var(a:buffer, 'c_uncrustify_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ' --no-backup'
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . ' --no-backup '
\ . '-l' . ale#Pad(ale#fixers#uncrustify#Language(a:buffer))
\ . ale#Pad(l:options)
\}
endfunction

View File

@ -0,0 +1,14 @@
scriptencoding utf-8
" Author: Arash Mousavi <arash-m>
" Description: Official formatter for Zig.
call ale#Set('zig_zigfmt_executable', 'zig')
function! ale#fixers#zigfmt#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'zig_zigfmt_executable')
return {
\ 'command': ale#Escape(l:executable) . ' fmt %t',
\ 'read_temporary_file': 1,
\}
endfunction

View File

@ -7,7 +7,8 @@
function! ale#floating_preview#Show(lines, ...) abort
if !exists('*nvim_open_win') && !has('popupwin')
execute 'echom ''Floating windows not supported in this vim instance.'''
" no-custom-checks
echom 'Floating windows not supported in this vim instance.'
return
endif

View File

@ -0,0 +1,24 @@
function! ale#handlers#actionlint#GetCommand(buffer) abort
return '%e --no-color --oneline %t'
endfunction
function! ale#handlers#actionlint#Handle(buffer, lines) abort
" Matches patterns line the following:
".github/workflows/main.yml:19:0: could not parse as YAML: yaml: line 19: mapping values are not allowed in this context [yaml-syntax]
let l:pattern = '\v^.*:(\d+):(\d+): (.+) \[(.+)\]$'
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,
\ 'text': l:match[3],
\ 'code': l:match[4],
\ 'type': 'E',
\}
call add(l:output, l:item)
endfor
return l:output
endfunction

View File

@ -11,8 +11,9 @@ endfunction
function! ale#handlers#alex#CreateCommandCallback(flags) abort
return {b -> ale#node#Executable(b, ale#handlers#alex#GetExecutable(b))
\ . ' %s '
\ . a:flags}
\ . ' --stdin '
\ . a:flags
\}
endfunction
function! ale#handlers#alex#Handle(buffer, lines) abort
@ -38,6 +39,7 @@ endfunction
" Define a linter for a specific filetype. Accept flags to adapt to the filetype.
" no flags treat input as markdown
" --html treat input as HTML
" --mdx treat input as MDX
" --text treat input as plaintext
function! ale#handlers#alex#DefineLinter(filetype, flags) abort
call ale#Set('alex_executable', 'alex')
@ -49,6 +51,5 @@ function! ale#handlers#alex#DefineLinter(filetype, flags) abort
\ 'command': ale#handlers#alex#CreateCommandCallback(a:flags),
\ 'output_stream': 'stderr',
\ 'callback': 'ale#handlers#alex#Handle',
\ 'lint_file': 1,
\})
endfunction

View File

@ -19,6 +19,18 @@ function! ale#handlers#cppcheck#GetBufferPathIncludeOptions(buffer) abort
endfunction
function! ale#handlers#cppcheck#GetCompileCommandsOptions(buffer) abort
" The compile_commands.json doesn't apply to headers and cppheck will
" bail out if it cannot find a file matching the filter, below. Skip out
" now, for headers. Also, suppress FPs; cppcheck is not meant to
" process lone header files.
let b:buffer_name = bufname(a:buffer)
let b:file_extension = fnamemodify(b:buffer_name, ':e')
if b:file_extension is# 'h' || b:file_extension is# 'hpp'
return ale#handlers#cppcheck#GetBufferPathIncludeOptions(a:buffer)
\ . ' --suppress=unusedStructMember'
endif
" If the current buffer is modified, using compile_commands.json does no
" good, so include the file's directory instead. It's not quite as good as
" using --project, but is at least equivalent to running cppcheck on this
@ -35,8 +47,10 @@ function! ale#handlers#cppcheck#GetCompileCommandsOptions(buffer) abort
" then use the file to set up import paths, etc.
let [l:dir, l:json_path] = ale#c#FindCompileCommands(a:buffer)
" By default, cppcheck processes every config in compile_commands.json.
" Use --file-filter to limit to just the buffer file.
return !empty(l:json_path)
\ ? '--project=' . ale#Escape(l:json_path[len(l:dir) + 1: ])
\ ? '--project=' . ale#Escape(l:json_path[len(l:dir) + 1: ]) . ' --file-filter=' . ale#Escape(bufname(a:buffer))
\ : ''
endfunction
@ -50,7 +64,12 @@ function! ale#handlers#cppcheck#HandleCppCheckFormat(buffer, lines) abort
"test.cpp:974:{column}: error:{inconclusive:inconclusive} Array 'n[3]' accessed at index 3, which is out of bounds. [arrayIndexOutOfBounds]\
" n[3]=3;
" ^
let l:pattern = '\v(\f+):(\d+):(\d+|\{column\}): (\w+):(\{inconclusive:inconclusive\})? ?(.*) \[(\w+)\]\'
"
"" OR if using the misra addon:
"test.c:1:16: style: misra violation (use --rule-texts=<file> to get proper output) [misra-c2012-2.7]\'
"void test( int parm ) {}
" ^
let l:pattern = '\v(\f+):(\d+):(\d+|\{column\}): (\w+):(\{inconclusive:inconclusive\})? ?(.*) \[(%(\w[-.]?)+)\]\'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)

View File

@ -0,0 +1,54 @@
scriptencoding utf-8
" Author: David Houston <houstdav000>
" Description: Define a handler function for cspell's output
function! ale#handlers#cspell#GetExecutable(buffer) abort
return ale#path#FindExecutable(a:buffer,
\ 'cspell', [
\ 'node_modules/.bin/cspell',
\ 'node_modules/cspell/bin.js',
\ ]
\)
endfunction
function! ale#handlers#cspell#GetCommand(buffer) abort
let l:executable = ale#handlers#cspell#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'cspell_options')
return ale#node#Executable(a:buffer, l:executable)
\ . ' lint --no-color --no-progress --no-summary'
\ . ale#Pad(l:options)
\ . ' -- stdin'
endfunction
function! ale#handlers#cspell#Handle(buffer, lines) abort
" Look for lines like the following:
"
" /home/user/repos/ale/README.md:723:48 - Unknown word (stylelint)
let l:pattern = '\v^.*:(\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': 'W',
\})
endfor
return l:output
endfunction
function! ale#handlers#cspell#DefineLinter(filetype) abort
call ale#Set('cspell_executable', 'cspell')
call ale#Set('cspell_options', '')
call ale#Set('cspell_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#linter#Define(a:filetype, {
\ 'name': 'cspell',
\ 'executable': function('ale#handlers#cspell#GetExecutable'),
\ 'command': function('ale#handlers#cspell#GetCommand'),
\ 'callback': 'ale#handlers#cspell#Handle',
\})
endfunction

View File

@ -19,6 +19,16 @@ let s:temp_regex_prefix =
\ . substitute(s:temp_dir, '\\', '\\\\', 'g')
\ . '\.\{-}'
function! s:PanicOutput(lines) abort
return [{
\ 'lnum': 1,
\ 'col': 1,
\ 'text': 'ghc panic!',
\ 'type': 'E',
\ 'detail' : join(a:lines, "\n"),
\}]
endfunction
function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort
" Look for lines like the following.
"
@ -34,6 +44,14 @@ function! ale#handlers#haskell#HandleGHCFormat(buffer, lines) abort
let l:corrected_lines = []
" If ghc panic error, put the whole message in details and exit.
let l:panic_position = match(a:lines,'ghc: panic!')
let l:panic_end = match(a:lines,'Please report this as a GHC bug:')
if l:panic_position >= 0
return s:PanicOutput(a:lines[l:panic_position : l:panic_end])
endif
" Group the lines into smaller lists.
for l:line in a:lines
if len(matchlist(l:line, l:pattern)) > 0

View File

@ -0,0 +1,30 @@
" Author: rhysd <https://github.com/rhysd>
" Description: Handle errors for naga-cli.
function! ale#handlers#naga#Handle(buffer, lines) abort
let l:errors = []
let l:current_error = v:null
for l:line in a:lines
if l:line =~# '^error: '
let l:text = l:line[7:]
let l:current_error = { 'text': l:text, 'type': 'E' }
continue
endif
if l:current_error isnot v:null
let l:matches = matchlist(l:line, '\v:(\d+):(\d+)$')
if !empty(l:matches)
let l:current_error.lnum = str2nr(l:matches[1])
let l:current_error.col = str2nr(l:matches[2])
call add(l:errors, l:current_error)
let l:current_error = v:null
continue
endif
endif
endfor
return l:errors
endfunction

View File

@ -63,26 +63,35 @@ function! ale#handlers#sml#Handle(buffer, lines) abort
let l:match2 = matchlist(l:line, l:pattern2)
if len(l:match2) != 0
call add(l:out, {
\ 'filename': l:match2[1],
if l:match2[1] =~# 'stdIn$'
let l:loc = {'bufnr': a:buffer}
else
let l:loc = {'filename': l:match2[1]}
endif
call add(l:out, extend(l:loc, {
\ 'lnum': l:match2[2] + 0,
\ 'col' : l:match2[3] - 1,
\ 'text': l:match2[4],
\ 'type': l:match2[4] =~# '^Warning' ? 'W' : 'E',
\})
\}))
continue
endif
let l:match = matchlist(l:line, l:pattern)
if len(l:match) != 0
call add(l:out, {
\ 'filename': l:match[1],
if l:match[1] =~# 'stdIn$'
let l:loc = {'bufnr': a:buffer}
else
let l:loc = {'filename': l:match[1]}
endif
call add(l:out, extend(l:loc, {
\ 'lnum': l:match[2] + 0,
\ 'text': l:match[3] . ': ' . l:match[4],
\ 'type': l:match[3] is# 'error' ? 'E' : 'W',
\})
\}))
continue
endif
endfor

View File

@ -0,0 +1,24 @@
scriptencoding utf-8
" Author: David Houston
" Description: This file defines a handler function for statix's errorformat
" output.
function! ale#handlers#statix#Handle(buffer, lines) abort
" Look for lines like the following.
"
" flake.nix>46:13:W:3:This assignment is better written with `inherit`
let l:pattern = '\v^.*\>(\d+):(\d+):([A-Z]):(\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,
\ 'type': l:match[3],
\ 'code': l:match[4],
\ 'text': l:match[5],
\})
endfor
return l:output
endfunction

View File

@ -22,6 +22,26 @@ if !hlexists('ALEInfo')
highlight link ALEInfo ALEWarning
endif
if !hlexists('ALEVirtualTextError')
highlight link ALEVirtualTextError ALEError
endif
if !hlexists('ALEVirtualTextStyleError')
highlight link ALEVirtualTextStyleError ALEVirtualTextError
endif
if !hlexists('ALEVirtualTextWarning')
highlight link ALEVirtualTextWarning ALEWarning
endif
if !hlexists('ALEVirtualTextStyleWarning')
highlight link ALEVirtualTextStyleWarning ALEVirtualTextWarning
endif
if !hlexists('ALEVirtualTextInfo')
highlight link ALEVirtualTextInfo ALEVirtualTextWarning
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)

View File

@ -46,7 +46,7 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort
call balloon_show(a:response.body.displayString)
elseif get(l:options, 'truncated_echo', 0)
if !empty(a:response.body.displayString)
call ale#cursor#TruncatedEcho(split(a:response.body.displayString, "\n")[0])
call ale#cursor#TruncatedEcho(a:response.body.displayString)
endif
elseif g:ale_hover_to_floating_preview || g:ale_floating_preview
call ale#floating_preview#Show(split(a:response.body.displayString, "\n"), {
@ -231,7 +231,11 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
\&& (l:set_balloons is 1 || l:set_balloons is# 'hover')
call balloon_show(join(l:lines, "\n"))
elseif get(l:options, 'truncated_echo', 0)
call ale#cursor#TruncatedEcho(l:lines[0])
if type(l:lines[0]) is# v:t_list
call ale#cursor#TruncatedEcho(join(l:lines[0], '\n'))
else
call ale#cursor#TruncatedEcho(l:lines[0])
endif
elseif g:ale_hover_to_floating_preview || g:ale_floating_preview
call ale#floating_preview#Show(l:lines, {
\ 'filetype': 'ale-preview.message',

View File

@ -187,10 +187,16 @@ function! ale#job#PrepareCommand(buffer, command) abort
\ : a:command
" If a custom shell is specified, use that.
if exists('g:ale_shell')
let l:shell_arguments = get(g:, 'ale_shell_arguments', &shellcmdflag)
if exists('b:ale_shell')
let l:ale_shell = b:ale_shell
elseif exists('g:ale_shell')
let l:ale_shell = g:ale_shell
endif
return split(g:ale_shell) + split(l:shell_arguments) + [l:command]
if exists('l:ale_shell')
let l:shell_arguments = get(b:, 'ale_shell_arguments', get(g:, 'ale_shell_arguments', &shellcmdflag))
return split(l:ale_shell) + split(l:shell_arguments) + [l:command]
endif
if has('win32')

View File

@ -19,6 +19,7 @@ let s:default_ale_linter_aliases = {
\ 'rmd': 'r',
\ 'systemverilog': 'verilog',
\ 'typescriptreact': ['typescript', 'tsx'],
\ 'vader': ['vim', 'vader'],
\ 'verilog_systemverilog': ['verilog_systemverilog', 'verilog'],
\ 'vimwiki': 'markdown',
\ 'vue': ['vue', 'javascript'],
@ -45,15 +46,20 @@ let s:default_ale_linters = {
\ 'hack': ['hack'],
\ 'help': [],
\ 'inko': ['inko'],
\ 'json': ['jsonlint', 'spectral', 'vscodejson'],
\ 'json5': [],
\ 'jsonc': [],
\ 'perl': ['perlcritic'],
\ 'perl6': [],
\ 'python': ['flake8', 'mypy', 'pylint', 'pyright'],
\ 'rust': ['cargo', 'rls'],
\ 'spec': [],
\ 'text': [],
\ 'vader': ['vimls'],
\ 'vue': ['eslint', 'vls'],
\ 'zsh': ['shell'],
\ 'v': ['v'],
\ 'yaml': ['spectral', 'yaml-language-server', 'yamllint'],
\}
" Testing/debugging helper to unload all linters.

View File

@ -36,12 +36,22 @@ function! ale#list#IsQuickfixOpen() abort
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
" that setting being on, or that the error count is at least as high as the
" setting when set to an integer value.
function! s:ShouldOpen(buffer, loclist_len) 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)
return l:val > 0 ? a:loclist_len >= l:val : l:val is# 'on_save' && l:saved
endfunction
" Check if we should close the list, based on the save event being fired, and
" that setting being on, or the setting just being set to an integer value.
function! s:ShouldClose(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 >= 1) || (l:val is# 'on_save' && l:saved))
endfunction
function! s:Deduplicate(list) abort
@ -122,9 +132,9 @@ function! s:SetListsImpl(timer_id, buffer, loclist) abort
" 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)
" ShouldOpen() checks if the current buffer has enough problems to be
" opened.
if s:ShouldOpen(a:buffer, len(a:loclist))
let l:winnr = winnr()
let l:mode = mode()
@ -212,8 +222,25 @@ function! ale#list#SetLists(buffer, loclist) abort
endif
endfunction
function! ale#list#ForcePopulateErrorList(populate_quickfix) abort
let l:quickfix_bak = g:ale_set_quickfix
let g:ale_set_quickfix = a:populate_quickfix
let l:loclist_bak = g:ale_set_loclist
let g:ale_set_loclist = !a:populate_quickfix
let l:open_list_bak = g:ale_open_list
let g:ale_open_list = 1
let l:buffer = bufnr('')
let l:loclist = get(g:ale_buffer_info, l:buffer, {'loclist': []}).loclist
call s:SetListsImpl(-1, l:buffer, l:loclist)
let g:ale_open_list = l:open_list_bak
let g:ale_set_loclist = l:loclist_bak
let g:ale_set_quickfix = l:quickfix_bak
endfunction
function! s:CloseWindowIfNeeded(buffer) abort
if ale#Var(a:buffer, 'keep_list_window_open') || !s:ShouldOpen(a:buffer)
if ale#Var(a:buffer, 'keep_list_window_open') || s:ShouldClose(a:buffer)
return
endif

View File

@ -38,11 +38,13 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort
\ 'capabilities': {
\ 'hover': 0,
\ 'rename': 0,
\ 'filerename': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ 'typeDefinition': 0,
\ 'implementation': 0,
\ 'symbol_search': 0,
\ 'code_actions': 0,
\ 'did_save': 0,
@ -258,6 +260,14 @@ function! s:UpdateCapabilities(conn, capabilities) abort
let a:conn.capabilities.typeDefinition = 1
endif
if get(a:capabilities, 'implementationProvider') is v:true
let a:conn.capabilities.implementation = 1
endif
if type(get(a:capabilities, 'implementationProvider')) is v:t_dict
let a:conn.capabilities.implementation = 1
endif
if get(a:capabilities, 'workspaceSymbolProvider') is v:true
let a:conn.capabilities.symbol_search = 1
endif
@ -378,8 +388,10 @@ function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
let l:conn.capabilities.completion_trigger_characters = ['.']
let l:conn.capabilities.definition = 1
let l:conn.capabilities.typeDefinition = 1
let l:conn.capabilities.implementation = 1
let l:conn.capabilities.symbol_search = 1
let l:conn.capabilities.rename = 1
let l:conn.capabilities.filerename = 1
let l:conn.capabilities.code_actions = 1
endfunction
@ -436,11 +448,20 @@ function! s:SendInitMessage(conn) abort
\ 'typeDefinition': {
\ 'dynamicRegistration': v:false,
\ },
\ 'implementation': {
\ 'dynamicRegistration': v:false,
\ 'linkSupport': v:false,
\ },
\ 'publishDiagnostics': {
\ 'relatedInformation': v:true,
\ },
\ 'codeAction': {
\ 'dynamicRegistration': v:false,
\ 'codeActionLiteralSupport': {
\ 'codeActionKind': {
\ 'valueSet': []
\ }
\ }
\ },
\ 'rename': {
\ 'dynamicRegistration': v:false,
@ -465,6 +486,7 @@ function! ale#lsp#StartProgram(conn_id, executable, command) abort
let l:options = {
\ 'mode': 'raw',
\ 'out_cb': {_, message -> ale#lsp#HandleMessage(a:conn_id, message)},
\ 'exit_cb': { -> ale#lsp#Stop(a:conn_id) },
\}
if has('win32')

View File

@ -35,7 +35,7 @@ function! ale#lsp#message#Initialize(root_path, options, capabilities) abort
\ 'rootPath': a:root_path,
\ 'capabilities': a:capabilities,
\ 'initializationOptions': a:options,
\ 'rootUri': ale#path#ToURI(a:root_path),
\ 'rootUri': ale#util#ToURI(a:root_path),
\}]
endfunction
@ -56,7 +56,7 @@ function! ale#lsp#message#DidOpen(buffer, language_id) abort
return [1, 'textDocument/didOpen', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ 'languageId': a:language_id,
\ 'version': ale#lsp#message#GetNextVersionID(),
\ 'text': join(l:lines, "\n") . "\n",
@ -70,21 +70,21 @@ function! ale#lsp#message#DidChange(buffer) abort
" 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')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ 'version': ale#lsp#message#GetNextVersionID(),
\ },
\ 'contentChanges': [{'text': join(l:lines, "\n") . "\n"}]
\}]
endfunction
function! ale#lsp#message#DidSave(buffer, includeText) abort
function! ale#lsp#message#DidSave(buffer, include_text) abort
let l:response = [1, 'textDocument/didSave', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\}]
if a:includeText
if a:include_text
let l:response[2].textDocument.version = ale#lsp#message#GetNextVersionID()
let l:response[2].text = ale#util#GetBufferContents(a:buffer)
endif
@ -95,7 +95,7 @@ endfunction
function! ale#lsp#message#DidClose(buffer) abort
return [1, 'textDocument/didClose', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\}]
endfunction
@ -106,7 +106,7 @@ 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')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
@ -124,7 +124,7 @@ endfunction
function! ale#lsp#message#Definition(buffer, line, column) abort
return [0, 'textDocument/definition', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
@ -133,7 +133,16 @@ endfunction
function! ale#lsp#message#TypeDefinition(buffer, line, column) abort
return [0, 'textDocument/typeDefinition', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
endfunction
function! ale#lsp#message#Implementation(buffer, line, column) abort
return [0, 'textDocument/implementation', {
\ 'textDocument': {
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
@ -142,7 +151,7 @@ endfunction
function! ale#lsp#message#References(buffer, line, column) abort
return [0, 'textDocument/references', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\ 'context': {'includeDeclaration': v:false},
@ -158,7 +167,7 @@ endfunction
function! ale#lsp#message#Hover(buffer, line, column) abort
return [0, 'textDocument/hover', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
@ -173,7 +182,7 @@ endfunction
function! ale#lsp#message#Rename(buffer, line, column, new_name) abort
return [0, 'textDocument/rename', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\ 'newName': a:new_name,
@ -183,7 +192,7 @@ endfunction
function! ale#lsp#message#CodeAction(buffer, line, column, end_line, end_column, diagnostics) abort
return [0, 'textDocument/codeAction', {
\ 'textDocument': {
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'range': {
\ 'start': {'line': a:line - 1, 'character': a:column - 1},

View File

@ -59,7 +59,7 @@ function! ale#lsp#response#ReadDiagnostics(response) abort
\ && l:diagnostic.relatedInformation isnot v:null
let l:related = deepcopy(l:diagnostic.relatedInformation)
call map(l:related, {key, val ->
\ ale#path#FromURI(val.location.uri) .
\ ale#util#ToResource(val.location.uri) .
\ ':' . (val.location.range.start.line + 1) .
\ ':' . (val.location.range.start.character + 1) .
\ ":\n\t" . val.message

View File

@ -72,6 +72,14 @@ function! ale#lsp#tsserver_message#TypeDefinition(buffer, line, column) abort
\}]
endfunction
function! ale#lsp#tsserver_message#Implementation(buffer, line, column) abort
return [0, 'ts@implementation', {
\ 'line': a:line,
\ 'offset': a:column,
\ 'file': expand('#' . a:buffer . ':p'),
\}]
endfunction
function! ale#lsp#tsserver_message#References(buffer, line, column) abort
return [0, 'ts@references', {
\ 'line': a:line,
@ -101,6 +109,14 @@ function! ale#lsp#tsserver_message#Rename(
\}]
endfunction
function! ale#lsp#tsserver_message#GetEditsForFileRename(
\ oldFilePath, newFilePath) abort
return [0, 'ts@getEditsForFileRename', {
\ 'oldFilePath': a:oldFilePath,
\ 'newFilePath': a:newFilePath,
\}]
endfunction
function! ale#lsp#tsserver_message#OrganizeImports(buffer) abort
return [0, 'ts@organizeImports', {
\ 'scope': {

View File

@ -11,6 +11,22 @@ endif
" A Dictionary to track one-shot handlers for custom LSP requests
let s:custom_handlers_map = get(s:, 'custom_handlers_map', {})
" Clear LSP linter data for the linting engine.
function! ale#lsp_linter#ClearLSPData() abort
let s:lsp_linter_map = {}
let s:custom_handlers_map = {}
endfunction
" Only for internal use.
function! ale#lsp_linter#GetLSPLinterMap() abort
return s:lsp_linter_map
endfunction
" Just for tests.
function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
let s:lsp_linter_map = a:replacement_map
endfunction
" Check if diagnostics for a particular linter should be ignored.
function! s:ShouldIgnore(buffer, linter_name) abort
" Ignore all diagnostics if LSP integration is disabled.
@ -33,7 +49,7 @@ 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:filename = ale#util#ToResource(a:response.params.uri)
let l:escaped_name = escape(
\ fnameescape(l:filename),
\ has('win32') ? '^' : '^,}]'
@ -466,8 +482,8 @@ function! s:CheckWithLSP(linter, details) abort
" If this was a file save event, also notify the server of that.
if a:linter.lsp isnot# 'tsserver'
\&& getbufvar(l:buffer, 'ale_save_event_fired', 0)
\&& ale#lsp#HasCapability(l:buffer, 'did_save')
let l:include_text = ale#lsp#HasCapability(l:buffer, 'includeText')
\&& ale#lsp#HasCapability(l:id, 'did_save')
let l:include_text = ale#lsp#HasCapability(l:id, 'includeText')
let l:save_message = ale#lsp#message#DidSave(l:buffer, l:include_text)
let l:notified = ale#lsp#Send(l:id, l:save_message) != 0
endif
@ -477,17 +493,6 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
return ale#lsp_linter#StartLSP(a:buffer, a:linter, function('s:CheckWithLSP'))
endfunction
" Clear LSP linter data for the linting engine.
function! ale#lsp_linter#ClearLSPData() abort
let s:lsp_linter_map = {}
let s:custom_handlers_map = {}
endfunction
" Just for tests.
function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
let s:lsp_linter_map = a:replacement_map
endfunction
function! s:HandleLSPResponseToCustomRequests(conn_id, response) abort
if has_key(a:response, 'id')
\&& has_key(s:custom_handlers_map, a:response.id)

View File

@ -1,6 +1,6 @@
" Author: Jerko Steiner <jerko.steiner@gmail.com>
" Description: Organize imports support for tsserver
"
function! ale#organize_imports#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') isnot# 'organizeImports'
return
@ -17,7 +17,10 @@ function! ale#organize_imports#HandleTSServerResponse(conn_id, response) abort
\ 'description': 'Organize Imports',
\ 'changes': l:file_code_edits,
\ },
\ {}
\ {
\ 'conn_id': a:conn_id,
\ 'should_save': !&hidden,
\ },
\)
endfunction

View File

@ -218,7 +218,7 @@ 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
function! ale#path#ToFileURI(path) abort
let l:has_drive_letter = a:path[1:2] is# ':\'
return substitute(
@ -231,7 +231,7 @@ function! ale#path#ToURI(path) abort
\)
endfunction
function! ale#path#FromURI(uri) abort
function! ale#path#FromFileURI(uri) abort
if a:uri[:6] is? 'file://'
let l:encoded_path = a:uri[7:]
elseif a:uri[:4] is? 'file:'

View File

@ -26,6 +26,7 @@ function! ale#python#FindProjectRootIni(buffer) abort
\|| filereadable(l:path . '/tox.ini')
\|| filereadable(l:path . '/.pyre_configuration.local')
\|| filereadable(l:path . '/mypy.ini')
\|| filereadable(l:path . '/.mypy.ini')
\|| filereadable(l:path . '/pycodestyle.cfg')
\|| filereadable(l:path . '/.flake8')
\|| filereadable(l:path . '/.flake8rc')

View File

@ -16,6 +16,23 @@ function! ale#references#ClearLSPData() abort
let s:references_map = {}
endfunction
function! ale#references#FormatTSResponseItem(response_item, options) abort
if get(a:options, 'open_in') is# 'quickfix'
return {
\ 'filename': a:response_item.file,
\ 'lnum': a:response_item.start.line,
\ 'col': a:response_item.start.offset,
\}
else
return {
\ 'filename': a:response_item.file,
\ 'line': a:response_item.start.line,
\ 'column': a:response_item.start.offset,
\ 'match': substitute(a:response_item.lineText, '^\s*\(.\{-}\)\s*$', '\1', ''),
\}
endif
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)
@ -25,23 +42,43 @@ function! ale#references#HandleTSServerResponse(conn_id, response) abort
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,
\ 'match': substitute(l:response_item.lineText, '^\s*\(.\{-}\)\s*$', '\1', ''),
\})
call add(
\ l:item_list,
\ ale#references#FormatTSResponseItem(l:response_item, l:options)
\)
endfor
if empty(l:item_list)
call ale#util#Execute('echom ''No references found.''')
else
call ale#preview#ShowSelection(l:item_list, l:options)
if get(l:options, 'open_in') is# 'quickfix'
call setqflist([], 'r')
call setqflist(l:item_list, 'a')
call ale#util#Execute('cc 1')
else
call ale#preview#ShowSelection(l:item_list, l:options)
endif
endif
endif
endif
endfunction
function! ale#references#FormatLSPResponseItem(response_item, options) abort
if get(a:options, 'open_in') is# 'quickfix'
return {
\ 'filename': ale#util#ToResource(a:response_item.uri),
\ 'lnum': a:response_item.range.start.line + 1,
\ 'col': a:response_item.range.start.character + 1,
\}
else
return {
\ 'filename': ale#util#ToResource(a:response_item.uri),
\ 'line': a:response_item.range.start.line + 1,
\ 'column': a:response_item.range.start.character + 1,
\}
endif
endfunction
function! ale#references#HandleLSPResponse(conn_id, response) abort
if has_key(a:response, 'id')
\&& has_key(s:references_map, a:response.id)
@ -53,18 +90,22 @@ function! ale#references#HandleLSPResponse(conn_id, response) abort
if type(l:result) is v:t_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,
\})
call add(l:item_list,
\ ale#references#FormatLSPResponseItem(l:response_item, l:options)
\)
endfor
endif
if empty(l:item_list)
call ale#util#Execute('echom ''No references found.''')
else
call ale#preview#ShowSelection(l:item_list, l:options)
if get(l:options, 'open_in') is# 'quickfix'
call setqflist([], 'r')
call setqflist(l:item_list, 'a')
call ale#util#Execute('cc 1')
else
call ale#preview#ShowSelection(l:item_list, l:options)
endif
endif
endif
endfunction
@ -119,6 +160,8 @@ function! ale#references#Find(...) abort
let l:options.open_in = 'split'
elseif l:option is? '-vsplit'
let l:options.open_in = 'vsplit'
elseif l:option is? '-quickfix'
let l:options.open_in = 'quickfix'
endif
endfor
endif

View File

@ -84,7 +84,8 @@ function! ale#rename#HandleTSServerResponse(conn_id, response) abort
\ 'changes': l:changes,
\ },
\ {
\ 'should_save': 1,
\ 'conn_id': a:conn_id,
\ 'should_save': !&hidden,
\ },
\)
endfunction
@ -116,7 +117,8 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort
\ 'changes': l:changes,
\ },
\ {
\ 'should_save': 1,
\ 'conn_id': a:conn_id,
\ 'should_save': !&hidden,
\ },
\)
endif

View File

@ -41,7 +41,7 @@ function! ale#symbol#HandleLSPResponse(conn_id, response) abort
let l:location = l:response_item.location
call add(l:item_list, {
\ 'filename': ale#path#FromURI(l:location.uri),
\ 'filename': ale#util#ToResource(l:location.uri),
\ 'line': l:location.range.start.line + 1,
\ 'column': l:location.range.start.character + 1,
\ 'match': l:response_item.name,

View File

@ -62,25 +62,34 @@ function! ale#test#SetFilename(path) abort
silent! noautocmd execute 'file ' . fnameescape(l:full_path)
endfunction
function! s:RemoveModule(results) abort
function! RemoveNewerKeys(results) abort
for l:item in a:results
if has_key(l:item, 'module')
call remove(l:item, 'module')
endif
if has_key(l:item, 'end_col')
call remove(l:item, 'end_col')
endif
if has_key(l:item, 'end_lnum')
call remove(l:item, 'end_lnum')
endif
endfor
endfunction
" Return loclist data without the module string, only in newer Vim versions.
function! ale#test#GetLoclistWithoutModule() abort
" Return loclist data with only the keys supported by the lowest Vim versions.
function! ale#test#GetLoclistWithoutNewerKeys() abort
let l:results = getloclist(0)
call s:RemoveModule(l:results)
call RemoveNewerKeys(l:results)
return l:results
endfunction
function! ale#test#GetQflistWithoutModule() abort
" Return quickfix data with only the keys supported by the lowest Vim versions.
function! ale#test#GetQflistWithoutNewerKeys() abort
let l:results = getqflist()
call s:RemoveModule(l:results)
call RemoveNewerKeys(l:results)
return l:results
endfunction

View File

@ -64,7 +64,8 @@ function! ale#toggle#ToggleBuffer(buffer) abort
" 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'''
" no-custom-checks
echom 'ALE cannot be enabled locally when disabled globally'
return
endif

View File

@ -25,3 +25,19 @@ function! ale#uri#Decode(value) abort
\ 'g'
\)
endfunction
let s:uri_handlers = {
\ 'jdt': {
\ 'OpenURILink': function('ale#uri#jdt#OpenJDTLink'),
\ }
\}
function! ale#uri#GetURIHandler(uri) abort
for l:scheme in keys(s:uri_handlers)
if a:uri =~# '^'.l:scheme.'://'
return s:uri_handlers[scheme]
endif
endfor
return v:null
endfunction

View File

@ -0,0 +1,110 @@
" Author: yoshi1123 <yoshi1@tutanota.com>
" Description: Functions for working with jdt:// URIs.
function! s:OpenJDTLink(root, uri, line, column, options, result) abort
if has_key(a:result, 'error')
" no-custom-checks
echoerr a:result.error.message
return
endif
let l:contents = a:result['result']
if type(l:contents) is# type(v:null)
" no-custom-checks
echoerr 'File content not found'
endif
" disable autocmd when opening buffer
autocmd! AleURISchemes
call ale#util#Open(a:uri, a:line, a:column, a:options)
autocmd AleURISchemes BufNewFile,BufReadPre jdt://** call ale#uri#jdt#ReadJDTLink(expand('<amatch>'))
if !empty(getbufvar(bufnr(''), 'ale_lsp_root', ''))
return
endif
let b:ale_lsp_root = a:root
set filetype=java
call setline(1, split(l:contents, '\n'))
call cursor(a:line, a:column)
normal! zz
setlocal buftype=nofile nomodified nomodifiable readonly
endfunction
" Load new buffer with jdt:// contents and jump to line and column.
function! ale#uri#jdt#OpenJDTLink(encoded_uri, line, column, options, conn_id) abort
let l:found_eclipselsp = v:false
for l:linter in ale#linter#Get('java')
if l:linter.name is# 'eclipselsp'
let l:found_eclipselsp = v:true
endif
endfor
if !l:found_eclipselsp
throw 'eclipselsp not running'
endif
let l:root = a:conn_id[stridx(a:conn_id, ':')+1:]
let l:uri = a:encoded_uri
call ale#lsp_linter#SendRequest(
\ bufnr(''),
\ 'eclipselsp',
\ [0, 'java/classFileContents', {'uri': ale#util#ToURI(l:uri)}],
\ function('s:OpenJDTLink', [l:root, l:uri, a:line, a:column, a:options])
\)
endfunction
function! s:ReadClassFileContents(uri, result) abort
if has_key(a:result, 'error')
" no-custom-checks
echoerr a:result.error.message
return
endif
let l:contents = a:result['result']
if type(l:contents) is# type(v:null)
" no-custom-checks
echoerr 'File content not found'
endif
call setline(1, split(l:contents, '\n'))
setlocal buftype=nofile nomodified nomodifiable readonly
endfunction
" Read jdt:// contents, as part of current project, into current buffer.
function! ale#uri#jdt#ReadJDTLink(encoded_uri) abort
if !empty(getbufvar(bufnr(''), 'ale_lsp_root', ''))
return
endif
let l:linter_map = ale#lsp_linter#GetLSPLinterMap()
for l:conn_id in keys(l:linter_map)
if l:linter_map[l:conn_id] is# 'eclipselsp'
let l:root = l:conn_id[stridx(l:conn_id, ':')+1:]
endif
endfor
if l:root is# v:null
throw 'eclipselsp not running'
endif
let l:uri = a:encoded_uri
let b:ale_lsp_root = l:root
set filetype=java
call ale#lsp_linter#SendRequest(
\ bufnr(''),
\ 'eclipselsp',
\ [0, 'java/classFileContents', {'uri': ale#util#ToURI(l:uri)}],
\ function('s:ReadClassFileContents', [l:uri])
\)
endfunction

View File

@ -25,7 +25,8 @@ 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'
" no-custom-checks
echo a:string
else
call ale#preview#Show(split(a:string, "\n"), extend(
\ {
@ -491,8 +492,12 @@ function! ale#util#FindItemAtCursor(buffer) abort
return [l:info, l:loc]
endfunction
function! ale#util#Input(message, value) abort
return input(a:message, a:value)
function! ale#util#Input(message, value, ...) abort
if a:0 > 0
return input(a:message, a:value, a:1)
else
return input(a:message, a:value)
endif
endfunction
function! ale#util#HasBuflineApi() abort
@ -539,3 +544,31 @@ endfunction
function! ale#util#GetBufferContents(buffer) abort
return join(getbufline(a:buffer, 1, '$'), '\n') . '\n'
endfunction
function! ale#util#ToURI(resource) abort
let l:uri_handler = ale#uri#GetURIHandler(a:resource)
if l:uri_handler is# v:null
" resource is a filesystem path
let l:uri = ale#path#ToFileURI(a:resource)
else
" resource is a URI
let l:uri = a:resource
endif
return l:uri
endfunction
function! ale#util#ToResource(uri) abort
let l:uri_handler = ale#uri#GetURIHandler(a:uri)
if l:uri_handler is# v:null
" resource is a filesystem path
let l:resource = ale#path#FromFileURI(a:uri)
else
" resource is a URI
let l:resource = a:uri
endif
return l:resource
endfunction

View File

@ -18,26 +18,6 @@ elseif has('textprop') && has('popupwin')
let s:has_virt_text = 1
endif
if !hlexists('ALEVirtualTextError')
highlight link ALEVirtualTextError ALEError
endif
if !hlexists('ALEVirtualTextStyleError')
highlight link ALEVirtualTextStyleError ALEVirtualTextError
endif
if !hlexists('ALEVirtualTextWarning')
highlight link ALEVirtualTextWarning ALEWarning
endif
if !hlexists('ALEVirtualTextStyleWarning')
highlight link ALEVirtualTextStyleWarning ALEVirtualTextWarning
endif
if !hlexists('ALEVirtualTextInfo')
highlight link ALEVirtualTextInfo ALEVirtualTextWarning
endif
function! ale#virtualtext#Clear() abort
if !s:has_virt_text
return