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-07-30 23:18:16 +02:00
parent 46f1a1bd52
commit 587a185a98
89 changed files with 2619 additions and 1634 deletions

View File

@ -95,7 +95,7 @@ function! ale#Queue(delay, ...) abort
throw "linting_flag must be either '' or 'lint_file'"
endif
if type(l:buffer) != type(0)
if type(l:buffer) isnot v:t_number
throw 'buffer_number must be a Number'
endif
@ -192,12 +192,7 @@ endfunction
" Every variable name will be prefixed with 'ale_'.
function! ale#Var(buffer, variable_name) abort
let l:full_name = 'ale_' . a:variable_name
let l:vars = getbufvar(str2nr(a:buffer), '', 0)
if l:vars is 0
" Look for variables from deleted buffers, saved from :ALEFix
let l:vars = get(get(g:ale_fix_buffer_data, a:buffer, {}), 'vars', {})
endif
let l:vars = getbufvar(str2nr(a:buffer), '', {})
return get(l:vars, l:full_name, g:[l:full_name])
endfunction

View File

@ -30,7 +30,7 @@ function! ale#assert#Linter(expected_executable, expected_command) abort
let l:callbacks = map(copy(l:linter.command_chain), 'v:val.callback')
" If the expected command is a string, just check the last one.
if type(a:expected_command) is type('')
if type(a:expected_command) is v:t_string
if len(l:callbacks) is 1
let l:command = call(l:callbacks[0], [l:buffer])
else

View File

@ -34,26 +34,25 @@ function! ale#balloon#Expr() abort
endfunction
function! ale#balloon#Disable() abort
if has('balloon_eval_term')
set noballoonevalterm
if has('balloon_eval')
set noballooneval
set balloonexpr=
endif
set noballooneval
set balloonexpr=
if has('balloon_eval_term')
set noballoonevalterm
set balloonexpr=
endif
endfunction
function! ale#balloon#Enable() abort
if !has('balloon_eval') && !has('balloon_eval_term')
return
endif
if has('balloon_eval')
set ballooneval
set balloonexpr=ale#balloon#Expr()
endif
if has('balloon_eval_term')
set balloonevalterm
set balloonexpr=ale#balloon#Expr()
endif
set balloonexpr=ale#balloon#Expr()
endfunction

View File

@ -2,10 +2,14 @@
" Description: Functions for integrating with C-family linters.
call ale#Set('c_parse_makefile', 0)
call ale#Set('c_parse_compile_commands', 0)
let s:sep = has('win32') ? '\' : '/'
" Set just so tests can override it.
let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt']
function! ale#c#FindProjectRoot(buffer) abort
for l:project_filename in ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt']
for l:project_filename in g:__ale_c_project_filenames
let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename)
if !empty(l:full_path)
@ -23,11 +27,11 @@ function! ale#c#FindProjectRoot(buffer) abort
return ''
endfunction
function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort
function! ale#c#ParseCFlags(path_prefix, cflag_line) abort
let l:cflags_list = []
let l:previous_options = []
for l:option in a:cflags
for l:option in split(a:cflag_line, '-')
call add(l:previous_options, l:option)
" Check if cflag contained a '-' and should not have been splitted
let l:option_list = split(l:option, '\zs')
@ -59,32 +63,122 @@ function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort
endif
endfor
return l:cflags_list
return join(l:cflags_list, ' ')
endfunction
function! ale#c#ParseCFlags(buffer, stdout_make) abort
function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort
if !g:ale_c_parse_makefile
return []
return ''
endif
let l:buffer_filename = expand('#' . a:buffer . ':t')
let l:cflags = []
for l:lines in split(a:stdout_make, '\\n')
if stridx(l:lines, l:buffer_filename) >= 0
let l:cflags = split(l:lines, '-')
let l:cflag_line = ''
" Find a line matching this buffer's filename in the make output.
for l:line in a:make_output
if stridx(l:line, l:buffer_filename) >= 0
let l:cflag_line = l:line
break
endif
endfor
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
return ale#c#ParseCFlagsToList(fnamemodify(l:makefile_path, ':p:h'), l:cflags)
let l:makefile_dir = fnamemodify(l:makefile_path, ':p:h')
return ale#c#ParseCFlags(l:makefile_dir, l:cflag_line)
endfunction
" Given a buffer number, find the build subdirectory with compile commands
" The subdirectory is returned without the trailing /
function! ale#c#FindCompileCommands(buffer) abort
" Look above the current source file to find compile_commands.json
let l:json_file = ale#path#FindNearestFile(a:buffer, 'compile_commands.json')
if !empty(l:json_file)
return l:json_file
endif
" Search in build directories if we can't find it in the project.
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
for l:dirname in ale#Var(a:buffer, 'c_build_dir_names')
let l:c_build_dir = l:path . s:sep . l:dirname
let l:json_file = l:c_build_dir . s:sep . 'compile_commands.json'
if filereadable(l:json_file)
return l:json_file
endif
endfor
endfor
return ''
endfunction
" Cache compile_commands.json data in a Dictionary, so we don't need to read
" the same files over and over again. The key in the dictionary will include
" the last modified time of the file.
if !exists('s:compile_commands_cache')
let s:compile_commands_cache = {}
endif
function! s:GetListFromCompileCommandsFile(compile_commands_file) abort
if empty(a:compile_commands_file)
return []
endif
let l:time = getftime(a:compile_commands_file)
if l:time < 0
return []
endif
let l:key = a:compile_commands_file . ':' . l:time
if has_key(s:compile_commands_cache, l:key)
return s:compile_commands_cache[l:key]
endif
let l:data = []
silent! let l:data = json_decode(join(readfile(a:compile_commands_file), ''))
if !empty(l:data)
let s:compile_commands_cache[l:key] = l:data
return l:data
endif
return []
endfunction
function! ale#c#ParseCompileCommandsFlags(buffer, dir, json_list) abort
for l:item in a:json_list
if bufnr(l:item.file) is a:buffer
return ale#c#ParseCFlags(a:dir, l:item.command)
endif
endfor
return ''
endfunction
function! ale#c#FlagsFromCompileCommands(buffer, compile_commands_file) abort
let l:dir = ale#path#Dirname(a:compile_commands_file)
let l:json_list = s:GetListFromCompileCommandsFile(a:compile_commands_file)
return ale#c#ParseCompileCommandsFlags(a:buffer, l:dir, l:json_list)
endfunction
function! ale#c#GetCFlags(buffer, output) abort
let l:cflags = ' '
if g:ale_c_parse_makefile && !empty(a:output)
let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') . ' '
if ale#Var(a:buffer, 'c_parse_makefile') && !empty(a:output)
let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output)
endif
if ale#Var(a:buffer, 'c_parse_compile_commands')
let l:json_file = ale#c#FindCompileCommands(a:buffer)
if !empty(l:json_file)
let l:cflags = ale#c#FlagsFromCompileCommands(a:buffer, l:json_file)
endif
endif
if l:cflags is# ' '
@ -95,8 +189,9 @@ function! ale#c#GetCFlags(buffer, output) abort
endfunction
function! ale#c#GetMakeCommand(buffer) abort
if g:ale_c_parse_makefile
if ale#Var(a:buffer, 'c_parse_makefile')
let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile')
if !empty(l:makefile_path)
return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n'
endif
@ -158,19 +253,3 @@ let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [
\ 'build',
\ 'bin',
\])
" Given a buffer number, find the build subdirectory with compile commands
" The subdirectory is returned without the trailing /
function! ale#c#FindCompileCommands(buffer) abort
for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
for l:dirname in ale#Var(a:buffer, 'c_build_dir_names')
let l:c_build_dir = l:path . s:sep . l:dirname
if filereadable(l:c_build_dir . '/compile_commands.json')
return l:c_build_dir
endif
endfor
endfor
return ''
endfunction

View File

@ -113,7 +113,7 @@ function! ale#completion#Filter(buffer, suggestions, prefix) abort
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
let l:word = type(l:item) is v:t_string ? l:item : l:item.word
" Add suggestions if the suggestion starts with a case-insensitive
" match for the prefix.
@ -133,7 +133,7 @@ function! ale#completion#Filter(buffer, suggestions, prefix) abort
" 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',
\ 'index(l:excluded_words, type(v:val) is v:t_string ? v:val : v:val.word) < 0',
\)
endif
@ -214,8 +214,10 @@ function! ale#completion#Show(response, completion_parser) abort
" function, and then start omni-completion.
let b:ale_completion_response = a:response
let b:ale_completion_parser = a:completion_parser
" Replace completion options shortly before opening the menu.
call s:ReplaceCompletionOptions()
call ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")
call timer_start(0, {-> ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")})
endfunction
function! s:CompletionStillValid(request_id) abort
@ -315,10 +317,10 @@ function! ale#completion#ParseLSPCompletions(response) abort
let l:item_list = []
if type(get(a:response, 'result')) is type([])
if type(get(a:response, 'result')) is v:t_list
let l:item_list = a:response.result
elseif type(get(a:response, 'result')) is type({})
\&& type(get(a:response.result, 'items')) is type([])
elseif type(get(a:response, 'result')) is v:t_dict
\&& type(get(a:response.result, 'items')) is v:t_list
let l:item_list = a:response.result.items
endif
@ -336,7 +338,9 @@ function! ale#completion#ParseLSPCompletions(response) abort
endif
" See :help complete-items for Vim completion kinds
if l:item.kind is s:LSP_COMPLETION_METHOD_KIND
if !has_key(l:item, 'kind')
let l:kind = 'v'
elseif l:item.kind is s:LSP_COMPLETION_METHOD_KIND
let l:kind = 'm'
elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND
let l:kind = 'm'
@ -422,54 +426,65 @@ endfunction
function! s:GetLSPCompletions(linter) abort
let l:buffer = bufnr('')
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#completion#HandleTSServerResponse')
\ : function('ale#completion#HandleLSPResponse')
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Completions(
\ l:buffer,
\ b:ale_completion_info.line,
\ b:ale_completion_info.column,
\ b:ale_completion_info.prefix,
\)
else
" Send a message saying the buffer has changed first, otherwise
" completions won't know what text is nearby.
call ale#lsp#NotifyForChanges(l:lsp_details)
" For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement
" this correctly.
let l:message = ale#lsp#message#Completion(
\ l:buffer,
\ b:ale_completion_info.line,
\ min([
\ b:ale_completion_info.line_length,
\ b:ale_completion_info.column,
\ ]),
\ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix),
\)
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
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
function! OnReady(...) abort closure
" If we have sent a completion request already, don't send another.
if b:ale_completion_info.request_id
return
endif
endif
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#completion#HandleTSServerResponse')
\ : function('ale#completion#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Completions(
\ l:buffer,
\ b:ale_completion_info.line,
\ b:ale_completion_info.column,
\ b:ale_completion_info.prefix,
\)
else
" Send a message saying the buffer has changed first, otherwise
" completions won't know what text is nearby.
call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer)
" For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement
" this correctly.
let l:message = ale#lsp#message#Completion(
\ l:buffer,
\ b:ale_completion_info.line,
\ min([
\ b:ale_completion_info.line_length,
\ b:ale_completion_info.column,
\ ]),
\ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix),
\)
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
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
call ale#lsp#WaitForCapability(l:id, l:root, 'completion', function('OnReady'))
endfunction
function! ale#completion#GetCompletions() abort

View File

@ -40,9 +40,9 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort
" The result can be a Dictionary item, a List of the same, or null.
let l:result = get(a:response, 'result', v:null)
if type(l:result) is type({})
if type(l:result) is v:t_dict
let l:result = [l:result]
elseif type(l:result) isnot type([])
elseif type(l:result) isnot v:t_list
let l:result = []
endif
@ -60,43 +60,50 @@ endfunction
function! s:GoToLSPDefinition(linter, options) abort
let l:buffer = bufnr('')
let [l:line, l:column] = getcurpos()[1:2]
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#definition#HandleTSServerResponse')
\ : function('ale#definition#HandleLSPResponse')
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if a:linter.lsp isnot# 'tsserver'
let l:column = min([l:column, len(getline(l:line))])
endif
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Definition(
\ l:buffer,
\ l:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" definition position probably won't make sense.
call ale#lsp#NotifyForChanges(l:lsp_details)
function! OnReady(...) abort closure
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#definition#HandleTSServerResponse')
\ : function('ale#definition#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
let l:column = min([l:column, len(getline(l:line))])
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Definition(
\ l:buffer,
\ l:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" definition position probably won't make sense.
call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer)
" For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement
" this correctly.
let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column)
endif
" For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement
" this correctly.
let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column)
endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:go_to_definition_map[l:request_id] = {
\ 'open_in_tab': get(a:options, 'open_in_tab', 0),
\}
let s:go_to_definition_map[l:request_id] = {
\ 'open_in_tab': get(a:options, 'open_in_tab', 0),
\}
endfunction
call ale#lsp#WaitForCapability(l:id, l:root, 'definition', function('OnReady'))
endfunction
function! ale#definition#GoTo(options) abort

View File

@ -557,7 +557,7 @@ function! s:RunJob(options) abort
if get(g:, 'ale_run_synchronously') == 1
" Run a command synchronously if this test option is set.
let s:job_info_map[l:job_id].output = systemlist(
\ type(l:command) == type([])
\ type(l:command) is v:t_list
\ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
\ : l:command
\)

View File

@ -4,11 +4,11 @@
" Given a filetype and a configuration for ignoring linters, return a List of
" Strings for linter names to ignore.
function! ale#engine#ignore#GetList(filetype, config) abort
if type(a:config) is type([])
if type(a:config) is v:t_list
return a:config
endif
if type(a:config) is type({})
if type(a:config) is v:t_dict
let l:names_to_remove = []
for l:part in split(a:filetype , '\.')

View File

@ -275,7 +275,7 @@ function! s:RunJob(options) abort
if get(g:, 'ale_run_synchronously') == 1
" Run a command synchronously if this test option is set.
let l:output = systemlist(
\ type(l:command) == type([])
\ type(l:command) is v:t_list
\ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2])
\ : l:command
\)
@ -313,10 +313,10 @@ function! s:RunFixer(options) abort
\ : call(l:Function, [l:buffer, copy(l:input)])
endif
if type(l:result) == type(0) && l:result == 0
if type(l:result) is v:t_number && l:result == 0
" When `0` is returned, skip this item.
let l:index += 1
elseif type(l:result) == type([])
elseif type(l:result) is v:t_list
let l:input = l:result
let l:index += 1
else
@ -351,9 +351,9 @@ function! s:RunFixer(options) abort
endfunction
function! s:AddSubCallbacks(full_list, callbacks) abort
if type(a:callbacks) == type('')
if type(a:callbacks) is v:t_string
call add(a:full_list, a:callbacks)
elseif type(a:callbacks) == type([])
elseif type(a:callbacks) is v:t_list
call extend(a:full_list, a:callbacks)
else
return 0
@ -365,7 +365,7 @@ endfunction
function! s:GetCallbacks(buffer, fixers) abort
if len(a:fixers)
let l:callback_list = a:fixers
elseif type(get(b:, 'ale_fixers')) is type([])
elseif type(get(b:, 'ale_fixers')) is v:t_list
" Lists can be used for buffer-local variables only
let l:callback_list = b:ale_fixers
else
@ -396,7 +396,7 @@ function! s:GetCallbacks(buffer, fixers) abort
" Variables with capital characters are needed, or Vim will complain about
" funcref variables.
for l:Item in l:callback_list
if type(l:Item) == type('')
if type(l:Item) is v:t_string
let l:Func = ale#fix#registry#GetFunc(l:Item)
if !empty(l:Func)
@ -420,9 +420,7 @@ function! ale#fix#InitBufferData(buffer, fixing_flag) abort
" The 'done' flag tells the function for applying changes when fixing
" is complete.
let g:ale_fix_buffer_data[a:buffer] = {
\ 'vars': getbufvar(a:buffer, ''),
\ 'lines_before': getbufline(a:buffer, 1, '$'),
\ 'filename': expand('#' . a:buffer . ':p'),
\ 'done': 0,
\ 'should_save': a:fixing_flag is# 'save_file',
\ 'temporary_directory_list': [],

View File

@ -245,32 +245,32 @@ function! ale#fix#registry#Add(name, func, filetypes, desc, ...) abort
" This command will throw from the sandbox.
let &equalprg=&equalprg
if type(a:name) != type('')
if type(a:name) isnot v:t_string
throw '''name'' must be a String'
endif
if type(a:func) != type('')
if type(a:func) isnot v:t_string
throw '''func'' must be a String'
endif
if type(a:filetypes) != type([])
if type(a:filetypes) isnot v:t_list
throw '''filetypes'' must be a List'
endif
for l:type in a:filetypes
if type(l:type) != type('')
if type(l:type) isnot v:t_string
throw 'Each entry of ''filetypes'' must be a String'
endif
endfor
if type(a:desc) != type('')
if type(a:desc) isnot v:t_string
throw '''desc'' must be a String'
endif
let l:aliases = get(a:000, 0, [])
if type(l:aliases) != type([])
\|| !empty(filter(copy(l:aliases), 'type(v:val) != type('''')'))
if type(l:aliases) isnot v:t_list
\|| !empty(filter(copy(l:aliases), 'type(v:val) isnot v:t_string'))
throw '''aliases'' must be a List of String values'
endif

View File

@ -5,6 +5,13 @@ scriptencoding utf-8
let s:pragma_error = '#pragma once in main file'
" Look for lines like the following.
"
" <stdin>:8:5: warning: conversion lacks type at end of format [-Wformat=]
" <stdin>:10:27: error: invalid operands to binary - (have int and char *)
" -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004]
let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$'
function! s:IsHeaderFile(filename) abort
return a:filename =~? '\v\.(h|hpp)$'
endfunction
@ -18,16 +25,63 @@ function! s:RemoveUnicodeQuotes(text) abort
return l:text
endfunction
" Report problems inside of header files just for gcc and clang
function! s:ParseProblemsInHeaders(buffer, lines) abort
let l:output = []
let l:include_item = {}
for l:line in a:lines[: -2]
let l:include_match = matchlist(l:line, '\v^In file included from')
if !empty(l:include_item)
let l:pattern_match = matchlist(l:line, s:pattern)
if !empty(l:pattern_match) && l:pattern_match[1] is# '<stdin>'
if has_key(l:include_item, 'lnum')
call add(l:output, l:include_item)
endif
let l:include_item = {}
continue
endif
let l:include_item.detail .= "\n" . l:line
endif
if !empty(l:include_match)
if empty(l:include_item)
let l:include_item = {
\ 'text': 'Error found in header. See :ALEDetail',
\ 'detail': l:line,
\}
endif
endif
if !empty(l:include_item)
let l:stdin_match = matchlist(l:line, '\vfrom \<stdin\>:(\d+):(\d*):?$')
if !empty(l:stdin_match)
let l:include_item.lnum = str2nr(l:stdin_match[1])
if str2nr(l:stdin_match[2])
let l:include_item.col = str2nr(l:stdin_match[2])
endif
endif
endif
endfor
if !empty(l:include_item) && has_key(l:include_item, 'lnum')
call add(l:output, l:include_item)
endif
return l:output
endfunction
function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
" Look for lines like the following.
"
" <stdin>:8:5: warning: conversion lacks type at end of format [-Wformat=]
" <stdin>:10:27: error: invalid operands to binary - (have int and char *)
" -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004]
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
for l:match in ale#util#GetMatches(a:lines, s:pattern)
" Filter out the pragma errors
if s:IsHeaderFile(bufname(bufnr('')))
\&& l:match[5][:len(s:pragma_error) - 1] is# s:pragma_error
@ -67,3 +121,12 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
return l:output
endfunction
" Handle problems with the GCC format, but report problems inside of headers.
function! ale#handlers#gcc#HandleGCCFormatWithIncludes(buffer, lines) abort
let l:output = ale#handlers#gcc#HandleGCCFormat(a:buffer, a:lines)
call extend(l:output, s:ParseProblemsInHeaders(a:buffer, a:lines))
return l:output
endfunction

View File

@ -32,7 +32,7 @@ function! ale#handlers#rust#HandleRustErrors(buffer, lines) abort
let l:error = json_decode(l:errorline)
if has_key(l:error, 'message') && type(l:error.message) == type({})
if has_key(l:error, 'message') && type(l:error.message) is v:t_dict
let l:error = l:error.message
endif

View File

@ -59,3 +59,14 @@ function! ale#handlers#writegood#Handle(buffer, lines) abort
return l:output
endfunction
" Define the writegood linter for a given filetype.
function! ale#handlers#writegood#DefineLinter(filetype) abort
call ale#linter#Define(a:filetype, {
\ 'name': 'writegood',
\ 'aliases': ['write-good'],
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})
endfunction

View File

@ -63,19 +63,19 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
let l:result = l:result.contents
if type(l:result) is type('')
if type(l:result) is v:t_string
" The result can be just a string.
let l:result = [l:result]
endif
if type(l:result) is type({})
if type(l:result) is v:t_dict
" If the result is an object, then it's markup content.
let l:result = [l:result.value]
endif
if type(l:result) is type([])
if type(l:result) is v:t_list
" Replace objects with text values.
call map(l:result, 'type(v:val) is type('''') ? v:val : v:val.value')
call map(l:result, 'type(v:val) is v:t_string ? v:val : v:val.value')
let l:str = join(l:result, "\n")
let l:str = substitute(l:str, '^\s*\(.\{-}\)\s*$', '\1', '')
@ -93,45 +93,51 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
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#lsp_linter#StartLSP(a:buffer, a:linter, l:Callback)
let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
let l:language_id = l:lsp_details.language_id
if a:linter.lsp is# 'tsserver'
let l:column = a:column
function! OnReady(...) abort closure
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
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#NotifyForChanges(l:lsp_details)
if a:linter.lsp is# 'tsserver'
let l:column = a:column
let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])])
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#NotifyForChanges(l:id, l:root, a:buffer)
let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column)
endif
let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])])
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column)
endif
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),
\}
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_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
call ale#lsp#WaitForCapability(l:id, l:root, 'hover', function('OnReady'))
endfunction
" Obtain Hover information for the specified position

View File

@ -57,11 +57,11 @@ function! ale#linter#GetLintersLoaded() abort
endfunction
function! s:IsCallback(value) abort
return type(a:value) == type('') || type(a:value) == type(function('type'))
return type(a:value) is v:t_string || type(a:value) is v:t_func
endfunction
function! s:IsBoolean(value) abort
return type(a:value) == type(0) && (a:value == 0 || a:value == 1)
return type(a:value) is v:t_number && (a:value == 0 || a:value == 1)
endfunction
function! s:LanguageGetter(buffer) dict abort
@ -69,7 +69,7 @@ function! s:LanguageGetter(buffer) dict abort
endfunction
function! ale#linter#PreProcess(filetype, linter) abort
if type(a:linter) != type({})
if type(a:linter) isnot v:t_dict
throw 'The linter object must be a Dictionary'
endif
@ -79,7 +79,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
\ 'lsp': get(a:linter, 'lsp', ''),
\}
if type(l:obj.name) != type('')
if type(l:obj.name) isnot v:t_string
throw '`name` must be defined to name the linter'
endif
@ -114,7 +114,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
elseif has_key(a:linter, 'executable')
let l:obj.executable = a:linter.executable
if type(l:obj.executable) != type('')
if type(l:obj.executable) isnot v:t_string
throw '`executable` must be a string if defined'
endif
else
@ -130,7 +130,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
elseif has_key(a:linter, 'command_chain')
let l:obj.command_chain = a:linter.command_chain
if type(l:obj.command_chain) != type([])
if type(l:obj.command_chain) isnot v:t_list
throw '`command_chain` must be a List'
endif
@ -148,7 +148,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
if has_key(l:link, 'output_stream')
if type(l:link.output_stream) != type('')
if type(l:link.output_stream) isnot v:t_string
\|| index(['stdout', 'stderr', 'both'], l:link.output_stream) < 0
throw l:err_prefix . '`output_stream` flag must be '
\ . "'stdout', 'stderr', or 'both'"
@ -170,7 +170,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
elseif has_key(a:linter, 'command')
let l:obj.command = a:linter.command
if type(l:obj.command) != type('')
if type(l:obj.command) isnot v:t_string
throw '`command` must be a string if defined'
endif
else
@ -217,7 +217,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
" Default to using the filetype as the language.
let l:obj.language = get(a:linter, 'language', a:filetype)
if type(l:obj.language) != type('')
if type(l:obj.language) isnot v:t_string
throw '`language` must be a string'
endif
@ -257,7 +257,7 @@ function! ale#linter#PreProcess(filetype, linter) abort
let l:obj.output_stream = get(a:linter, 'output_stream', 'stdout')
if type(l:obj.output_stream) != type('')
if type(l:obj.output_stream) isnot v:t_string
\|| index(['stdout', 'stderr', 'both'], l:obj.output_stream) < 0
throw "`output_stream` must be 'stdout', 'stderr', or 'both'"
endif
@ -283,8 +283,8 @@ function! ale#linter#PreProcess(filetype, linter) abort
let l:obj.aliases = get(a:linter, 'aliases', [])
if type(l:obj.aliases) != type([])
\|| len(filter(copy(l:obj.aliases), 'type(v:val) != type('''')')) > 0
if type(l:obj.aliases) isnot v:t_list
\|| len(filter(copy(l:obj.aliases), 'type(v:val) isnot v:t_string')) > 0
throw '`aliases` must be a List of String values'
endif
@ -336,7 +336,7 @@ function! s:GetAliasedFiletype(original_filetype) abort
let l:buffer_aliases = get(b:, 'ale_linter_aliases', {})
" b:ale_linter_aliases can be set to a List.
if type(l:buffer_aliases) is type([])
if type(l:buffer_aliases) is v:t_list
return l:buffer_aliases
endif
@ -360,7 +360,7 @@ endfunction
function! ale#linter#ResolveFiletype(original_filetype) abort
let l:filetype = s:GetAliasedFiletype(a:original_filetype)
if type(l:filetype) != type([])
if type(l:filetype) isnot v:t_list
return [l:filetype]
endif
@ -376,7 +376,7 @@ function! s:GetLinterNames(original_filetype) abort
endif
" b:ale_linters can be set to a List.
if type(l:buffer_ale_linters) is type([])
if type(l:buffer_ale_linters) is v:t_list
return l:buffer_ale_linters
endif
@ -414,9 +414,9 @@ function! ale#linter#Get(original_filetypes) abort
let l:all_linters = ale#linter#GetAll(l:filetype)
let l:filetype_linters = []
if type(l:linter_names) == type('') && l:linter_names is# 'all'
if type(l:linter_names) is v:t_string && l:linter_names is# 'all'
let l:filetype_linters = l:all_linters
elseif type(l:linter_names) == type([])
elseif type(l:linter_names) is v:t_list
" Select only the linters we or the user has specified.
for l:linter in l:all_linters
let l:name_list = [l:linter.name] + l:linter.aliases

View File

@ -66,6 +66,7 @@ function! ale#loclist_jumping#Jump(direction, wrap) abort
let l:nearest = ale#loclist_jumping#FindNearest(a:direction, a:wrap)
if !empty(l:nearest)
normal! m`
call cursor(l:nearest)
endif
endfunction
@ -82,6 +83,7 @@ function! ale#loclist_jumping#JumpToIndex(index) abort
let l:item = l:loclist[a:index]
if !empty(l:item)
normal! m`
call cursor([l:item.lnum, l:item.col])
endif
endfunction

View File

@ -15,13 +15,23 @@ function! ale#lsp#NewConnection(initialization_options) abort
" open_documents: A Dictionary mapping buffers to b:changedtick, keeping
" track of when documents were opened, and when we last changed them.
" callback_list: A list of callbacks for handling LSP responses.
" initialization_options: Options to send to the server.
" capabilities: Features the server supports.
let l:conn = {
\ 'is_tsserver': 0,
\ 'id': '',
\ 'data': '',
\ 'projects': {},
\ 'open_documents': {},
\ 'callback_list': [],
\ 'initialization_options': a:initialization_options,
\ 'capabilities': {
\ 'hover': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ },
\}
call add(s:connections, l:conn)
@ -44,6 +54,11 @@ function! s:FindConnection(key, value) abort
return {}
endfunction
" Get the capabilities for a connection, or an empty Dictionary.
function! ale#lsp#GetConnectionCapabilities(id) abort
return get(s:FindConnection('id', a:id), 'capabilities', {})
endfunction
function! ale#lsp#GetNextMessageID() abort
" Use the current ID
let l:id = g:ale_lsp_next_message_id
@ -174,6 +189,16 @@ function! s:MarkProjectAsInitialized(conn, project) abort
" Remove the messages now.
let a:conn.message_queue = []
" Call capabilities callbacks queued for the project.
for [l:capability, l:Callback] in a:project.capabilities_queue
if a:conn.is_tsserver || a:conn.capabilities[l:capability]
call call(l:Callback, [a:conn.id, a:project.root])
endif
endfor
" Clear the queued callbacks now.
let a:project.capabilities_queue = []
endfunction
function! s:HandleInitializeResponse(conn, response) abort
@ -185,6 +210,38 @@ function! s:HandleInitializeResponse(conn, response) abort
endif
endfunction
" Update capabilities from the server, so we know which features the server
" supports.
function! s:UpdateCapabilities(conn, capabilities) abort
if type(a:capabilities) isnot v:t_dict
return
endif
if get(a:capabilities, 'hoverProvider') is v:true
let a:conn.capabilities.hover = 1
endif
if get(a:capabilities, 'referencesProvider') is v:true
let a:conn.capabilities.references = 1
endif
if !empty(get(a:capabilities, 'completionProvider'))
let a:conn.capabilities.completion = 1
endif
if type(get(a:capabilities, 'completionProvider')) is v:t_dict
let l:chars = get(a:capabilities.completionProvider, 'triggerCharacters')
if type(l:chars) is v:t_list
let a:conn.capabilities.completion_trigger_characters = l:chars
endif
endif
if get(a:capabilities, 'definitionProvider') is v:true
let a:conn.capabilities.definition = 1
endif
endfunction
function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
let l:uninitialized_projects = []
@ -200,6 +257,8 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
if get(a:response, 'method', '') is# ''
if has_key(get(a:response, 'result', {}), 'capabilities')
call s:UpdateCapabilities(a:conn, a:response.result.capabilities)
for [l:dir, l:project] in l:uninitialized_projects
call s:MarkProjectAsInitialized(a:conn, l:project)
endfor
@ -216,7 +275,7 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort
endfunction
function! ale#lsp#HandleMessage(conn, message) abort
if type(a:message) != type('')
if type(a:message) isnot v:t_string
" Ignore messages that aren't strings.
return
endif
@ -254,22 +313,43 @@ function! s:HandleCommandMessage(job_id, message) abort
call ale#lsp#HandleMessage(l:conn, a:message)
endfunction
function! ale#lsp#RegisterProject(conn, project_root) abort
" Given a connection ID, mark it as a tsserver connection, so it will be
" handled that way.
function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
let l:conn = s:FindConnection('id', a:conn_id)
if !empty(l:conn)
let l:conn.is_tsserver = 1
endif
endfunction
" Register a project for an LSP connection.
"
" This function will throw if the connection doesn't exist.
function! ale#lsp#RegisterProject(conn_id, project_root) abort
let l:conn = s:FindConnection('id', a:conn_id)
" Empty strings can't be used for Dictionary keys in NeoVim, due to E713.
" This appears to be a nonsensical bug in NeoVim.
let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root
if !has_key(a:conn.projects, l:key)
if !has_key(l:conn.projects, l:key)
" Tools without project roots are ready right away, like tsserver.
let a:conn.projects[l:key] = {
let l:conn.projects[l:key] = {
\ 'root': a:project_root,
\ 'initialized': empty(a:project_root),
\ 'init_request_id': 0,
\ 'message_queue': [],
\ 'capabilities_queue': [],
\}
endif
endfunction
function! ale#lsp#GetProject(conn, project_root) abort
if empty(a:conn)
return {}
endif
let l:key = empty(a:project_root) ? '<<EMPTY>>' : a:project_root
return get(a:conn.projects, l:key, {})
@ -279,7 +359,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, initialization_options) abort
function! ale#lsp#StartProgram(executable, command, init_options) abort
if !executable(a:executable)
return 0
endif
@ -287,7 +367,7 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init
let l:conn = s:FindConnection('executable', a:executable)
" Get the current connection or a new one.
let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options)
let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options)
let l:conn.executable = a:executable
if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id)
@ -305,18 +385,15 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init
endif
let l:conn.id = l:job_id
" Add the callback to the List if it's not there already.
call uniq(sort(add(l:conn.callback_list, a:callback)))
call ale#lsp#RegisterProject(l:conn, a:project_root)
return l:job_id
endfunction
" Connect to an address and set up a callback for handling responses.
function! ale#lsp#ConnectToAddress(address, project_root, callback, initialization_options) abort
function! ale#lsp#ConnectToAddress(address, init_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 : ale#lsp#NewConnection(a:initialization_options)
let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options)
if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id)
let l:conn.channel_id = ale#socket#Open(a:address, {
@ -329,13 +406,21 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback, initializati
endif
let l:conn.id = a:address
" Add the callback to the List if it's not there already.
call uniq(sort(add(l:conn.callback_list, a:callback)))
call ale#lsp#RegisterProject(l:conn, a:project_root)
return a:address
endfunction
" Given a connection ID and a callback, register that callback for handling
" messages if the connection exists.
function! ale#lsp#RegisterCallback(conn_id, callback) abort
let l:conn = s:FindConnection('id', a:conn_id)
if !empty(l:conn)
" Add the callback to the List if it's not there already.
call uniq(sort(add(l:conn.callback_list, a:callback)))
endif
endfunction
" Stop all LSP connections, closing all jobs and channels, and removing any
" queued messages.
function! ale#lsp#StopAll() abort
@ -373,11 +458,6 @@ function! ale#lsp#Send(conn_id, message, ...) abort
let l:project_root = get(a:000, 0, '')
let l:conn = s:FindConnection('id', a:conn_id)
if empty(l:conn)
return 0
endif
let l:project = ale#lsp#GetProject(l:conn, l:project_root)
if empty(l:project)
@ -411,45 +491,22 @@ function! ale#lsp#Send(conn_id, message, ...) abort
return l:id == 0 ? -1 : l:id
endfunction
" The Document details Dictionary should contain the following keys.
"
" buffer - The buffer number for the document.
" connection_id - The connection ID for the LSP server.
" command - The command to run to start the LSP connection.
" project_root - The project root for the LSP project.
" language_id - The language ID for the project, like 'python', 'rust', etc.
" Create a new Dictionary containing more connection details, with the
" following information added:
"
" conn - An existing LSP connection for the document.
" document_open - 1 if the document is currently open, 0 otherwise.
function! s:ExtendDocumentDetails(details) abort
let l:extended = copy(a:details)
let l:conn = s:FindConnection('id', a:details.connection_id)
let l:extended.conn = l:conn
let l:extended.document_open = !empty(l:conn)
\ && has_key(l:conn.open_documents, a:details.buffer)
return l:extended
endfunction
" Notify LSP servers or tsserver if a document is opened, if needed.
" If a document is opened, 1 will be returned, otherwise 0 will be returned.
function! ale#lsp#OpenDocument(basic_details) abort
let l:d = s:ExtendDocumentDetails(a:basic_details)
function! ale#lsp#OpenDocument(conn_id, project_root, buffer, language_id) abort
let l:conn = s:FindConnection('id', a:conn_id)
let l:opened = 0
if !empty(l:d.conn) && !l:d.document_open
if empty(l:d.language_id)
let l:message = ale#lsp#tsserver_message#Open(l:d.buffer)
" FIXME: Return 1 if the document is already open?
if !empty(l:conn) && !has_key(l:conn.open_documents, a:buffer)
if l:conn.is_tsserver
let l:message = ale#lsp#tsserver_message#Open(a:buffer)
else
let l:message = ale#lsp#message#DidOpen(l:d.buffer, l:d.language_id)
let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id)
endif
call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root)
let l:d.conn.open_documents[l:d.buffer] = getbufvar(l:d.buffer, 'changedtick')
call ale#lsp#Send(a:conn_id, l:message, a:project_root)
let l:conn.open_documents[a:buffer] = getbufvar(a:buffer, 'changedtick')
let l:opened = 1
endif
@ -458,25 +515,50 @@ endfunction
" Notify LSP servers or tsserver that a document has changed, if needed.
" If a notification is sent, 1 will be returned, otherwise 0 will be returned.
function! ale#lsp#NotifyForChanges(basic_details) abort
let l:d = s:ExtendDocumentDetails(a:basic_details)
function! ale#lsp#NotifyForChanges(conn_id, project_root, buffer) abort
let l:conn = s:FindConnection('id', a:conn_id)
let l:notified = 0
if l:d.document_open
let l:new_tick = getbufvar(l:d.buffer, 'changedtick')
if !empty(l:conn) && has_key(l:conn.open_documents, a:buffer)
let l:new_tick = getbufvar(a:buffer, 'changedtick')
if l:d.conn.open_documents[l:d.buffer] < l:new_tick
if empty(l:d.language_id)
let l:message = ale#lsp#tsserver_message#Change(l:d.buffer)
if l:conn.open_documents[a:buffer] < l:new_tick
if l:conn.is_tsserver
let l:message = ale#lsp#tsserver_message#Change(a:buffer)
else
let l:message = ale#lsp#message#DidChange(l:d.buffer)
let l:message = ale#lsp#message#DidChange(a:buffer)
endif
call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root)
let l:d.conn.open_documents[l:d.buffer] = l:new_tick
call ale#lsp#Send(a:conn_id, l:message, a:project_root)
let l:conn.open_documents[a:buffer] = l:new_tick
let l:notified = 1
endif
endif
return l:notified
endfunction
" Given some LSP details that must contain at least `connection_id` and
" `project_root` keys,
function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort
let l:conn = s:FindConnection('id', a:conn_id)
let l:project = ale#lsp#GetProject(l:conn, a:project_root)
if empty(l:project)
return 0
endif
if type(get(l:conn.capabilities, a:capability, v:null)) isnot v:t_number
throw 'Invalid capability ' . a:capability
endif
if l:project.initialized
if l:conn.is_tsserver || l:conn.capabilities[a:capability]
" The project has been initialized, so call the callback now.
call call(a:callback, [a:conn_id, a:project_root])
endif
else
" Call the callback later, once we have the information we need.
call add(l:project.capabilities_queue, [a:capability, a:callback])
endif
endfunction

View File

@ -88,7 +88,7 @@ function! ale#lsp#response#ReadTSServerDiagnostics(response) abort
endfunction
function! ale#lsp#response#GetErrorMessage(response) abort
if type(get(a:response, 'error', 0)) isnot type({})
if type(get(a:response, 'error', 0)) isnot v:t_dict
return ''
endif
@ -108,12 +108,12 @@ function! ale#lsp#response#GetErrorMessage(response) abort
" Include the traceback or error data as details, if present.
let l:error_data = get(a:response.error, 'data', {})
if type(l:error_data) is type('')
if type(l:error_data) is v:t_string
let l:message .= "\n" . l:error_data
else
let l:traceback = get(l:error_data, 'traceback', [])
if type(l:traceback) is type([]) && !empty(l:traceback)
if type(l:traceback) is v:t_list && !empty(l:traceback)
let l:message .= "\n" . join(l:traceback, "\n")
endif
endif

View File

@ -126,10 +126,9 @@ function! ale#lsp_linter#GetOptions(buffer, linter) abort
return l:initialization_options
endfunction
" Given a buffer, an LSP linter, and a callback to register for handling
" messages, start up an LSP linter and get ready to receive errors or
" completions.
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
" Given a buffer, an LSP linter, start up an LSP linter and get ready to
" receive messages for the document.
function! ale#lsp_linter#StartLSP(buffer, linter) abort
let l:command = ''
let l:address = ''
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
@ -140,16 +139,11 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
return {}
endif
let l:initialization_options = ale#lsp_linter#GetOptions(a:buffer, a:linter)
let l:init_options = ale#lsp_linter#GetOptions(a:buffer, a:linter)
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,
\)
let l:conn_id = ale#lsp#ConnectToAddress(l:address, l:init_options)
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
@ -164,14 +158,10 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let l:conn_id = ale#lsp#StartProgram(
\ l:executable,
\ l:command,
\ l:root,
\ a:callback,
\ l:initialization_options,
\ l:init_options,
\)
endif
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
if empty(l:conn_id)
if g:ale_history_enabled && !empty(l:command)
call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command)
@ -180,6 +170,16 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
return {}
endif
" tsserver behaves differently, so tell the LSP API that it is tsserver.
if a:linter.lsp is# 'tsserver'
call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
endif
" Register the project now the connection is ready.
call ale#lsp#RegisterProject(l:conn_id, l:root)
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
let l:details = {
\ 'buffer': a:buffer,
\ 'connection_id': l:conn_id,
@ -188,7 +188,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
\ 'language_id': l:language_id,
\}
if ale#lsp#OpenDocument(l:details)
if ale#lsp#OpenDocument(l:conn_id, l:root, a:buffer, l:language_id)
if g:ale_history_enabled && !empty(l:command)
call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
endif
@ -196,7 +196,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
" The change message needs to be sent for tsserver before doing anything.
if a:linter.lsp is# 'tsserver'
call ale#lsp#NotifyForChanges(l:details)
call ale#lsp#NotifyForChanges(l:conn_id, l:root, a:buffer)
endif
return l:details
@ -204,11 +204,7 @@ endfunction
function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
let l:info = g:ale_buffer_info[a:buffer]
let l:lsp_details = ale#lsp_linter#StartLSP(
\ a:buffer,
\ a:linter,
\ function('ale#lsp_linter#HandleLSPResponse'),
\)
let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
if empty(l:lsp_details)
return 0
@ -217,25 +213,25 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
" Register a callback now for handling errors now.
let l:Callback = function('ale#lsp_linter#HandleLSPResponse')
call ale#lsp#RegisterCallback(l:id, l:Callback)
" Remember the linter this connection is for.
let s:lsp_linter_map[l:id] = a:linter.name
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Geterr(a:buffer)
let l:request_id = ale#lsp#Send(l:id, l:message, l:root)
let l:notified = l:request_id != 0
let l:notified = ale#lsp#Send(l:id, l:message, l:root) != 0
else
let l:notified = ale#lsp#NotifyForChanges(l:lsp_details)
let l:notified = ale#lsp#NotifyForChanges(l:id, l:root, a:buffer)
endif
" If this was a file save event, also notify the server of that.
if a:linter.lsp isnot# 'tsserver'
\&& getbufvar(a:buffer, 'ale_save_event_fired', 0)
let l:save_message = ale#lsp#message#DidSave(a:buffer)
let l:request_id = ale#lsp#Send(l:id, l:save_message, l:root)
let l:notified = l:request_id != 0
let l:notified = ale#lsp#Send(l:id, l:save_message, l:root) != 0
endif
if l:notified

View File

@ -105,6 +105,21 @@ function! ale#path#GetAbsPath(base_directory, filename) abort
return ale#path#Simplify(a:base_directory . l:sep . a:filename)
endfunction
" Given a path, return the directory name for that path, with no trailing
" slashes. If the argument is empty(), return an empty string.
function! ale#path#Dirname(path) abort
if empty(a:path)
return ''
endif
" For /foo/bar/ we need :h:h to get /foo
if a:path[-1:] is# '/'
return fnamemodify(a:path, ':h:h')
endif
return fnamemodify(a:path, ':h')
endfunction
" Given a buffer number and a relative or absolute path, return 1 if the
" two paths represent the same file on disk.
function! ale#path#IsBufferPath(buffer, complex_filename) abort

View File

@ -68,37 +68,46 @@ 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')
if a:linter.lsp isnot# 'tsserver'
let l:column = min([l:column, len(getline(l:line))])
endif
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
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#NotifyForChanges(l:lsp_details)
function! OnReady(...) abort closure
let l:Callback = a:linter.lsp is# 'tsserver'
\ ? function('ale#references#HandleTSServerResponse')
\ : function('ale#references#HandleLSPResponse')
let l:column = min([l:column, len(getline(l:line))])
call ale#lsp#RegisterCallback(l:id, l:Callback)
let l:message = ale#lsp#message#References(l:buffer, l:line, l:column)
endif
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#NotifyForChanges(l:id, l:root, l:buffer)
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let l:message = ale#lsp#message#References(l:buffer, l:line, l:column)
endif
let s:references_map[l:request_id] = {}
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:references_map[l:request_id] = {}
endfunction
call ale#lsp#WaitForCapability(l:id, l:root, 'references', function('OnReady'))
endfunction
function! ale#references#Find() abort

View File

@ -211,7 +211,7 @@ function! s:BuildSignMap(buffer, current_sign_list, grouped_items) abort
if l:max_signs is 0
let l:selected_grouped_items = []
elseif type(l:max_signs) is type(0) && l:max_signs > 0
elseif type(l:max_signs) is v:t_number && l:max_signs > 0
let l:selected_grouped_items = a:grouped_items[:l:max_signs - 1]
else
let l:selected_grouped_items = a:grouped_items

View File

@ -52,3 +52,26 @@ function! ale#test#SetFilename(path) abort
silent! noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path))
endfunction
function! s:RemoveModule(results) abort
for l:item in a:results
if has_key(l:item, 'module')
call remove(l:item, 'module')
endif
endfor
endfunction
" Return loclist data without the module string, only in newer Vim versions.
function! ale#test#GetLoclistWithoutModule() abort
let l:results = getloclist(0)
call s:RemoveModule(l:results)
return l:results
endfunction
function! ale#test#GetQflistWithoutModule() abort
let l:results = getqflist()
call s:RemoveModule(l:results)
return l:results
endfunction

View File

@ -43,7 +43,7 @@ function! ale#toggle#Toggle() abort
call s:CleanupEveryBuffer()
call s:DisablePostamble()
if has('balloon_eval')
if exists('*ale#balloon#Disable')
call ale#balloon#Disable()
endif
endif

View File

@ -79,7 +79,7 @@ function! ale#util#GetLineCount(buffer) abort
endfunction
function! ale#util#GetFunction(string_or_ref) abort
if type(a:string_or_ref) == type('')
if type(a:string_or_ref) is v:t_string
return function(a:string_or_ref)
endif
@ -89,11 +89,11 @@ 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
elseif bufnr(a:filename) isnot bufnr('')
" Open another file only if we need to.
if bufnr(a:filename) isnot bufnr('')
call ale#util#Execute('edit ' . fnameescape(a:filename))
endif
call ale#util#Execute('edit ' . fnameescape(a:filename))
else
normal! m`
endif
call cursor(a:line, a:column)
@ -303,8 +303,8 @@ endfunction
" Only the first pattern which matches a line will be returned.
function! ale#util#GetMatches(lines, patterns) abort
let l:matches = []
let l:lines = type(a:lines) == type([]) ? a:lines : [a:lines]
let l:patterns = type(a:patterns) == type([]) ? a:patterns : [a:patterns]
let l:lines = type(a:lines) is v:t_list ? a:lines : [a:lines]
let l:patterns = type(a:patterns) is v:t_list ? a:patterns : [a:patterns]
for l:line in l:lines
for l:pattern in l:patterns
@ -382,7 +382,7 @@ function! ale#util#FuzzyJSONDecode(data, default) abort
return a:default
endif
let l:str = type(a:data) == type('') ? a:data : join(a:data, '')
let l:str = type(a:data) is v:t_string ? a:data : join(a:data, '')
try
let l:result = json_decode(l:str)