mirror of
1
0
Fork 0

Updated plugins

This commit is contained in:
Amir Salihefendic 2018-07-19 14:52:53 +02:00
parent 2f164fee9b
commit cc997dc3d0
99 changed files with 1572 additions and 1151 deletions

View File

@ -15,9 +15,9 @@ function! ale_linters#awk#gawk#GetCommand(buffer) abort
" note the --source 'BEGIN ...' is to prevent " note the --source 'BEGIN ...' is to prevent
" gawk from attempting to execute the body of the script " gawk from attempting to execute the body of the script
" it is linting. " it is linting.
return ale_linters#awk#gawk#GetExecutable(a:buffer) return ale#Escape(ale_linters#awk#gawk#GetExecutable(a:buffer))
\ . " --source 'BEGIN { exit } END { exit 1 }'" \ . " --source 'BEGIN { exit } END { exit 1 }'"
\ . ' ' . ale#Var(a:buffer, 'awk_gawk_options') \ . ale#Pad(ale#Var(a:buffer, 'awk_gawk_options'))
\ . ' ' . '-f %t --lint /dev/null' \ . ' ' . '-f %t --lint /dev/null'
endfunction endfunction

View File

@ -30,5 +30,4 @@ call ale#linter#Define('cpp', {
\ 'command_callback': 'ale_linters#cpp#cquery#GetCommand', \ 'command_callback': 'ale_linters#cpp#cquery#GetCommand',
\ 'project_root_callback': 'ale_linters#cpp#cquery#GetProjectRoot', \ 'project_root_callback': 'ale_linters#cpp#cquery#GetProjectRoot',
\ 'initialization_options_callback': 'ale_linters#cpp#cquery#GetInitializationOptions', \ 'initialization_options_callback': 'ale_linters#cpp#cquery#GetInitializationOptions',
\ 'language': 'cpp',
\}) \})

View File

@ -1,7 +1,11 @@
let g:ale_cs_mcs_options = get(g:, 'ale_cs_mcs_options', '') let g:ale_cs_mcs_options = get(g:, 'ale_cs_mcs_options', '')
function! ale_linters#cs#mcs#GetCommand(buffer) abort function! ale_linters#cs#mcs#GetCommand(buffer) abort
return 'mcs -unsafe --parse ' . ale#Var(a:buffer, 'cs_mcs_options') . ' %t' let l:options = ale#Var(a:buffer, 'cs_mcs_options')
return 'mcs -unsafe --parse'
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' %t'
endfunction endfunction
function! ale_linters#cs#mcs#Handle(buffer, lines) abort function! ale_linters#cs#mcs#Handle(buffer, lines) abort

View File

@ -29,16 +29,16 @@ function! ale_linters#cs#mcsc#GetCommand(buffer) abort
\ : '' \ : ''
" register temporary module target file with ale " register temporary module target file with ale
let l:out = tempname() " register temporary module target file with ALE.
call ale#engine#ManageFile(a:buffer, l:out) let l:out = ale#engine#CreateFile(a:buffer)
" The code is compiled as a module and the output is redirected to a " The code is compiled as a module and the output is redirected to a
" temporary file. " temporary file.
return ale#path#CdString(s:GetWorkingDirectory(a:buffer)) return ale#path#CdString(s:GetWorkingDirectory(a:buffer))
\ . 'mcs -unsafe' \ . 'mcs -unsafe'
\ . ' ' . ale#Var(a:buffer, 'cs_mcsc_options') \ . ale#Pad(ale#Var(a:buffer, 'cs_mcsc_options'))
\ . ' ' . l:lib_option \ . ale#Pad(l:lib_option)
\ . ' ' . l:r_option \ . ale#Pad(l:r_option)
\ . ' -out:' . l:out \ . ' -out:' . l:out
\ . ' -t:module' \ . ' -t:module'
\ . ' -recurse:' . ale#Escape('*.cs') \ . ' -recurse:' . ale#Escape('*.cs')

View File

@ -10,7 +10,7 @@ endfunction
function! ale_linters#cuda#nvcc#GetCommand(buffer) abort function! ale_linters#cuda#nvcc#GetCommand(buffer) abort
" Unused: use ale#util#nul_file " Unused: use ale#util#nul_file
" let l:output_file = tempname() . '.ii' " let l:output_file = ale#util#Tempname() . '.ii'
" call ale#engine#ManageFile(a:buffer, l:output_file) " call ale#engine#ManageFile(a:buffer, l:output_file)
return ale#Escape(ale_linters#cuda#nvcc#GetExecutable(a:buffer)) return ale#Escape(ale_linters#cuda#nvcc#GetExecutable(a:buffer))

View File

@ -20,6 +20,5 @@ call ale#linter#Define('dart', {
\ 'lsp': 'stdio', \ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#dart#language_server#GetExecutable', \ 'executable_callback': 'ale_linters#dart#language_server#GetExecutable',
\ 'command_callback': 'ale_linters#dart#language_server#GetExecutable', \ 'command_callback': 'ale_linters#dart#language_server#GetExecutable',
\ 'language': 'dart',
\ 'project_root_callback': 'ale_linters#dart#language_server#GetProjectRoot', \ 'project_root_callback': 'ale_linters#dart#language_server#GetProjectRoot',
\}) \})

View File

@ -128,14 +128,7 @@ function! ale_linters#elm#make#HandleElm018Line(line, output) abort
endfunction endfunction
function! ale_linters#elm#make#FileIsBuffer(path) abort function! ale_linters#elm#make#FileIsBuffer(path) abort
let l:is_windows = has('win32') return ale#path#IsTempName(a:path)
let l:temp_dir = l:is_windows ? $TMP : $TMPDIR
if has('win32')
return a:path[0:len(l:temp_dir) - 1] is? l:temp_dir
else
return a:path[0:len(l:temp_dir) - 1] is# l:temp_dir
endif
endfunction endfunction
function! ale_linters#elm#make#ParseMessage(message) abort function! ale_linters#elm#make#ParseMessage(message) abort

View File

@ -3,7 +3,7 @@
let g:ale_erlang_erlc_options = get(g:, 'ale_erlang_erlc_options', '') let g:ale_erlang_erlc_options = get(g:, 'ale_erlang_erlc_options', '')
function! ale_linters#erlang#erlc#GetCommand(buffer) abort function! ale_linters#erlang#erlc#GetCommand(buffer) abort
let l:output_file = tempname() let l:output_file = ale#util#Tempname()
call ale#engine#ManageFile(a:buffer, l:output_file) call ale#engine#ManageFile(a:buffer, l:output_file)
return 'erlc -o ' . ale#Escape(l:output_file) return 'erlc -o ' . ale#Escape(l:output_file)

View File

@ -1,11 +1,8 @@
" Author: RyanSquared <vandor2012@gmail.com> " Author: RyanSquared <vandor2012@gmail.com>
" Description: `fusion-lint` linter for FusionScript files " Description: `fusion-lint` linter for FusionScript files
let g:ale_fuse_fusionlint_executable = call ale#Set('fuse_fusionlint_executable', 'fusion-lint')
\ get(g:, 'ale_fuse_fusionlint_executable', 'fusion-lint') call ale#Set('fuse_fusionlint_options', '')
let g:ale_fuse_fusionlint_options =
\ get(g:, 'ale_fuse_fusionlint_options', '')
function! ale_linters#fuse#fusionlint#GetExecutable(buffer) abort function! ale_linters#fuse#fusionlint#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'fuse_fusionlint_executable') return ale#Var(a:buffer, 'fuse_fusionlint_executable')
@ -13,7 +10,7 @@ endfunction
function! ale_linters#fuse#fusionlint#GetCommand(buffer) abort function! ale_linters#fuse#fusionlint#GetCommand(buffer) abort
return ale#Escape(ale_linters#fuse#fusionlint#GetExecutable(a:buffer)) return ale#Escape(ale_linters#fuse#fusionlint#GetExecutable(a:buffer))
\ . ' ' . ale#Var(a:buffer, 'fuse_fusionlint_options') \ . ale#Pad(ale#Var(a:buffer, 'fuse_fusionlint_options'))
\ . ' --filename %s -i' \ . ' --filename %s -i'
endfunction endfunction

View File

@ -14,9 +14,9 @@ function! ale_linters#glsl#glslang#GetExecutable(buffer) abort
endfunction endfunction
function! ale_linters#glsl#glslang#GetCommand(buffer) abort function! ale_linters#glsl#glslang#GetCommand(buffer) abort
return ale_linters#glsl#glslang#GetExecutable(a:buffer) return ale#Escape(ale_linters#glsl#glslang#GetExecutable(a:buffer))
\ . ' ' . ale#Var(a:buffer, 'glsl_glslang_options') \ . ale#Pad(ale#Var(a:buffer, 'glsl_glslang_options'))
\ . ' ' . '-C %t' \ . ' -C %t'
endfunction endfunction
function! ale_linters#glsl#glslang#Handle(buffer, lines) abort function! ale_linters#glsl#glslang#Handle(buffer, lines) abort

View File

@ -29,6 +29,5 @@ call ale#linter#Define('glsl', {
\ 'lsp': 'stdio', \ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#glsl#glslls#GetExecutable', \ 'executable_callback': 'ale_linters#glsl#glslls#GetExecutable',
\ 'command_callback': 'ale_linters#glsl#glslls#GetCommand', \ 'command_callback': 'ale_linters#glsl#glslls#GetCommand',
\ 'language': 'glsl',
\ 'project_root_callback': 'ale_linters#glsl#glslls#GetProjectRoot', \ 'project_root_callback': 'ale_linters#glsl#glslls#GetProjectRoot',
\}) \})

View File

@ -3,10 +3,9 @@
function! ale_linters#go#gotype#GetCommand(buffer) abort function! ale_linters#go#gotype#GetCommand(buffer) abort
if expand('#' . a:buffer . ':p') =~# '_test\.go$' if expand('#' . a:buffer . ':p') =~# '_test\.go$'
return return ''
endif endif
return ale#path#BufferCdString(a:buffer) . ' gotype .' return ale#path#BufferCdString(a:buffer) . ' gotype .'
endfunction endfunction

View File

@ -84,10 +84,10 @@ function! ale_linters#java#javac#GetCommand(buffer, import_paths) abort
return ale#path#BufferCdString(a:buffer) return ale#path#BufferCdString(a:buffer)
\ . ale#Escape(l:executable) \ . ale#Escape(l:executable)
\ . ' -Xlint' \ . ' -Xlint'
\ . ' ' . l:cp_option \ . ale#Pad(l:cp_option)
\ . ' ' . l:sp_option \ . ale#Pad(l:sp_option)
\ . ' -d ' . ale#Escape(l:class_file_directory) \ . ' -d ' . ale#Escape(l:class_file_directory)
\ . ' ' . ale#Var(a:buffer, 'java_javac_options') \ . ale#Pad(ale#Var(a:buffer, 'java_javac_options'))
\ . ' %t' \ . ' %t'
endfunction endfunction

View File

@ -0,0 +1,38 @@
" Author: t_t <jamestthompson3@gmail.com>
" Description: Integrate ALE with flow-language-server.
call ale#Set('javascript_flow_ls_executable', 'flow')
call ale#Set('javascript_flow_ls_use_global',
\ get(g:, 'ale_use_global_executables', 0)
\)
function! ale_linters#javascript#flow_ls#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_flow_ls', [
\ 'node_modules/.bin/flow',
\])
endfunction
function! ale_linters#javascript#flow_ls#GetCommand(buffer) abort
let l:executable = ale_linters#javascript#flow_ls#GetExecutable(a:buffer)
return ale#Escape(l:executable) . ' lsp --from ale-lsp'
endfunction
function! ale_linters#javascript#flow_ls#FindProjectRoot(buffer) abort
let l:flow_config = ale#path#FindNearestFile(a:buffer, '.flowconfig')
if !empty(l:flow_config)
return fnamemodify(l:flow_config, ':h')
endif
return ''
endfunction
call ale#linter#Define('javascript', {
\ 'name': 'flow-language-server',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#javascript#flow_ls#GetExecutable',
\ 'command_callback': 'ale_linters#javascript#flow_ls#GetCommand',
\ 'project_root_callback': 'ale_linters#javascript#flow_ls#FindProjectRoot',
\ 'language': 'javascript',
\})

View File

@ -0,0 +1,38 @@
" Author: MTDL9 <https://github.com/MTDL9>
" Description: Support for the Kotlin language server https://github.com/fwcd/KotlinLanguageServer
call ale#Set('kotlin_languageserver_executable', 'kotlin-language-server')
function! ale_linters#kotlin#languageserver#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'kotlin_languageserver_executable')
endfunction
function! ale_linters#kotlin#languageserver#GetCommand(buffer) abort
let l:executable = ale_linters#kotlin#languageserver#GetExecutable(a:buffer)
return ale#Escape(l:executable)
endfunction
function! ale_linters#kotlin#languageserver#GetProjectRoot(buffer) abort
let l:gradle_root = ale#gradle#FindProjectRoot(a:buffer)
if !empty(l:gradle_root)
return l:gradle_root
endif
let l:maven_pom_file = ale#path#FindNearestFile(a:buffer, 'pom.xml')
if !empty(l:maven_pom_file)
return fnamemodify(l:maven_pom_file, ':h')
endif
return ''
endfunction
call ale#linter#Define('kotlin', {
\ 'name': 'languageserver',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#kotlin#languageserver#GetExecutable',
\ 'command_callback': 'ale_linters#kotlin#languageserver#GetCommand',
\ 'language': 'kotlin',
\ 'project_root_callback': 'ale_linters#kotlin#languageserver#GetProjectRoot',
\})

View File

@ -9,8 +9,7 @@ endfunction
function! ale_linters#llvm#llc#GetCommand(buffer) abort function! ale_linters#llvm#llc#GetCommand(buffer) abort
return ale#Escape(ale_linters#llvm#llc#GetExecutable(a:buffer)) return ale#Escape(ale_linters#llvm#llc#GetExecutable(a:buffer))
\ . ' -filetype=null -o=' \ . ' -filetype=null -o=' . g:ale#util#nul_file
\ . ale#Escape(g:ale#util#nul_file)
endfunction endfunction
function! ale_linters#llvm#llc#HandleErrors(buffer, lines) abort function! ale_linters#llvm#llc#HandleErrors(buffer, lines) abort

View File

@ -1,5 +1,23 @@
" Author rhysd https://rhysd.github.io/, Dirk Roorda (dirkroorda), Adrián González Rus (@adrigzr) " Author rhysd https://rhysd.github.io/, Dirk Roorda (dirkroorda), Adrián González Rus (@adrigzr)
" Description: remark-lint for Markdown files " Description: remark-lint for Markdown files
call ale#Set('markdown_remark_lint_executable', 'remark')
call ale#Set('markdown_remark_lint_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('markdown_remark_lint_options', '')
function! ale_linters#markdown#remark_lint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'markdown_remark_lint', [
\ 'node_modules/.bin/remark',
\])
endfunction
function! ale_linters#markdown#remark_lint#GetCommand(buffer) abort
let l:executable = ale_linters#markdown#remark_lint#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'markdown_remark_lint_options')
return ale#node#Executable(a:buffer, l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --no-stdout --no-color'
endfunction
function! ale_linters#markdown#remark_lint#Handle(buffer, lines) abort function! ale_linters#markdown#remark_lint#Handle(buffer, lines) abort
" matches: ' 1:4 warning Incorrect list-item indent: add 1 space list-item-indent remark-lint' " matches: ' 1:4 warning Incorrect list-item indent: add 1 space list-item-indent remark-lint'
@ -26,9 +44,8 @@ endfunction
call ale#linter#Define('markdown', { call ale#linter#Define('markdown', {
\ 'name': 'remark-lint', \ 'name': 'remark-lint',
\ 'executable': 'remark', \ 'executable_callback': 'ale_linters#markdown#remark_lint#GetExecutable',
\ 'command': 'remark --no-stdout --no-color %s', \ 'command_callback': 'ale_linters#markdown#remark_lint#GetCommand',
\ 'callback': 'ale_linters#markdown#remark_lint#Handle', \ 'callback': 'ale_linters#markdown#remark_lint#Handle',
\ 'lint_file': 1,
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\}) \})

View File

@ -42,9 +42,9 @@ endfunction
call ale#linter#Define('nasm', { call ale#linter#Define('nasm', {
\ 'name': 'nasm', \ 'name': 'nasm',
\ 'executable': 'nasm',
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'lint_file': 1, \ 'lint_file': 1,
\ 'executable_callback': 'ale_linters#nasm#nasm#GetExecutable',
\ 'command_callback': 'ale_linters#nasm#nasm#GetCommand', \ 'command_callback': 'ale_linters#nasm#nasm#GetCommand',
\ 'callback': 'ale_linters#nasm#nasm#Handle', \ 'callback': 'ale_linters#nasm#nasm#Handle',
\}) \})

View File

@ -25,6 +25,5 @@ call ale#linter#Define('php', {
\ 'lsp': 'stdio', \ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#php#langserver#GetExecutable', \ 'executable_callback': 'ale_linters#php#langserver#GetExecutable',
\ 'command_callback': 'ale_linters#php#langserver#GetCommand', \ 'command_callback': 'ale_linters#php#langserver#GetCommand',
\ 'language': 'php',
\ 'project_root_callback': 'ale_linters#php#langserver#GetProjectRoot', \ 'project_root_callback': 'ale_linters#php#langserver#GetProjectRoot',
\}) \})

View File

@ -0,0 +1,45 @@
" Author: Alexander Olofsson <alexander.olofsson@liu.se>
" Description: Puppet Language Server integration for ALE
call ale#Set('puppet_languageserver_executable', 'puppet-languageserver')
function! ale_linters#puppet#languageserver#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'puppet_languageserver_executable')
endfunction
function! ale_linters#puppet#languageserver#GetCommand(buffer) abort
let l:exe = ale#Escape(ale_linters#puppet#languageserver#GetExecutable(a:buffer))
return l:exe . ' --stdio'
endfunction
function! ale_linters#puppet#languageserver#GetProjectRoot(buffer) abort
" Note: The metadata.json file is recommended for Puppet 4+ modules, but
" there's no requirement to have it, so fall back to the other possible
" Puppet module directories
let l:root_path = ale#path#FindNearestFile(a:buffer, 'metadata.json')
if !empty(l:root_path)
return fnamemodify(l:root_path, ':h')
endif
for l:test_path in [
\ 'manifests',
\ 'templates',
\]
let l:root_path = ale#path#FindNearestDirectory(a:buffer, l:test_path)
if !empty(l:root_path)
return fnamemodify(l:root_path, ':h:h')
endif
endfor
return ''
endfunction
call ale#linter#Define('puppet', {
\ 'name': 'languageserver',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#puppet#languageserver#GetExecutable',
\ 'command_callback': 'ale_linters#puppet#languageserver#GetCommand',
\ 'language': 'puppet',
\ 'project_root_callback': 'ale_linters#puppet#languageserver#GetProjectRoot',
\})

View File

@ -23,7 +23,6 @@ call ale#linter#Define('python', {
\ 'lsp': 'stdio', \ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#python#pyls#GetExecutable', \ 'executable_callback': 'ale_linters#python#pyls#GetExecutable',
\ 'command_callback': 'ale_linters#python#pyls#GetCommand', \ 'command_callback': 'ale_linters#python#pyls#GetCommand',
\ 'language': 'python',
\ 'project_root_callback': 'ale#python#FindProjectRoot', \ 'project_root_callback': 'ale#python#FindProjectRoot',
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\}) \})

View File

@ -23,7 +23,6 @@ call ale#linter#Define('python', {
\ 'lsp': 'stdio', \ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#python#pyre#GetExecutable', \ 'executable_callback': 'ale_linters#python#pyre#GetExecutable',
\ 'command_callback': 'ale_linters#python#pyre#GetCommand', \ 'command_callback': 'ale_linters#python#pyre#GetCommand',
\ 'language': 'python',
\ 'project_root_callback': 'ale#python#FindProjectRoot', \ 'project_root_callback': 'ale#python#FindProjectRoot',
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\}) \})

View File

@ -1,8 +1,8 @@
" Author: Eddie Lebow https://github.com/elebow " Author: Eddie Lebow https://github.com/elebow
" Description: rails_best_practices, a code metric tool for rails projects " Description: rails_best_practices, a code metric tool for rails projects
let g:ale_ruby_rails_best_practices_options = call ale#Set('ruby_rails_best_practices_options', '')
\ get(g:, 'ale_ruby_rails_best_practices_options', '') call ale#Set('ruby_rails_best_practices_executable', 'rails_best_practices')
function! ale_linters#ruby#rails_best_practices#Handle(buffer, lines) abort function! ale_linters#ruby#rails_best_practices#Handle(buffer, lines) abort
let l:output = [] let l:output = []
@ -22,8 +22,12 @@ function! ale_linters#ruby#rails_best_practices#Handle(buffer, lines) abort
return l:output return l:output
endfunction endfunction
function! ale_linters#ruby#rails_best_practices#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'ruby_rails_best_practices_executable')
endfunction
function! ale_linters#ruby#rails_best_practices#GetCommand(buffer) abort function! ale_linters#ruby#rails_best_practices#GetCommand(buffer) abort
let l:executable = ale#handlers#rails_best_practices#GetExecutable(a:buffer) let l:executable = ale_linters#ruby#rails_best_practices#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'bundle$' let l:exec_args = l:executable =~? 'bundle$'
\ ? ' exec rails_best_practices' \ ? ' exec rails_best_practices'
\ : '' \ : ''
@ -46,7 +50,7 @@ endfunction
call ale#linter#Define('ruby', { call ale#linter#Define('ruby', {
\ 'name': 'rails_best_practices', \ 'name': 'rails_best_practices',
\ 'executable_callback': 'ale#handlers#rails_best_practices#GetExecutable', \ 'executable_callback': 'ale_linters#ruby#rails_best_practices#GetExecutable',
\ 'command_callback': 'ale_linters#ruby#rails_best_practices#GetCommand', \ 'command_callback': 'ale_linters#ruby#rails_best_practices#GetCommand',
\ 'callback': 'ale_linters#ruby#rails_best_practices#Handle', \ 'callback': 'ale_linters#ruby#rails_best_practices#Handle',
\ 'lint_file': 1, \ 'lint_file': 1,

View File

@ -30,6 +30,5 @@ call ale#linter#Define('rust', {
\ 'lsp': 'stdio', \ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#rust#rls#GetExecutable', \ 'executable_callback': 'ale_linters#rust#rls#GetExecutable',
\ 'command_callback': 'ale_linters#rust#rls#GetCommand', \ 'command_callback': 'ale_linters#rust#rls#GetCommand',
\ 'language': 'rust',
\ 'project_root_callback': 'ale_linters#rust#rls#GetProjectRoot', \ 'project_root_callback': 'ale_linters#rust#rls#GetProjectRoot',
\}) \})

View File

@ -1,11 +1,11 @@
" Author: Kevin Kays - https://github.com/okkays " Author: Kevin Kays - https://github.com/okkays
" Description: Support for the scalastyle checker. " Description: Support for the scalastyle checker.
let g:ale_scala_scalastyle_options = call ale#Set('scala_scalastyle_options', '')
\ get(g:, 'ale_scala_scalastyle_options', '') " TODO: Remove support for the old option name in ALE 3.0.
call ale#Set('scala_scalastyle_config',
let g:ale_scalastyle_config_loc =
\ get(g:, 'ale_scalastyle_config_loc', '') \ get(g:, 'ale_scalastyle_config_loc', '')
\)
function! ale_linters#scala#scalastyle#Handle(buffer, lines) abort function! ale_linters#scala#scalastyle#Handle(buffer, lines) abort
" Look for help output from scalastyle first, which indicates that no " Look for help output from scalastyle first, which indicates that no
@ -66,23 +66,13 @@ function! ale_linters#scala#scalastyle#GetCommand(buffer) abort
" If all else fails, try the global config. " If all else fails, try the global config.
if empty(l:scalastyle_config) if empty(l:scalastyle_config)
let l:scalastyle_config = get(g:, 'ale_scalastyle_config_loc', '') let l:scalastyle_config = ale#Var(a:buffer, 'scala_scalastyle_config')
endif endif
" Build the command using the config file and additional options. return 'scalastyle'
let l:command = 'scalastyle' \ . (!empty(l:scalastyle_config) ? ' --config ' . ale#Escape(l:scalastyle_config) : '')
\ . ale#Pad(ale#Var(a:buffer, 'scala_scalastyle_options'))
if !empty(l:scalastyle_config) \ . ' %t'
let l:command .= ' --config ' . ale#Escape(l:scalastyle_config)
endif
if !empty(g:ale_scala_scalastyle_options)
let l:command .= ' ' . g:ale_scala_scalastyle_options
endif
let l:command .= ' %t'
return l:command
endfunction endfunction
call ale#linter#Define('scala', { call ale#linter#Define('scala', {

View File

@ -28,6 +28,5 @@ call ale#linter#Define('sh', {
\ 'lsp': 'stdio', \ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#sh#language_server#GetExecutable', \ 'executable_callback': 'ale_linters#sh#language_server#GetExecutable',
\ 'command_callback': 'ale_linters#sh#language_server#GetCommand', \ 'command_callback': 'ale_linters#sh#language_server#GetCommand',
\ 'language': 'sh',
\ 'project_root_callback': 'ale_linters#sh#language_server#GetProjectRoot', \ 'project_root_callback': 'ale_linters#sh#language_server#GetProjectRoot',
\}) \})

View File

@ -20,14 +20,12 @@ function! ale_linters#thrift#thrift#GetCommand(buffer) abort
let l:generators = ['cpp'] let l:generators = ['cpp']
endif endif
let l:output_dir = tempname() let l:output_dir = ale#engine#CreateDirectory(a:buffer)
call mkdir(l:output_dir)
call ale#engine#ManageDirectory(a:buffer, l:output_dir)
return ale#Escape(ale_linters#thrift#thrift#GetExecutable(a:buffer)) return ale#Escape(ale_linters#thrift#thrift#GetExecutable(a:buffer))
\ . ' ' . join(map(copy(l:generators), "'--gen ' . v:val")) \ . ale#Pad(join(map(copy(l:generators), "'--gen ' . v:val")))
\ . ' ' . join(map(copy(l:includes), "'-I ' . v:val")) \ . ale#Pad(join(map(copy(l:includes), "'-I ' . v:val")))
\ . ' ' . ale#Var(a:buffer, 'thrift_thrift_options') \ . ale#Pad(ale#Var(a:buffer, 'thrift_thrift_options'))
\ . ' -out ' . ale#Escape(l:output_dir) \ . ' -out ' . ale#Escape(l:output_dir)
\ . ' %t' \ . ' %t'
endfunction endfunction

View File

@ -1,17 +1,7 @@
" Author: Prashanth Chandra <https://github.com/prashcr>, Jonathan Clem <https://jclem.net> " Author: Prashanth Chandra <https://github.com/prashcr>, Jonathan Clem <https://jclem.net>
" Description: tslint for TypeScript files " Description: tslint for TypeScript files
call ale#Set('typescript_tslint_executable', 'tslint') call ale#handlers#tslint#InitVariables()
call ale#Set('typescript_tslint_config_path', '')
call ale#Set('typescript_tslint_rules_dir', '')
call ale#Set('typescript_tslint_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('typescript_tslint_ignore_empty_files', 0)
function! ale_linters#typescript#tslint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'typescript_tslint', [
\ 'node_modules/.bin/tslint',
\])
endfunction
function! ale_linters#typescript#tslint#Handle(buffer, lines) abort function! ale_linters#typescript#tslint#Handle(buffer, lines) abort
" Do not output any errors for empty files if the option is on. " Do not output any errors for empty files if the option is on.
@ -70,7 +60,7 @@ function! ale_linters#typescript#tslint#GetCommand(buffer) abort
\ : '' \ : ''
return ale#path#BufferCdString(a:buffer) return ale#path#BufferCdString(a:buffer)
\ . ale#Escape(ale_linters#typescript#tslint#GetExecutable(a:buffer)) \ . ale#Escape(ale#handlers#tslint#GetExecutable(a:buffer))
\ . ' --format json' \ . ' --format json'
\ . l:tslint_config_option \ . l:tslint_config_option
\ . l:tslint_rules_option \ . l:tslint_rules_option
@ -79,7 +69,7 @@ endfunction
call ale#linter#Define('typescript', { call ale#linter#Define('typescript', {
\ 'name': 'tslint', \ 'name': 'tslint',
\ 'executable_callback': 'ale_linters#typescript#tslint#GetExecutable', \ 'executable_callback': 'ale#handlers#tslint#GetExecutable',
\ 'command_callback': 'ale_linters#typescript#tslint#GetCommand', \ 'command_callback': 'ale_linters#typescript#tslint#GetCommand',
\ 'callback': 'ale_linters#typescript#tslint#Handle', \ 'callback': 'ale_linters#typescript#tslint#Handle',
\}) \})

View File

@ -7,7 +7,7 @@ if !exists('g:ale_verilog_verilator_options')
endif endif
function! ale_linters#verilog#verilator#GetCommand(buffer) abort function! ale_linters#verilog#verilator#GetCommand(buffer) abort
let l:filename = tempname() . '_verilator_linted.v' let l:filename = ale#util#Tempname() . '_verilator_linted.v'
" Create a special filename, so we can detect it in the handler. " Create a special filename, so we can detect it in the handler.
call ale#engine#ManageFile(a:buffer, l:filename) call ale#engine#ManageFile(a:buffer, l:filename)

View File

@ -0,0 +1,32 @@
" Author: Alexander Olofsson <alexander.olofsson@liu.se>
" Description: Vue vls Language Server integration for ALE
call ale#Set('vue_vls_executable', 'vls')
call ale#Set('vue_vls_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#vue#vls#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'vue_vls', [
\ 'node_modules/.bin/vls',
\])
endfunction
function! ale_linters#vue#vls#GetCommand(buffer) abort
let l:exe = ale#Escape(ale_linters#vue#vls#GetExecutable(a:buffer))
return l:exe . ' --stdio'
endfunction
function! ale_linters#vue#vls#GetProjectRoot(buffer) abort
let l:package_path = ale#path#FindNearestFile(a:buffer, 'package.json')
return !empty(l:package_path) ? fnamemodify(l:package_path, ':h') : ''
endfunction
call ale#linter#Define('vue', {
\ 'name': 'vls',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#vue#vls#GetExecutable',
\ 'command_callback': 'ale_linters#vue#vls#GetCommand',
\ 'language': 'vue',
\ 'project_root_callback': 'ale_linters#vue#vls#GetProjectRoot',
\})

View File

@ -11,7 +11,7 @@ endfunction
function! ale_linters#xml#xmllint#GetCommand(buffer) abort function! ale_linters#xml#xmllint#GetCommand(buffer) abort
return ale#Escape(ale_linters#xml#xmllint#GetExecutable(a:buffer)) return ale#Escape(ale_linters#xml#xmllint#GetExecutable(a:buffer))
\ . ' ' . ale#Var(a:buffer, 'xml_xmllint_options') \ . ale#Pad(ale#Var(a:buffer, 'xml_xmllint_options'))
\ . ' --noout -' \ . ' --noout -'
endfunction endfunction

View File

@ -191,15 +191,12 @@ endfunction
" "
" Every variable name will be prefixed with 'ale_'. " Every variable name will be prefixed with 'ale_'.
function! ale#Var(buffer, variable_name) abort function! ale#Var(buffer, variable_name) abort
let l:nr = str2nr(a:buffer)
let l:full_name = 'ale_' . a:variable_name let l:full_name = 'ale_' . a:variable_name
let l:vars = getbufvar(str2nr(a:buffer), '', 0)
if bufexists(l:nr) if l:vars is 0
let l:vars = getbufvar(l:nr, '') " Look for variables from deleted buffers, saved from :ALEFix
elseif has_key(g:, 'ale_fix_buffer_data') let l:vars = get(get(g:ale_fix_buffer_data, a:buffer, {}), 'vars', {})
let l:vars = get(g:ale_fix_buffer_data, l:nr, {'vars': {}}).vars
else
let l:vars = {}
endif endif
return get(l:vars, l:full_name, g:[l:full_name]) return get(l:vars, l:full_name, g:[l:full_name])
@ -210,10 +207,29 @@ endfunction
" Every variable name will be prefixed with 'ale_'. " Every variable name will be prefixed with 'ale_'.
function! ale#Set(variable_name, default) abort function! ale#Set(variable_name, default) abort
let l:full_name = 'ale_' . a:variable_name let l:full_name = 'ale_' . a:variable_name
let l:value = get(g:, l:full_name, a:default)
let g:[l:full_name] = l:value
return l:value if !has_key(g:, l:full_name)
let g:[l:full_name] = a:default
endif
endfunction
" Given a string for adding to a command, return the string padded with a
" space on the left if it is not empty. Otherwise return an empty string.
"
" This can be used for making command strings cleaner and easier to test.
function! ale#Pad(string) abort
return !empty(a:string) ? ' ' . a:string : ''
endfunction
" Given a environment variable name and a value, produce part of a command for
" setting an environment variable before running a command. The syntax will be
" valid for cmd on Windows, or most shells on Unix.
function! ale#Env(variable_name, value) abort
if has('win32')
return 'set ' . a:variable_name . '=' . ale#Escape(a:value) . ' && '
endif
return a:variable_name . '=' . ale#Escape(a:value) . ' '
endfunction endfunction
" Escape a string suitably for each platform. " Escape a string suitably for each platform.

View File

@ -0,0 +1,159 @@
let s:chain_results = []
function! ale#assert#WithChainResults(...) abort
let s:chain_results = a:000
endfunction
function! s:GetLinter() abort
let l:linters = ale#linter#GetLintersLoaded()
let l:filetype_linters = get(values(l:linters), 0, [])
if len(l:linters) is 0 || len(l:filetype_linters) is 0
throw 'No linters were loaded'
endif
if len(l:linters) > 1 || len(l:filetype_linters) > 1
throw 'More than one linter was loaded'
endif
return l:filetype_linters[0]
endfunction
" Load the currently loaded linter for a test case, and check that the command
" matches the given string.
function! ale#assert#Linter(expected_executable, expected_command) abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
let l:executable = ale#linter#GetExecutable(l:buffer, l:linter)
if has_key(l:linter, 'command_chain')
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 len(l:callbacks) is 1
let l:command = call(l:callbacks[0], [l:buffer])
else
let l:input = get(s:chain_results, len(l:callbacks) - 2, [])
let l:command = call(l:callbacks[-1], [l:buffer, l:input])
endif
else
let l:command = []
let l:chain_index = 0
for l:Callback in l:callbacks
if l:chain_index is 0
call add(l:command, call(l:Callback, [l:buffer]))
else
let l:input = get(s:chain_results, l:chain_index - 1, [])
call add(l:command, call(l:Callback, [l:buffer, l:input]))
endif
let l:chain_index += 1
endfor
endif
else
let l:command = ale#linter#GetCommand(l:buffer, l:linter)
" Replace %e with the escaped executable, so tests keep passing after
" linters are changed to use %e.
let l:command = substitute(l:command, '%e', '\=ale#Escape(l:executable)', 'g')
endif
AssertEqual
\ [a:expected_executable, a:expected_command],
\ [l:executable, l:command]
endfunction
function! ale#assert#LinterNotExecuted() abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
let l:executable = ale#linter#GetExecutable(l:buffer, l:linter)
Assert empty(l:executable), "The linter will be executed when it shouldn't be"
endfunction
function! ale#assert#LSPOptions(expected_options) abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
let l:initialization_options = ale#lsp_linter#GetOptions(l:buffer, l:linter)
AssertEqual a:expected_options, l:initialization_options
endfunction
function! ale#assert#LSPLanguage(expected_language) abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
let l:language = ale#util#GetFunction(l:linter.language_callback)(l:buffer)
AssertEqual a:expected_language, l:language
endfunction
function! ale#assert#LSPProject(expected_root) abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
let l:root = ale#util#GetFunction(l:linter.project_root_callback)(l:buffer)
AssertEqual a:expected_root, l:root
endfunction
" A dummy function for making sure this module is loaded.
function! ale#assert#SetUpLinterTest(filetype, name) abort
" Set up a marker so ALE doesn't create real random temporary filenames.
let g:ale_create_dummy_temporary_file = 1
" Remove current linters.
call ale#linter#Reset()
call ale#linter#PreventLoading(a:filetype)
let l:prefix = 'ale_' . a:filetype . '_' . a:name
let b:filter_expr = 'v:val[: len(l:prefix) - 1] is# l:prefix'
Save g:ale_c_build_dir
unlet! g:ale_c_build_dir
" Save and clear linter variables.
" We'll load the runtime file to reset them to defaults.
for l:key in filter(keys(g:), b:filter_expr)
execute 'Save g:' . l:key
unlet g:[l:key]
endfor
unlet! b:ale_c_build_dir
for l:key in filter(keys(b:), b:filter_expr)
unlet b:[l:key]
endfor
execute 'runtime ale_linters/' . a:filetype . '/' . a:name . '.vim'
call ale#test#SetDirectory('/testplugin/test/command_callback')
command! -nargs=+ WithChainResults :call ale#assert#WithChainResults(<args>)
command! -nargs=+ AssertLinter :call ale#assert#Linter(<args>)
command! -nargs=0 AssertLinterNotExecuted :call ale#assert#LinterNotExecuted()
command! -nargs=+ AssertLSPOptions :call ale#assert#LSPOptions(<args>)
command! -nargs=+ AssertLSPLanguage :call ale#assert#LSPLanguage(<args>)
command! -nargs=+ AssertLSPProject :call ale#assert#LSPProject(<args>)
endfunction
function! ale#assert#TearDownLinterTest() abort
unlet! g:ale_create_dummy_temporary_file
let s:chain_results = []
delcommand WithChainResults
delcommand AssertLinter
delcommand AssertLinterNotExecuted
delcommand AssertLSPOptions
delcommand AssertLSPLanguage
delcommand AssertLSPProject
call ale#test#RestoreDirectory()
Restore
call ale#linter#Reset()
if exists('*ale#semver#ResetVersionCache')
call ale#semver#ResetVersionCache()
endif
endfunction

View File

@ -13,14 +13,14 @@ function! s:TemporaryFilename(buffer) abort
" Create a temporary filename, <temp_dir>/<original_basename> " Create a temporary filename, <temp_dir>/<original_basename>
" The file itself will not be created by this function. " The file itself will not be created by this function.
return tempname() . (has('win32') ? '\' : '/') . l:filename return ale#util#Tempname() . (has('win32') ? '\' : '/') . l:filename
endfunction endfunction
" Given a command string, replace every... " Given a command string, replace every...
" %s -> with the current filename " %s -> with the current filename
" %t -> with the name of an unused file in a temporary directory " %t -> with the name of an unused file in a temporary directory
" %% -> with a literal % " %% -> with a literal %
function! ale#command#FormatCommand(buffer, command, pipe_file_if_needed) abort function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_needed) abort
let l:temporary_file = '' let l:temporary_file = ''
let l:command = a:command let l:command = a:command
@ -28,6 +28,11 @@ function! ale#command#FormatCommand(buffer, command, pipe_file_if_needed) abort
" with an ugly string. " with an ugly string.
let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g') let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g')
" Replace %e with the escaped executable, if available.
if !empty(a:executable) && l:command =~# '%e'
let l:command = substitute(l:command, '%e', '\=ale#Escape(a:executable)', 'g')
endif
" Replace all %s occurrences in the string with the name of the current " Replace all %s occurrences in the string with the name of the current
" file. " file.
if l:command =~# '%s' if l:command =~# '%s'

View File

@ -1,6 +1,17 @@
" Author: w0rp <devw0rp@gmail.com> " Author: w0rp <devw0rp@gmail.com>
" Description: Completion support for LSP linters " Description: Completion support for LSP linters
" The omnicompletion menu is shown through a special Plug mapping which is
" only valid in Insert mode. This way, feedkeys() won't send these keys if you
" quit Insert mode quickly enough.
inoremap <silent> <Plug>(ale_show_completion_menu) <C-x><C-o>
" If we hit the key sequence in normal mode, then we won't show the menu, so
" we should restore the old settings right away.
nnoremap <silent> <Plug>(ale_show_completion_menu) :call ale#completion#RestoreCompletionOptions()<CR>
cnoremap <silent> <Plug>(ale_show_completion_menu) <Nop>
vnoremap <silent> <Plug>(ale_show_completion_menu) <Nop>
onoremap <silent> <Plug>(ale_show_completion_menu) <Nop>
let g:ale_completion_delay = get(g:, 'ale_completion_delay', 100) 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_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_max_suggestions = get(g:, 'ale_completion_max_suggestions', 50)
@ -129,7 +140,16 @@ function! ale#completion#Filter(buffer, suggestions, prefix) abort
return l:filtered_suggestions return l:filtered_suggestions
endfunction endfunction
function! s:ReplaceCompleteopt() abort function! s:ReplaceCompletionOptions() abort
" Remember the old omnifunc value, if there is one.
" If we don't store an old one, we'll just never reset the option.
" This will stop some random exceptions from appearing.
if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
let b:ale_old_omnifunc = &l:omnifunc
endif
let &l:omnifunc = 'ale#completion#OmniFunc'
if !exists('b:ale_old_completopt') if !exists('b:ale_old_completopt')
let b:ale_old_completopt = &l:completeopt let b:ale_old_completopt = &l:completeopt
endif endif
@ -141,6 +161,22 @@ function! s:ReplaceCompleteopt() abort
endif endif
endfunction endfunction
function! ale#completion#RestoreCompletionOptions() abort
" Reset settings when completion is done.
if exists('b:ale_old_omnifunc')
if b:ale_old_omnifunc isnot# 'pythoncomplete#Complete'
let &l:omnifunc = b:ale_old_omnifunc
endif
unlet b:ale_old_omnifunc
endif
if exists('b:ale_old_completopt')
let &l:completeopt = b:ale_old_completopt
unlet b:ale_old_completopt
endif
endfunction
function! ale#completion#OmniFunc(findstart, base) abort function! ale#completion#OmniFunc(findstart, base) abort
if a:findstart if a:findstart
let l:line = b:ale_completion_info.line let l:line = b:ale_completion_info.line
@ -163,33 +199,30 @@ function! ale#completion#OmniFunc(findstart, base) abort
let b:ale_completion_result = function(l:parser)(l:response) let b:ale_completion_result = function(l:parser)(l:response)
endif endif
call s:ReplaceCompleteopt() call s:ReplaceCompletionOptions()
return get(b:, 'ale_completion_result', []) return get(b:, 'ale_completion_result', [])
endif endif
endfunction endfunction
function! ale#completion#Show(response, completion_parser) abort function! ale#completion#Show(response, completion_parser) abort
" Remember the old omnifunc value, if there is one. if ale#util#Mode() isnot# 'i'
" If we don't store an old one, we'll just never reset the option. return
" This will stop some random exceptions from appearing.
if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc)
let b:ale_old_omnifunc = &l:omnifunc
endif endif
" Set the list in the buffer, temporarily replace omnifunc with our " Set the list in the buffer, temporarily replace omnifunc with our
" function, and then start omni-completion. " function, and then start omni-completion.
let b:ale_completion_response = a:response let b:ale_completion_response = a:response
let b:ale_completion_parser = a:completion_parser let b:ale_completion_parser = a:completion_parser
let &l:omnifunc = 'ale#completion#OmniFunc' call s:ReplaceCompletionOptions()
call s:ReplaceCompleteopt() call ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")
call ale#util#FeedKeys("\<C-x>\<C-o>", 'n')
endfunction endfunction
function! s:CompletionStillValid(request_id) abort function! s:CompletionStillValid(request_id) abort
let [l:line, l:column] = getcurpos()[1:2] let [l:line, l:column] = getcurpos()[1:2]
return has_key(b:, 'ale_completion_info') return ale#util#Mode() is# 'i'
\&& has_key(b:, 'ale_completion_info')
\&& b:ale_completion_info.request_id == a:request_id \&& b:ale_completion_info.request_id == a:request_id
\&& b:ale_completion_info.line == l:line \&& b:ale_completion_info.line == l:line
\&& b:ale_completion_info.column == l:column \&& b:ale_completion_info.column == l:column
@ -477,7 +510,7 @@ function! s:TimerHandler(...) abort
" When running the timer callback, we have to be sure that the cursor " When running the timer callback, we have to be sure that the cursor
" hasn't moved from where it was when we requested completions by typing. " hasn't moved from where it was when we requested completions by typing.
if s:timer_pos == [l:line, l:column] if s:timer_pos == [l:line, l:column] && ale#util#Mode() is# 'i'
call ale#completion#GetCompletions() call ale#completion#GetCompletions()
endif endif
endfunction endfunction
@ -518,19 +551,7 @@ endfunction
function! ale#completion#Done() abort function! ale#completion#Done() abort
silent! pclose silent! pclose
" Reset settings when completion is done. call ale#completion#RestoreCompletionOptions()
if exists('b:ale_old_omnifunc')
if b:ale_old_omnifunc isnot# 'pythoncomplete#Complete'
let &l:omnifunc = b:ale_old_omnifunc
endif
unlet b:ale_old_omnifunc
endif
if exists('b:ale_old_completopt')
let &l:completeopt = b:ale_old_completopt
unlet b:ale_old_completopt
endif
let s:last_done_pos = getcurpos()[1:2] let s:last_done_pos = getcurpos()[1:2]
endfunction endfunction

View File

@ -96,9 +96,26 @@ function! ale#engine#ManageDirectory(buffer, directory) abort
call add(g:ale_buffer_info[a:buffer].temporary_directory_list, a:directory) call add(g:ale_buffer_info[a:buffer].temporary_directory_list, a:directory)
endfunction endfunction
function! ale#engine#CreateFile(buffer) abort
" This variable can be set to 1 in tests to stub this out.
if get(g:, 'ale_create_dummy_temporary_file')
return 'TEMP'
endif
let l:temporary_file = ale#util#Tempname()
call ale#engine#ManageFile(a:buffer, l:temporary_file)
return l:temporary_file
endfunction
" Create a new temporary directory and manage it in one go. " Create a new temporary directory and manage it in one go.
function! ale#engine#CreateDirectory(buffer) abort function! ale#engine#CreateDirectory(buffer) abort
let l:temporary_directory = tempname() " This variable can be set to 1 in tests to stub this out.
if get(g:, 'ale_create_dummy_temporary_file')
return 'TEMP_DIR'
endif
let l:temporary_directory = ale#util#Tempname()
" Create the temporary directory for the file, unreadable by 'other' " Create the temporary directory for the file, unreadable by 'other'
" users. " users.
call mkdir(l:temporary_directory, '', 0750) call mkdir(l:temporary_directory, '', 0750)
@ -189,6 +206,7 @@ function! s:HandleExit(job_id, exit_code) abort
let l:linter = l:job_info.linter let l:linter = l:job_info.linter
let l:output = l:job_info.output let l:output = l:job_info.output
let l:buffer = l:job_info.buffer let l:buffer = l:job_info.buffer
let l:executable = l:job_info.executable
let l:next_chain_index = l:job_info.next_chain_index let l:next_chain_index = l:job_info.next_chain_index
if g:ale_history_enabled if g:ale_history_enabled
@ -212,7 +230,7 @@ function! s:HandleExit(job_id, exit_code) abort
endif endif
if l:next_chain_index < len(get(l:linter, 'command_chain', [])) if l:next_chain_index < len(get(l:linter, 'command_chain', []))
call s:InvokeChain(l:buffer, l:linter, l:next_chain_index, l:output) call s:InvokeChain(l:buffer, l:executable, l:linter, l:next_chain_index, l:output)
return return
endif endif
@ -221,7 +239,12 @@ function! s:HandleExit(job_id, exit_code) abort
call ale#history#RememberOutput(l:buffer, a:job_id, l:output[:]) call ale#history#RememberOutput(l:buffer, a:job_id, l:output[:])
endif endif
let l:loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output) try
let l:loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output)
" Handle the function being unknown, or being deleted.
catch /E700/
let l:loclist = []
endtry
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist) call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist)
endfunction endfunction
@ -440,6 +463,12 @@ endfunction
" Returns 1 when the job was started successfully. " Returns 1 when the job was started successfully.
function! s:RunJob(options) abort function! s:RunJob(options) abort
let l:command = a:options.command let l:command = a:options.command
if empty(l:command)
return 0
endif
let l:executable = a:options.executable
let l:buffer = a:options.buffer let l:buffer = a:options.buffer
let l:linter = a:options.linter let l:linter = a:options.linter
let l:output_stream = a:options.output_stream let l:output_stream = a:options.output_stream
@ -447,11 +476,12 @@ function! s:RunJob(options) abort
let l:read_buffer = a:options.read_buffer let l:read_buffer = a:options.read_buffer
let l:info = g:ale_buffer_info[l:buffer] let l:info = g:ale_buffer_info[l:buffer]
if empty(l:command) let [l:temporary_file, l:command] = ale#command#FormatCommand(
return 0 \ l:buffer,
endif \ l:executable,
\ l:command,
let [l:temporary_file, l:command] = ale#command#FormatCommand(l:buffer, l:command, l:read_buffer) \ l:read_buffer,
\)
if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file) if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file)
" If a temporary filename has been formatted in to the command, then " If a temporary filename has been formatted in to the command, then
@ -512,6 +542,7 @@ function! s:RunJob(options) abort
let s:job_info_map[l:job_id] = { let s:job_info_map[l:job_id] = {
\ 'linter': l:linter, \ 'linter': l:linter,
\ 'buffer': l:buffer, \ 'buffer': l:buffer,
\ 'executable': l:executable,
\ 'output': [], \ 'output': [],
\ 'next_chain_index': l:next_chain_index, \ 'next_chain_index': l:next_chain_index,
\} \}
@ -604,8 +635,9 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
\} \}
endfunction endfunction
function! s:InvokeChain(buffer, linter, chain_index, input) abort function! s:InvokeChain(buffer, executable, linter, chain_index, input) abort
let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input) let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input)
let l:options.executable = a:executable
return s:RunJob(l:options) return s:RunJob(l:options)
endfunction endfunction
@ -699,7 +731,7 @@ function! s:RunLinter(buffer, linter) abort
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
if ale#engine#IsExecutable(a:buffer, l:executable) if ale#engine#IsExecutable(a:buffer, l:executable)
return s:InvokeChain(a:buffer, a:linter, 0, []) return s:InvokeChain(a:buffer, l:executable, a:linter, 0, [])
endif endif
endif endif

View File

@ -39,35 +39,48 @@ function! ale#events#SaveEvent(buffer) abort
endif endif
endfunction endfunction
function! s:LintOnEnter(buffer) abort function! ale#events#LintOnEnter(buffer) abort
if ale#Var(a:buffer, 'enabled') " Unmark a file as being changed outside of Vim after we try to check it.
\&& g:ale_lint_on_enter call setbufvar(a:buffer, 'ale_file_changed', 0)
\&& has_key(b:, 'ale_file_changed')
call remove(b:, 'ale_file_changed') if ale#Var(a:buffer, 'enabled') && g:ale_lint_on_enter
call ale#Queue(0, 'lint_file', a:buffer) call ale#Queue(0, 'lint_file', a:buffer)
endif endif
endfunction endfunction
function! ale#events#EnterEvent(buffer) abort function! ale#events#ReadOrEnterEvent(buffer) abort
" Apply pattern options if the variable is set.
if get(g:, 'ale_pattern_options_enabled', 1)
\&& !empty(get(g:, 'ale_pattern_options'))
call ale#pattern_options#SetOptions(a:buffer)
endif
" When entering a buffer, we are no longer quitting it. " When entering a buffer, we are no longer quitting it.
call setbufvar(a:buffer, 'ale_quitting', 0) call setbufvar(a:buffer, 'ale_quitting', 0)
let l:filetype = getbufvar(a:buffer, '&filetype') let l:filetype = getbufvar(a:buffer, '&filetype')
call setbufvar(a:buffer, 'ale_original_filetype', l:filetype) call setbufvar(a:buffer, 'ale_original_filetype', l:filetype)
call s:LintOnEnter(a:buffer) " If the file changed outside of Vim, check it on BufEnter,BufRead
if getbufvar(a:buffer, 'ale_file_changed')
call ale#events#LintOnEnter(a:buffer)
endif
endfunction endfunction
function! ale#events#FileTypeEvent(buffer, new_filetype) abort function! ale#events#FileTypeEvent(buffer, new_filetype) abort
let l:filetype = getbufvar(a:buffer, 'ale_original_filetype', '') " The old filetype will be set to an empty string by the BuFEnter event,
" and not linting when the old filetype hasn't been set yet prevents
" buffers being checked when you enter them when linting on enter is off.
let l:old_filetype = getbufvar(a:buffer, 'ale_original_filetype', v:null)
" If we're setting the filetype for the first time after it was blank, if l:old_filetype isnot v:null
" and the option for linting on enter is off, then we should set this \&& !empty(a:new_filetype)
" filetype as the original filetype. Otherwise ALE will still appear to \&& a:new_filetype isnot# l:old_filetype
" lint files because of the BufEnter event, etc. " Remember what the new filetype is.
if empty(l:filetype) && !ale#Var(a:buffer, 'lint_on_enter')
call setbufvar(a:buffer, 'ale_original_filetype', a:new_filetype) call setbufvar(a:buffer, 'ale_original_filetype', a:new_filetype)
elseif a:new_filetype isnot# l:filetype
call ale#Queue(300, 'lint_file', a:buffer) if g:ale_lint_on_filetype_changed
call ale#Queue(300, 'lint_file', a:buffer)
endif
endif endif
endfunction endfunction
@ -75,7 +88,7 @@ function! ale#events#FileChangedEvent(buffer) abort
call setbufvar(a:buffer, 'ale_file_changed', 1) call setbufvar(a:buffer, 'ale_file_changed', 1)
if bufnr('') == a:buffer if bufnr('') == a:buffer
call s:LintOnEnter(a:buffer) call ale#events#LintOnEnter(a:buffer)
endif endif
endfunction endfunction
@ -87,7 +100,7 @@ function! ale#events#Init() abort
autocmd! autocmd!
" These events always need to be set up. " These events always need to be set up.
autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions(str2nr(expand('<abuf>'))) autocmd BufEnter,BufRead * call ale#events#ReadOrEnterEvent(str2nr(expand('<abuf>')))
autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand('<abuf>'))) autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand('<abuf>')))
if g:ale_enabled if g:ale_enabled
@ -99,11 +112,8 @@ function! ale#events#Init() abort
autocmd TextChangedI * call ale#Queue(g:ale_lint_delay) autocmd TextChangedI * call ale#Queue(g:ale_lint_delay)
endif endif
" Handle everything that needs to happen when buffers are entered.
autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand('<abuf>')))
if g:ale_lint_on_enter if g:ale_lint_on_enter
autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand('<abuf>'))) autocmd BufWinEnter * call ale#events#LintOnEnter(str2nr(expand('<abuf>')))
" Track when the file is changed outside of Vim. " Track when the file is changed outside of Vim.
autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>'))) autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>')))
endif endif

View File

@ -1,10 +1,3 @@
" This global Dictionary tracks the ALE fix data for jobs, etc.
" This Dictionary should not be accessed outside of the plugin. It is only
" global so it can be modified in Vader tests.
if !has_key(g:, 'ale_fix_buffer_data')
let g:ale_fix_buffer_data = {}
endif
if !has_key(s:, 'job_info_map') if !has_key(s:, 'job_info_map')
let s:job_info_map = {} let s:job_info_map = {}
endif endif
@ -219,6 +212,7 @@ function! s:RunJob(options) abort
let [l:temporary_file, l:command] = ale#command#FormatCommand( let [l:temporary_file, l:command] = ale#command#FormatCommand(
\ l:buffer, \ l:buffer,
\ '',
\ l:command, \ l:command,
\ l:read_buffer, \ l:read_buffer,
\) \)

View File

@ -242,6 +242,9 @@ endfunction
" Add a function for fixing problems to the registry. " Add a function for fixing problems to the registry.
" (name, func, filetypes, desc, aliases) " (name, func, filetypes, desc, aliases)
function! ale#fix#registry#Add(name, func, filetypes, desc, ...) abort 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) != type('')
throw '''name'' must be a String' throw '''name'' must be a String'
endif endif

View File

@ -2,9 +2,12 @@
" Description: Fixing Python imports with isort. " Description: Fixing Python imports with isort.
call ale#Set('python_isort_executable', 'isort') call ale#Set('python_isort_executable', 'isort')
call ale#Set('python_isort_options', '')
call ale#Set('python_isort_use_global', get(g:, 'ale_use_global_executables', 0)) call ale#Set('python_isort_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#isort#Fix(buffer) abort function! ale#fixers#isort#Fix(buffer) abort
let l:options = ale#Var(a:buffer, 'python_isort_options')
let l:executable = ale#python#FindExecutable( let l:executable = ale#python#FindExecutable(
\ a:buffer, \ a:buffer,
\ 'python_isort', \ 'python_isort',
@ -17,6 +20,6 @@ function! ale#fixers#isort#Fix(buffer) abort
return { return {
\ 'command': ale#path#BufferCdString(a:buffer) \ 'command': ale#path#BufferCdString(a:buffer)
\ . ale#Escape(l:executable) . ' -', \ . ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '') . ' -',
\} \}
endfunction endfunction

View File

@ -2,7 +2,7 @@
" Description: Fixing files with tslint. " Description: Fixing files with tslint.
function! ale#fixers#tslint#Fix(buffer) abort function! ale#fixers#tslint#Fix(buffer) abort
let l:executable = ale_linters#typescript#tslint#GetExecutable(a:buffer) let l:executable = ale#handlers#tslint#GetExecutable(a:buffer)
let l:tslint_config_path = ale#path#ResolveLocalPath( let l:tslint_config_path = ale#path#ResolveLocalPath(
\ a:buffer, \ a:buffer,

View File

@ -2,7 +2,7 @@
" Description: Error handling for the format GHC outputs. " Description: Error handling for the format GHC outputs.
" Remember the directory used for temporary files for Vim. " Remember the directory used for temporary files for Vim.
let s:temp_dir = fnamemodify(tempname(), ':h') let s:temp_dir = fnamemodify(ale#util#Tempname(), ':h')
" Build part of a regular expression for matching ALE temporary filenames. " Build part of a regular expression for matching ALE temporary filenames.
let s:temp_regex_prefix = let s:temp_regex_prefix =
\ '\M' \ '\M'

View File

@ -1,6 +0,0 @@
call ale#Set('ruby_rails_best_practices_options', '')
call ale#Set('ruby_rails_best_practices_executable', 'rails_best_practices')
function! ale#handlers#rails_best_practices#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'ruby_rails_best_practices_executable')
endfunction

View File

@ -0,0 +1,13 @@
function! ale#handlers#tslint#InitVariables() abort
call ale#Set('typescript_tslint_executable', 'tslint')
call ale#Set('typescript_tslint_config_path', '')
call ale#Set('typescript_tslint_rules_dir', '')
call ale#Set('typescript_tslint_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('typescript_tslint_ignore_empty_files', 0)
endfunction
function! ale#handlers#tslint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'typescript_tslint', [
\ 'node_modules/.bin/tslint',
\])
endfunction

View File

@ -1,4 +1,3 @@
call ale#Set('wrap_command_as_one_argument', 0)
" Author: w0rp <devw0rp@gmail.com> " Author: w0rp <devw0rp@gmail.com>
" Description: Linter registration and lazy-loading " Description: Linter registration and lazy-loading
" Retrieves linters as requested by the engine, loading them if needed. " Retrieves linters as requested by the engine, loading them if needed.
@ -47,6 +46,16 @@ function! ale#linter#Reset() abort
let s:linters = {} let s:linters = {}
endfunction endfunction
" Return a reference to the linters loaded.
" This is only for tests.
" Do not call this function.
function! ale#linter#GetLintersLoaded() abort
" This command will throw from the sandbox.
let &equalprg=&equalprg
return s:linters
endfunction
function! s:IsCallback(value) abort function! s:IsCallback(value) abort
return type(a:value) == type('') || type(a:value) == type(function('type')) return type(a:value) == type('') || type(a:value) == type(function('type'))
endfunction endfunction
@ -59,7 +68,7 @@ function! s:LanguageGetter(buffer) dict abort
return l:self.language return l:self.language
endfunction endfunction
function! ale#linter#PreProcess(linter) abort function! ale#linter#PreProcess(filetype, linter) abort
if type(a:linter) != type({}) if type(a:linter) != type({})
throw 'The linter object must be a Dictionary' throw 'The linter object must be a Dictionary'
endif endif
@ -193,13 +202,20 @@ function! ale#linter#PreProcess(linter) abort
endif endif
if l:needs_lsp_details if l:needs_lsp_details
if has_key(a:linter, 'language') if has_key(a:linter, 'language_callback')
if has_key(a:linter, 'language_callback') if has_key(a:linter, 'language')
throw 'Only one of `language` or `language_callback` ' throw 'Only one of `language` or `language_callback` '
\ . 'should be set' \ . 'should be set'
endif endif
let l:obj.language = get(a:linter, 'language') let l:obj.language_callback = get(a:linter, 'language_callback')
if !s:IsCallback(l:obj.language_callback)
throw '`language_callback` must be a callback for LSP linters'
endif
else
" 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) != type('')
throw '`language` must be a string' throw '`language` must be a string'
@ -207,12 +223,6 @@ function! ale#linter#PreProcess(linter) abort
" Make 'language_callback' return the 'language' value. " Make 'language_callback' return the 'language' value.
let l:obj.language_callback = function('s:LanguageGetter') let l:obj.language_callback = function('s:LanguageGetter')
else
let l:obj.language_callback = get(a:linter, 'language_callback')
if !s:IsCallback(l:obj.language_callback)
throw '`language_callback` must be a callback for LSP linters'
endif
endif endif
let l:obj.project_root_callback = get(a:linter, 'project_root_callback') let l:obj.project_root_callback = get(a:linter, 'project_root_callback')
@ -282,11 +292,14 @@ function! ale#linter#PreProcess(linter) abort
endfunction endfunction
function! ale#linter#Define(filetype, linter) abort function! ale#linter#Define(filetype, linter) abort
" This command will throw from the sandbox.
let &equalprg=&equalprg
if !has_key(s:linters, a:filetype) if !has_key(s:linters, a:filetype)
let s:linters[a:filetype] = [] let s:linters[a:filetype] = []
endif endif
let l:new_linter = ale#linter#PreProcess(a:linter) let l:new_linter = ale#linter#PreProcess(a:filetype, a:linter)
call add(s:linters[a:filetype], l:new_linter) call add(s:linters[a:filetype], l:new_linter)
endfunction endfunction
@ -297,6 +310,12 @@ function! ale#linter#PreventLoading(filetype) abort
endfunction endfunction
function! ale#linter#GetAll(filetypes) abort function! ale#linter#GetAll(filetypes) abort
" Don't return linters in the sandbox.
" Otherwise a sandboxed script could modify them.
if ale#util#InSandbox()
return []
endif
let l:combined_linters = [] let l:combined_linters = []
for l:filetype in a:filetypes for l:filetype in a:filetypes

View File

@ -105,11 +105,17 @@ function! ale#lsp#response#GetErrorMessage(response) abort
return '' return ''
endif endif
" Include the traceback as details, if it's there. " Include the traceback or error data as details, if present.
let l:traceback = get(get(a:response.error, 'data', {}), 'traceback', []) let l:error_data = get(a:response.error, 'data', {})
if type(l:traceback) is type([]) && !empty(l:traceback) if type(l:error_data) is type('')
let l:message .= "\n" . join(l:traceback, "\n") 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)
let l:message .= "\n" . join(l:traceback, "\n")
endif
endif endif
return l:message return l:message

View File

@ -114,6 +114,18 @@ function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
endif endif
endfunction endfunction
function! ale#lsp_linter#GetOptions(buffer, linter) abort
let l:initialization_options = {}
if has_key(a:linter, 'initialization_options_callback')
let l:initialization_options = ale#util#GetFunction(a:linter.initialization_options_callback)(a:buffer)
elseif has_key(a:linter, 'initialization_options')
let l:initialization_options = a:linter.initialization_options
endif
return l:initialization_options
endfunction
" Given a buffer, an LSP linter, and a callback to register for handling " 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 " messages, start up an LSP linter and get ready to receive errors or
" completions. " completions.
@ -128,13 +140,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
return {} return {}
endif endif
let l:initialization_options = {} let l:initialization_options = ale#lsp_linter#GetOptions(a:buffer, a:linter)
if has_key(a:linter, 'initialization_options_callback')
let l:initialization_options = ale#util#GetFunction(a:linter.initialization_options_callback)(a:buffer)
elseif has_key(a:linter, 'initialization_options')
let l:initialization_options = a:linter.initialization_options
endif
if a:linter.lsp is# 'socket' if a:linter.lsp is# 'socket'
let l:address = ale#linter#GetAddress(a:buffer, a:linter) let l:address = ale#linter#GetAddress(a:buffer, a:linter)
@ -147,14 +153,14 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
else else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
if !executable(l:executable) if empty(l:executable) || !executable(l:executable)
return {} return {}
endif endif
let l:command = ale#job#PrepareCommand( let l:command = ale#linter#GetCommand(a:buffer, a:linter)
\ a:buffer, " Format the command, so %e can be formatted into it.
\ ale#linter#GetCommand(a:buffer, a:linter), let l:command = ale#command#FormatCommand(a:buffer, l:executable, l:command, 0)[1]
\) let l:command = ale#job#PrepareCommand(a:buffer, l:command)
let l:conn_id = ale#lsp#StartProgram( let l:conn_id = ale#lsp#StartProgram(
\ l:executable, \ l:executable,
\ l:command, \ l:command,

View File

@ -84,7 +84,7 @@ function! ale#path#IsAbsolute(filename) abort
return a:filename[:0] is# '/' || a:filename[1:2] is# ':\' return a:filename[:0] is# '/' || a:filename[1:2] is# ':\'
endfunction endfunction
let s:temp_dir = ale#path#Simplify(fnamemodify(tempname(), ':h')) let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h'))
" Given a filename, return 1 if the file represents some temporary file " Given a filename, return 1 if the file represents some temporary file
" created by Vim. " created by Vim.

View File

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

View File

@ -6,6 +6,7 @@ let s:sep = has('win32') ? '\' : '/'
let s:bin_dir = has('unix') ? 'bin' : 'Scripts' let s:bin_dir = has('unix') ? 'bin' : 'Scripts'
let g:ale_virtualenv_dir_names = get(g:, 'ale_virtualenv_dir_names', [ let g:ale_virtualenv_dir_names = get(g:, 'ale_virtualenv_dir_names', [
\ '.env', \ '.env',
\ '.venv',
\ 'env', \ 'env',
\ 've-py3', \ 've-py3',
\ 've', \ 've',
@ -23,6 +24,8 @@ function! ale#python#FindProjectRootIni(buffer) abort
\|| filereadable(l:path . '/mypy.ini') \|| filereadable(l:path . '/mypy.ini')
\|| filereadable(l:path . '/pycodestyle.cfg') \|| filereadable(l:path . '/pycodestyle.cfg')
\|| filereadable(l:path . '/flake8.cfg') \|| filereadable(l:path . '/flake8.cfg')
\|| filereadable(l:path . '/Pipfile')
\|| filereadable(l:path . '/Pipfile.lock')
return l:path return l:path
endif endif
endfor endfor

View File

@ -45,25 +45,23 @@ if !hlexists('ALESignColumnWithErrors')
highlight link ALESignColumnWithErrors error highlight link ALESignColumnWithErrors error
endif endif
function! ale#sign#SetUpDefaultColumnWithoutErrorsHighlight() abort
redir => l:output
0verbose silent highlight SignColumn
redir end
let l:highlight_syntax = join(split(l:output)[2:])
let l:match = matchlist(l:highlight_syntax, '\vlinks to (.+)$')
if !empty(l:match)
execute 'highlight link ALESignColumnWithoutErrors ' . l:match[1]
elseif l:highlight_syntax isnot# 'cleared'
execute 'highlight ALESignColumnWithoutErrors ' . l:highlight_syntax
endif
endfunction
if !hlexists('ALESignColumnWithoutErrors') if !hlexists('ALESignColumnWithoutErrors')
function! s:SetSignColumnWithoutErrorsHighlight() abort call ale#sign#SetUpDefaultColumnWithoutErrorsHighlight()
redir => l:output
silent highlight SignColumn
redir end
let l:highlight_syntax = join(split(l:output)[2:])
let l:match = matchlist(l:highlight_syntax, '\vlinks to (.+)$')
if !empty(l:match)
execute 'highlight link ALESignColumnWithoutErrors ' . l:match[1]
elseif l:highlight_syntax isnot# 'cleared'
execute 'highlight ALESignColumnWithoutErrors ' . l:highlight_syntax
endif
endfunction
call s:SetSignColumnWithoutErrorsHighlight()
delfunction s:SetSignColumnWithoutErrorsHighlight
endif endif
" Signs show up on the left for error markers. " Signs show up on the left for error markers.

View File

@ -268,9 +268,8 @@ endfunction
" See :help sandbox " See :help sandbox
function! ale#util#InSandbox() abort function! ale#util#InSandbox() abort
try try
function! s:SandboxCheck() abort let &equalprg=&equalprg
endfunction catch /E48/
catch /^Vim\%((\a\+)\)\=:E48/
" E48 is the sandbox error. " E48 is the sandbox error.
return 1 return 1
endtry endtry
@ -278,6 +277,25 @@ function! ale#util#InSandbox() abort
return 0 return 0
endfunction endfunction
function! ale#util#Tempname() abort
let l:clear_tempdir = 0
if exists('$TMPDIR') && empty($TMPDIR)
let l:clear_tempdir = 1
let $TMPDIR = '/tmp'
endif
try
let l:name = tempname() " no-custom-checks
finally
if l:clear_tempdir
let $TMPDIR = ''
endif
endtry
return l:name
endfunction
" Given a single line, or a List of lines, and a single pattern, or a List " Given a single line, or a List of lines, and a single pattern, or a List
" of patterns, return all of the matches for the lines(s) from the given " of patterns, return all of the matches for the lines(s) from the given
" patterns, using matchlist(). " patterns, using matchlist().

View File

@ -10,6 +10,7 @@ CONTENTS *ale-development-contents*
2. Design Goals.........................|ale-design-goals| 2. Design Goals.........................|ale-design-goals|
3. Coding Standards.....................|ale-coding-standards| 3. Coding Standards.....................|ale-coding-standards|
4. Testing ALE..........................|ale-development-tests| 4. Testing ALE..........................|ale-development-tests|
4.1. Writing Linter Tests.............|ale-development-linter-tests|
=============================================================================== ===============================================================================
1. Introduction *ale-development-introduction* 1. Introduction *ale-development-introduction*
@ -111,6 +112,9 @@ these are reported with ALE's `custom-linting-rules` script. See
* Don't use the `shellescape()` function. It doesn't escape arguments properly * Don't use the `shellescape()` function. It doesn't escape arguments properly
on Windows. Use `ale#Escape()` instead, which will avoid escaping where it on Windows. Use `ale#Escape()` instead, which will avoid escaping where it
isn't needed, and generally escape arguments better on Windows. isn't needed, and generally escape arguments better on Windows.
* Don't use the `tempname()` function. It doesn't work when `$TMPDIR` isn't
set. Use `ale#util#Tempname()` instead, which temporarily sets `$TMPDIR`
appropriately where needed.
Apply the following guidelines when writing Vader test files. Apply the following guidelines when writing Vader test files.
@ -170,6 +174,9 @@ Look at existing tests in the codebase for examples of how to write tests.
Refer to the Vader documentation for general information on how to write Vader Refer to the Vader documentation for general information on how to write Vader
tests: https://github.com/junegunn/vader.vim tests: https://github.com/junegunn/vader.vim
See |ale-development-linter-tests| for more information on how to write linter
tests.
When you add new linters or fixers, make sure to add them into the table in When you add new linters or fixers, make sure to add them into the table in
the README, and also into the |ale-support| list in the main help file. If you the README, and also into the |ale-support| list in the main help file. If you
forget to keep them both in sync, you should see an error like the following forget to keep them both in sync, you should see an error like the following
@ -219,5 +226,82 @@ margin. For example, if you add a heading for an `aardvark` tool to
Make sure to make the table of contents match the headings, and to keep the Make sure to make the table of contents match the headings, and to keep the
doc tags on the right margin. doc tags on the right margin.
===============================================================================
4.1 Writing Linter Tests *ale-development-linter-tests*
Tests for ALE linters take two forms.
1. Tests for handling the output of commands.
2. Tests for checking which commands are run, or connections are made.
Tests of the first form should go in the `test/handler` directory, and should
be written like so. >
Before:
" Load the file which defines the linter.
runtime ale_linters/filetype/linter_name_here.vim
After:
" Unload all linters again.
call ale#linter#Reset()
Execute(The output should be correct):
" Test that the right loclist items are parsed from the handler.
AssertEqual
\ [
\ {
\ 'lnum': 1,
\ 'type': 'E',
\ 'text': 'Something went wrong',
\ },
\ ],
\ ale_linters#filetype#linter_name#Handle(bufnr(''), [
\ '1:Something went wrong',
\ ]
<
Tests for what ALE runs should go in the `test/command_callback` directory,
and should be written like so. >
Before:
" Load the linter and set up a series of commands, reset linter variables,
" clear caches, etc.
"
" Vader's 'Save' command will be called here for linter variables.
call ale#assert#SetUpLinterTest('filetype', 'linter_name')
After:
" Reset linters, variables, etc.
"
" Vader's 'Restore' command will be called here.
call ale#assert#TearDownLinterTest()
Execute(The default command should be correct):
" AssertLinter checks the executable and command.
" Pass expected_executable, expected_command
AssertLinter 'some-command', ale#Escape('some-command') . ' --foo'
Execute(Check chained commands):
" WithChainResults can be called with 1 or more list for passing output
" to chained commands. The output for each callback defaults to an empty
" list.
WithChainResults ['v2.1.2']
" Given a List of commands, check all of them.
" Given a String, only the last command in the chain will be checked.
AssertLinter 'some-command', [
\ ale#Escape('some-command') . ' --version',
\ ale#Escape('some-command') . ' --foo',
\]
<
The full list of commands that will be temporarily defined for linter tests
given the above setup are as follows.
`WithChainResults [...]` - Define output for command chain functions.
`AssertLinter executable, command` - Check the executable and command.
`AssertLinterNotExecuted` - Check that linters will not be executed.
`AssertLSPLanguage language` - Check the language given to an LSP server.
`AssertLSPOptions options_dict` - Check the options given to an LSP server.
`AssertLSPProject project_root` - Check the root given to an LSP server.
=============================================================================== ===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@ -87,4 +87,19 @@ g:ale_kotlin_ktlint_rulesets *g:ale_kotlin_ktlint_rulesets*
let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom-rulset.jar', let g:ale_kotlin_ktlint_rulesets = ['/path/to/custom-rulset.jar',
'com.ktlint.rulesets:mycustomrule:1.0.0'] 'com.ktlint.rulesets:mycustomrule:1.0.0']
===============================================================================
languageserver *ale-kotlin-languageserver*
g:ale_kotlin_languageserver_executable *g:ale_kotlin_languageserver_executable*
Type: |String|
Default: `''`
The kotlin-language-server executable.
Executables are located inside the bin/ folder of the language server
release.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@ -28,6 +28,33 @@ prettier *ale-markdown-prettier*
See |ale-javascript-prettier| for information about the available options. See |ale-javascript-prettier| for information about the available options.
===============================================================================
remark-lint *ale-markdown-remark-lint*
g:ale_markdown_remark_lint_executable *g:ale_markdown_remark_lint_executable*
*b:ale_markdown_remark_lint_executable*
Type: |String|
Default: `'remark'`
See |ale-integrations-local-executables|
g:ale_markdown_remark_lint_options *g:ale_markdown_remark_lint_options*
*b:ale_markdown_remark_lint_options*
Type: |String|
Default: `''`
This variable can be set to pass additional options to remark-lint.
g:ale_markdown_remark_lint_use_global *g:ale_markdown_remark_lint_use_global*
*b:ale_markdown_remark_lint_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
=============================================================================== ===============================================================================
textlint *ale-markdown-textlint* textlint *ale-markdown-textlint*

View File

@ -22,5 +22,16 @@ g:ale_puppet_puppetlint_options *g:ale_puppet_puppetlint_options*
puppet-lint invocation. puppet-lint invocation.
===============================================================================
puppet-languageserver *ale-puppet-languageserver*
g:ale_puppet_languageserver_executable *g:ale_puppet_languageserver_executable*
*b:ale_puppet_languageserver_executable*
type: |String|
Default: `'puppet-languageserver'`
This variable can be used to specify the executable used for
puppet-languageserver.
=============================================================================== ===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@ -22,6 +22,8 @@ ALE will look for configuration files with the following filenames. >
mypy.ini mypy.ini
pycodestyle.cfg pycodestyle.cfg
flake8.cfg flake8.cfg
Pipfile
Pipfile.lock
< <
The first directory containing any of the files named above will be used. The first directory containing any of the files named above will be used.
@ -145,6 +147,14 @@ g:ale_python_isort_executable *g:ale_python_isort_executable*
See |ale-integrations-local-executables| See |ale-integrations-local-executables|
g:ale_python_isort_options *g:ale_python_isort_options*
*b:ale_python_isort_options*
Type: |String|
Default: `''`
This variable can be set to pass extra options to isort.
g:ale_python_isort_use_global *g:ale_python_isort_use_global* g:ale_python_isort_use_global *g:ale_python_isort_use_global*
*b:ale_python_isort_use_global* *b:ale_python_isort_use_global*
Type: |Number| Type: |Number|

View File

@ -41,8 +41,8 @@ To disable `scalastyle` globally, use |g:ale_linters| like so: >
See |g:ale_linters| for more information on disabling linters. See |g:ale_linters| for more information on disabling linters.
g:ale_scalastyle_config_loc *g:ale_scalastyle_config_loc* g:ale_scala_scalastyle_config *g:ale_scala_scalastyle_config*
*b:ale_scala_scalastyle_config*
Type: |String| Type: |String|
Default: `''` Default: `''`
@ -54,7 +54,7 @@ g:ale_scalastyle_config_loc *g:ale_scalastyle_config_loc*
g:ale_scala_scalastyle_options *g:ale_scala_scalastyle_options* g:ale_scala_scalastyle_options *g:ale_scala_scalastyle_options*
*b:ale_scala_scalastyle_options*
Type: |String| Type: |String|
Default: `''` Default: `''`

View File

@ -7,5 +7,25 @@ prettier *ale-vue-prettier*
See |ale-javascript-prettier| for information about the available options. See |ale-javascript-prettier| for information about the available options.
===============================================================================
vls *ale-vue-vls*
g:ale_vue_vls_executable *g:ale_vue_vls_executable*
*b:ale_vue_vls_executable*
Type: |String|
Default: `'vls'`
See |ale-integrations-local-executables|
g:ale_vue_vls_use_global *g:ale_vue_vls_use_global*
*b:ale_vue_vls_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
=============================================================================== ===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@ -131,6 +131,7 @@ CONTENTS *ale-contents*
kotlin................................|ale-kotlin-options| kotlin................................|ale-kotlin-options|
kotlinc.............................|ale-kotlin-kotlinc| kotlinc.............................|ale-kotlin-kotlinc|
ktlint..............................|ale-kotlin-ktlint| ktlint..............................|ale-kotlin-ktlint|
languageserver......................|ale-kotlin-languageserver|
latex.................................|ale-latex-options| latex.................................|ale-latex-options|
write-good..........................|ale-latex-write-good| write-good..........................|ale-latex-write-good|
less..................................|ale-less-options| less..................................|ale-less-options|
@ -145,6 +146,7 @@ CONTENTS *ale-contents*
markdown..............................|ale-markdown-options| markdown..............................|ale-markdown-options|
mdl.................................|ale-markdown-mdl| mdl.................................|ale-markdown-mdl|
prettier............................|ale-markdown-prettier| prettier............................|ale-markdown-prettier|
remark-lint.........................|ale-markdown-remark-lint|
textlint............................|ale-markdown-textlint| textlint............................|ale-markdown-textlint|
write-good..........................|ale-markdown-write-good| write-good..........................|ale-markdown-write-good|
mercury...............................|ale-mercury-options| mercury...............................|ale-mercury-options|
@ -186,6 +188,7 @@ CONTENTS *ale-contents*
puglint.............................|ale-pug-puglint| puglint.............................|ale-pug-puglint|
puppet................................|ale-puppet-options| puppet................................|ale-puppet-options|
puppetlint..........................|ale-puppet-puppetlint| puppetlint..........................|ale-puppet-puppetlint|
puppet-languageserver...............|ale-puppet-languageserver|
pyrex (cython)........................|ale-pyrex-options| pyrex (cython)........................|ale-pyrex-options|
cython..............................|ale-pyrex-cython| cython..............................|ale-pyrex-cython|
python................................|ale-python-options| python................................|ale-python-options|
@ -273,6 +276,7 @@ CONTENTS *ale-contents*
write-good..........................|ale-vim-help-write-good| write-good..........................|ale-vim-help-write-good|
vue...................................|ale-vue-options| vue...................................|ale-vue-options|
prettier............................|ale-vue-prettier| prettier............................|ale-vue-prettier|
vls.................................|ale-vue-vls|
xhtml.................................|ale-xhtml-options| xhtml.................................|ale-xhtml-options|
write-good..........................|ale-xhtml-write-good| write-good..........................|ale-xhtml-write-good|
xml...................................|ale-xml-options| xml...................................|ale-xml-options|
@ -366,7 +370,7 @@ Notes:
* Java: `checkstyle`, `javac`, `google-java-format`, `PMD` * Java: `checkstyle`, `javac`, `google-java-format`, `PMD`
* JavaScript: `eslint`, `flow`, `jscs`, `jshint`, `prettier`, `prettier-eslint`, `prettier-standard`, `standard`, `xo` * JavaScript: `eslint`, `flow`, `jscs`, `jshint`, `prettier`, `prettier-eslint`, `prettier-standard`, `standard`, `xo`
* JSON: `fixjson`, `jsonlint`, `jq`, `prettier` * JSON: `fixjson`, `jsonlint`, `jq`, `prettier`
* Kotlin: `kotlinc`, `ktlint` * Kotlin: `kotlinc`!!, `ktlint`!!, `languageserver`
* LaTeX (tex): `alex`!!, `chktex`, `lacheck`, `proselint`, `redpen`, `vale`, `write-good` * LaTeX (tex): `alex`!!, `chktex`, `lacheck`, `proselint`, `redpen`, `vale`, `write-good`
* Less: `lessc`, `prettier`, `stylelint` * Less: `lessc`, `prettier`, `stylelint`
* LLVM: `llc` * LLVM: `llc`
@ -390,7 +394,7 @@ Notes:
* Pony: `ponyc` * Pony: `ponyc`
* proto: `protoc-gen-lint` * proto: `protoc-gen-lint`
* Pug: `pug-lint` * Pug: `pug-lint`
* Puppet: `puppet`, `puppet-lint` * Puppet: `languageserver`, `puppet`, `puppet-lint`
* Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pyls`, `pyre`, `pylint`!!, `yapf` * Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pyls`, `pyre`, `pylint`!!, `yapf`
* QML: `qmlfmt`, `qmllint` * QML: `qmlfmt`, `qmllint`
* R: `lintr` * R: `lintr`
@ -418,7 +422,7 @@ Notes:
* Verilog: `iverilog`, `verilator` * Verilog: `iverilog`, `verilator`
* Vim: `vint` * Vim: `vint`
* Vim help^: `alex`!!, `proselint`, `write-good` * Vim help^: `alex`!!, `proselint`, `write-good`
* Vue: `prettier` * Vue: `prettier`, `vls`
* XHTML: `alex`!!, `proselint`, `write-good` * XHTML: `alex`!!, `proselint`, `write-good`
* XML: `xmllint` * XML: `xmllint`
* YAML: `swaglint`, `yamllint` * YAML: `swaglint`, `yamllint`
@ -444,14 +448,20 @@ have even saved your changes. ALE will check your code in the following
circumstances, which can be configured with the associated options. circumstances, which can be configured with the associated options.
* When you modify a buffer. - |g:ale_lint_on_text_changed| * When you modify a buffer. - |g:ale_lint_on_text_changed|
* On leaving insert mode. (off by default) - |g:ale_lint_on_insert_leave|
* When you open a new or modified buffer. - |g:ale_lint_on_enter| * When you open a new or modified buffer. - |g:ale_lint_on_enter|
* When you save a buffer. - |g:ale_lint_on_save| * When you save a buffer. - |g:ale_lint_on_save|
* When the filetype changes for a buffer. - |g:ale_lint_on_filetype_changed| * When the filetype changes for a buffer. - |g:ale_lint_on_filetype_changed|
* If ALE is used to check code manually. - |:ALELint| * If ALE is used to check code manually. - |:ALELint|
In addition to the above options, ALE can also check buffers for errors when *ale-lint-settings-on-startup*
you leave insert mode with |g:ale_lint_on_insert_leave|, which is off by
default. It is worth reading the documentation for every option. It is worth reading the documentation for every option. You should configure
which events ALE will use before ALE is loaded, so it can optimize which
autocmd commands to run. You can force autocmd commands to be reloaded with
`:ALEDisable | ALEEnable`
This also applies to the autocmd commands used for |g:ale_echo_cursor|.
*ale-lint-file-linters* *ale-lint-file-linters*
@ -641,9 +651,6 @@ ALE supports the following LSP/tsserver features.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
5.1 Completion *ale-completion* 5.1 Completion *ale-completion*
NOTE: At the moment, only `tsserver` for TypeScript code is supported for
completion.
ALE offers limited support for automatic completion of code while you type. ALE offers limited support for automatic completion of code while you type.
Completion is only supported while a least one LSP linter is enabled. ALE Completion is only supported while a least one LSP linter is enabled. ALE
will only suggest symbols provided by the LSP servers. will only suggest symbols provided by the LSP servers.
@ -842,6 +849,9 @@ g:ale_echo_cursor *g:ale_echo_cursor*
this behaviour. this behaviour.
The format of the message can be customizable in |g:ale_echo_msg_format|. The format of the message can be customizable in |g:ale_echo_msg_format|.
You should set this setting once before ALE is loaded, and restart Vim if
you want to change your preferences. See |ale-lint-settings-on-startup|.
g:ale_echo_delay *g:ale_echo_delay* g:ale_echo_delay *g:ale_echo_delay*
*b:ale_echo_delay* *b:ale_echo_delay*
@ -1042,19 +1052,16 @@ g:ale_lint_on_enter *g:ale_lint_on_enter*
Type: |Number| Type: |Number|
Default: `1` Default: `1`
When this option is set to `1`, the |BufWinEnter| and |BufRead| events will When this option is set to `1`, the |BufWinEnter| event will be used to
be used to apply linters when buffers are first opened. If this is not apply linters when buffers are first opened. If this is not desired, this
desired, this variable can be set to `0` in your vimrc file to disable this variable can be set to `0` in your vimrc file to disable this behavior.
behaviour.
The |FileChangedShellPost| and |BufEnter| events will be used to check if The |FileChangedShellPost| and |BufEnter| events will be used to check if
files have been changed outside of Vim. If a file is changed outside of files have been changed outside of Vim. If a file is changed outside of
Vim, it will be checked when it is next opened. Vim, it will be checked when it is next opened.
A |BufWinLeave| event will be used to look for the |E924|, |E925|, or |E926| You should set this setting once before ALE is loaded, and restart Vim if
errors after moving from a loclist or quickfix window to a new buffer. If you want to change your preferences. See |ale-lint-settings-on-startup|.
prompts for these errors are opened after moving to new buffers, then ALE
will automatically send the `<CR>` key needed to close the prompt.
g:ale_lint_on_filetype_changed *g:ale_lint_on_filetype_changed* g:ale_lint_on_filetype_changed *g:ale_lint_on_filetype_changed*
@ -1062,14 +1069,13 @@ g:ale_lint_on_filetype_changed *g:ale_lint_on_filetype_changed*
Type: |Number| Type: |Number|
Default: `1` Default: `1`
This option will cause ALE to run whenever the filetype is changed. A short This option will cause ALE to run when the filetype for a file is changed
delay will be used before linting will be done, so the filetype can be after a buffer has first been loaded. A short delay will be used before
changed quickly several times in a row, but resulting in only one lint linting will be done, so the filetype can be changed quickly several times
cycle. in a row, but resulting in only one lint cycle.
If |g:ale_lint_on_enter| is set to `0`, then ALE will not lint a file when You should set this setting once before ALE is loaded, and restart Vim if
the filetype is initially set. Otherwise ALE would still lint files when you want to change your preferences. See |ale-lint-settings-on-startup|.
buffers are opened, and the option for doing so is turned off.
g:ale_lint_on_save *g:ale_lint_on_save* g:ale_lint_on_save *g:ale_lint_on_save*
@ -1087,17 +1093,22 @@ g:ale_lint_on_save *g:ale_lint_on_save*
g:ale_lint_on_text_changed *g:ale_lint_on_text_changed* g:ale_lint_on_text_changed *g:ale_lint_on_text_changed*
Type: |String| Type: |String|
Default: `always` Default: `'always'`
By default, ALE will check files with the various supported programs when This option controls how ALE will check your files as you make changes.
text is changed by using the |TextChanged| event. If this behaviour is not The following values can be used.
desired, then this option can be disabled by setting it to `never`. The
|g:ale_lint_delay| variable will be used to set a |timer_start()| on a `'always'`, `'1'`, or `1` - Check buffers on |TextChanged| or |TextChangedI|.
delay, and each change to a file will continue to call |timer_stop()| and `'normal'` - Check buffers only on |TextChanged|.
|timer_start()| repeatedly until the timer ticks by, and the linters will be `'insert'` - Check buffers only on |TextChangedI|.
run. The checking of files will run in the background, so it should not `'never'`, `'0'`, or `0` - Never check buffers on changes.
inhibit editing files. This option can also be set to `insert` or `normal`
to lint when text is changed only in insert or normal mode respectively. ALE will check buffers after a short delay, with a timer which resets on
each change. The delay can be configured by adjusting the |g:ale_lint_delay|
variable.
You should set this setting once before ALE is loaded, and restart Vim if
you want to change your preferences. See |ale-lint-settings-on-startup|.
g:ale_lint_on_insert_leave *g:ale_lint_on_insert_leave* g:ale_lint_on_insert_leave *g:ale_lint_on_insert_leave*
@ -1115,6 +1126,9 @@ g:ale_lint_on_insert_leave *g:ale_lint_on_insert_leave*
" Make using Ctrl+C do the same as Escape, to trigger autocmd commands " Make using Ctrl+C do the same as Escape, to trigger autocmd commands
inoremap <C-c> <Esc> inoremap <C-c> <Esc>
< <
You should set this setting once before ALE is loaded, and restart Vim if
you want to change your preferences. See |ale-lint-settings-on-startup|.
g:ale_linter_aliases *g:ale_linter_aliases* g:ale_linter_aliases *g:ale_linter_aliases*
*b:ale_linter_aliases* *b:ale_linter_aliases*
@ -1360,7 +1374,7 @@ g:ale_open_list *g:ale_open_list*
g:ale_pattern_options *g:ale_pattern_options* g:ale_pattern_options *g:ale_pattern_options*
Type: |Dictionary| Type: |Dictionary|
Default: `{}` Default: undefined
This option maps regular expression patterns to |Dictionary| values for This option maps regular expression patterns to |Dictionary| values for
buffer variables. This option can be set to automatically configure buffer variables. This option can be set to automatically configure
@ -1389,12 +1403,10 @@ g:ale_pattern_options *g:ale_pattern_options*
g:ale_pattern_options_enabled *g:ale_pattern_options_enabled* g:ale_pattern_options_enabled *g:ale_pattern_options_enabled*
Type: |Number| Type: |Number|
Default: `!empty(g:ale_pattern_options)` Default: undefined
This option can be used for turning the behaviour of setting This option can be used for disabling pattern options. If set to `0`, ALE
|g:ale_pattern_options| on or off. By default, setting a single key for will not set buffer variables per |g:ale_pattern_options|.
|g:ale_pattern_options| will turn this option on, as long as the setting is
configured before ALE is loaded.
g:ale_set_balloons *g:ale_set_balloons* g:ale_set_balloons *g:ale_set_balloons*
@ -1636,7 +1648,7 @@ g:ale_virtualenv_dir_names *g:ale_virtualenv_dir_names*
b:ale_virtualenv_dir_names *b:ale_virtualenv_dir_names* b:ale_virtualenv_dir_names *b:ale_virtualenv_dir_names*
Type: |List| Type: |List|
Default: `['.env', 'env', 've-py3', 've', 'virtualenv', 'venv']` Default: `['.env', '.venv', 'env', 've-py3', 've', 'virtualenv', 'venv']`
A list of directory names to be used when searching upwards from Python A list of directory names to be used when searching upwards from Python
files to discover virtulenv directories with. files to discover virtulenv directories with.
@ -2075,6 +2087,29 @@ ALEStopAllLSPs *ALEStopAllLSPs*
=============================================================================== ===============================================================================
9. API *ale-api* 9. API *ale-api*
ALE offers a number of functions for running linters or fixers, or defining
them. The following functions are part of the publicly documented part of that
API, and should be expected to continue to work.
ale#Env(variable_name, value) *ale#Env()*
Given a variable name and a string value, produce a string for including in
a command for setting environment variables. This function can be used for
building a command like so. >
:echo string(ale#Env('VAR', 'some value') . 'command')
'VAR=''some value'' command' # On Linux or Mac OSX
'set VAR="some value" && command' # On Windows
ale#Pad(string) *ale#Pad()*
Given a string or any |empty()| value, return either the string prefixed
with a single space, or an empty string. This function can be used to build
parts of a command from variables.
ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()* ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()*
Run linters for the current buffer, based on the filetype of the buffer, Run linters for the current buffer, based on the filetype of the buffer,
@ -2099,8 +2134,17 @@ ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()*
ale#engine#CreateDirectory(buffer) *ale#engine#CreateDirectory()* ale#engine#CreateDirectory(buffer) *ale#engine#CreateDirectory()*
Create a new temporary directory with a unique name, and manage that Create a new temporary directory with a unique name, and manage that
directory with |ale#engine#ManageDirectory()|, so it will be removed as directory with |ale#engine#ManageDirectory()|, so it will be removed as soon
soon as possible. as possible.
It is advised to only call this function from a callback function for
returning a linter command to run.
ale#engine#CreateFile(buffer) *ale#engine#CreateFile()*
Create a new temporary file with a unique name, and manage that file with
|ale#engine#ManageFile()|, so it will be removed as soon as possible.
It is advised to only call this function from a callback function for It is advised to only call this function from a callback function for
returning a linter command to run. returning a linter command to run.
@ -2206,6 +2250,10 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
defined, as LSP linters handle diagnostics defined, as LSP linters handle diagnostics
automatically. See |ale-lsp-linters|. automatically. See |ale-lsp-linters|.
If the function named does not exist, including if
the function is later deleted, ALE will behave as if
the callback returned an empty list.
The keys for each item in the List will be handled in The keys for each item in the List will be handled in
the following manner: the following manner:
*ale-loclist-format* *ale-loclist-format*
@ -2383,9 +2431,12 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
with a callback returning an address to connect to. with a callback returning an address to connect to.
ALE will not start a server automatically. ALE will not start a server automatically.
When this argument is not empty, only one of either When this argument is not empty
`language` or `language_callback` must be defined, `project_root_callback` must be defined.
and `project_root_callback` must be defined.
`language` or `language_callback` can be defined to
describe the language for a file. The filetype will
be used as the language by default.
LSP linters handle diagnostics automatically, so LSP linters handle diagnostics automatically, so
the `callback` argument must not be defined. the `callback` argument must not be defined.
@ -2418,8 +2469,9 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
being checked. This string will be sent to the LSP to being checked. This string will be sent to the LSP to
tell it what type of language is being checked. tell it what type of language is being checked.
This argument must only be set if the `lsp` argument If this or `language_callback` isn't set, the
is also set to a non-empty string. language will default to the value of the filetype
given to |ale#linter#Define|.
`language_callback` A |String| or |Funcref| for a callback function `language_callback` A |String| or |Funcref| for a callback function
accepting a buffer number. A |String| should be accepting a buffer number. A |String| should be
@ -2489,15 +2541,23 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
For example: > For example: >
'command': 'ghc -fno-code -v0 %t', 'command': 'ghc -fno-code -v0 %t',
<
Any substring `%e` will be replaced with the escaped executable supplied
with `executable` or `executable_callback`. This provides a convenient way
to define a command string which needs to include a dynamic executable name,
but which is otherwise static.
For example: >
'command': '%e --some-argument',
< <
The character sequence `%%` can be used to emit a literal `%` into a The character sequence `%%` can be used to emit a literal `%` into a
command, so literal character sequences `%s` and `%t` can be escaped by command, so literal character sequences `%s` and `%t` can be escaped by
using `%%s` and `%%t` instead, etc. using `%%s` and `%%t` instead, etc.
If a callback for a command generates part of a command string which might If a callback for a command generates part of a command string which might
possibly contain `%%`, `%s`, or `%t` where the special formatting behaviour possibly contain `%%`, `%s`, `%t`, or `%e`, where the special formatting
is not desired, the |ale#engine#EscapeCommandPart()| function can be used to behavior is not desired, the |ale#engine#EscapeCommandPart()| function can
replace those characters to avoid formatting issues. be used to replace those characters to avoid formatting issues.
*ale-linter-loading-behavior* *ale-linter-loading-behavior*
*ale-linter-loading-behaviour* *ale-linter-loading-behaviour*

View File

@ -35,15 +35,11 @@ endif
" Set this flag so that other plugins can use it, like airline. " Set this flag so that other plugins can use it, like airline.
let g:loaded_ale = 1 let g:loaded_ale = 1
" Set the TMPDIR environment variable if it is not set automatically.
" This can automatically fix some environments.
if has('unix') && empty($TMPDIR)
let $TMPDIR = '/tmp'
endif
" This global variable is used internally by ALE for tracking information for " This global variable is used internally by ALE for tracking information for
" each buffer which linters are being run against. " each buffer which linters are being run against.
let g:ale_buffer_info = {} let g:ale_buffer_info = {}
" This global Dictionary tracks data for fixing code. Don't mess with it.
let g:ale_fix_buffer_data = {}
" User Configuration " User Configuration

View File

@ -92,7 +92,7 @@ add the following configuration to your `.vimrc`.
set laststatus=2 set laststatus=2
``` ```
If the statusline does not be coloured like If the statusline is not coloured like
![lightline.vim - tutorial](https://raw.githubusercontent.com/wiki/itchyny/lightline.vim/image/tutorial/20.png) ![lightline.vim - tutorial](https://raw.githubusercontent.com/wiki/itchyny/lightline.vim/image/tutorial/20.png)
then modify `TERM` in your shell configuration (`.zshrc` for example) then modify `TERM` in your shell configuration (`.zshrc` for example)

View File

@ -18,15 +18,13 @@ uncomments a set of adjacent commented lines.
## Installation ## Installation
If you don't have a preferred installation method, I recommend Install using your favorite package manager, or use Vim's built-in package
installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and support:
then simply copy and paste:
cd ~/.vim/bundle mkdir -p ~/.vim/pack/tpope/start
git clone git://github.com/tpope/vim-commentary.git cd ~/.vim/pack/tpope/start
git clone https://tpope.io/vim/commentary.git
Once help tags have been generated, you can view the manual with vim -u NONE -c "helptags commentary/doc" -c q
`:help commentary`.
## FAQ ## FAQ

View File

@ -93,7 +93,7 @@ command! -range -bar Commentary call s:go(<line1>,<line2>)
xnoremap <expr> <Plug>Commentary <SID>go() xnoremap <expr> <Plug>Commentary <SID>go()
nnoremap <expr> <Plug>Commentary <SID>go() nnoremap <expr> <Plug>Commentary <SID>go()
nnoremap <expr> <Plug>CommentaryLine <SID>go() . '_' nnoremap <expr> <Plug>CommentaryLine <SID>go() . '_'
onoremap <silent> <Plug>Commentary :<C-U>call <SID>textobject(0)<CR> onoremap <silent> <Plug>Commentary :<C-U>call <SID>textobject(get(v:, 'operator', '') ==# 'c')<CR>
nnoremap <silent> <Plug>ChangeCommentary c:<C-U>call <SID>textobject(1)<CR> nnoremap <silent> <Plug>ChangeCommentary c:<C-U>call <SID>textobject(1)<CR>
nmap <silent> <Plug>CommentaryUndo :echoerr "Change your <Plug>CommentaryUndo map to <Plug>Commentary<Plug>Commentary"<CR> nmap <silent> <Plug>CommentaryUndo :echoerr "Change your <Plug>CommentaryUndo map to <Plug>Commentary<Plug>Commentary"<CR>
@ -102,7 +102,7 @@ if !hasmapto('<Plug>Commentary') || maparg('gc','n') ==# ''
nmap gc <Plug>Commentary nmap gc <Plug>Commentary
omap gc <Plug>Commentary omap gc <Plug>Commentary
nmap gcc <Plug>CommentaryLine nmap gcc <Plug>CommentaryLine
if maparg('c','n') ==# '' if maparg('c','n') ==# '' && !exists('v:operator')
nmap cgc <Plug>ChangeCommentary nmap cgc <Plug>ChangeCommentary
endif endif
nmap gcu <Plug>Commentary<Plug>Commentary nmap gcu <Plug>Commentary<Plug>Commentary

View File

@ -46,6 +46,14 @@ function! s:fnameescape(file) abort
endif endif
endfunction endfunction
function! s:tempname() abort
let temp = resolve(tempname())
if has('win32')
let temp = fnamemodify(fnamemodify(temp, ':h'), ':p').fnamemodify(temp, ':t')
endif
return temp
endfunction
function! s:throw(string) abort function! s:throw(string) abort
let v:errmsg = 'fugitive: '.a:string let v:errmsg = 'fugitive: '.a:string
throw v:errmsg throw v:errmsg
@ -83,12 +91,21 @@ function! s:executable(binary) abort
return s:executables[a:binary] return s:executables[a:binary]
endfunction endfunction
let s:git_versions = {}
function! s:git_command() abort function! s:git_command() abort
return get(g:, 'fugitive_git_command', g:fugitive_git_executable) return get(g:, 'fugitive_git_command', g:fugitive_git_executable)
endfunction endfunction
function! fugitive#Prepare(...) abort
let args = copy(a:000)
if empty(args)
return g:fugitive_git_executable
elseif args[0] !~# '^-' && args[0] =~# '[\/.]'
let args[0] = '--git-dir=' . args[0]
endif
return g:fugitive_git_executable . ' ' . join(map(args, 's:shellesc(v:val)'), ' ')
endfunction
let s:git_versions = {}
function! fugitive#GitVersion(...) abort function! fugitive#GitVersion(...) abort
if !has_key(s:git_versions, g:fugitive_git_executable) if !has_key(s:git_versions, g:fugitive_git_executable)
let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), "\\S\\+\\ze\n") let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), "\\S\\+\\ze\n")
@ -173,7 +190,7 @@ function! fugitive#Init() abort
call s:map('n', 'y<C-G>', ':call setreg(v:register, <SID>recall())<CR>', '<silent>') call s:map('n', 'y<C-G>', ':call setreg(v:register, <SID>recall())<CR>', '<silent>')
endif endif
let buffer = fugitive#buffer() let buffer = fugitive#buffer()
if expand('%:p') =~# '://' if expand('%:p') =~# ':[\/][\/]'
call buffer.setvar('&path', s:sub(buffer.getvar('&path'), '^\.%(,|$)', '')) call buffer.setvar('&path', s:sub(buffer.getvar('&path'), '^\.%(,|$)', ''))
endif endif
if stridx(buffer.getvar('&tags'), escape(b:git_dir, ', ')) == -1 if stridx(buffer.getvar('&tags'), escape(b:git_dir, ', ')) == -1
@ -315,9 +332,7 @@ function! s:repo_git_command(...) dict abort
endfunction endfunction
function! s:repo_git_chomp(...) dict abort function! s:repo_git_chomp(...) dict abort
let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir) return s:sub(system(call('fugitive#Prepare', [self.git_dir] + a:000)),'\n$','')
let output = git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
return s:sub(system(output),'\n$','')
endfunction endfunction
function! s:repo_git_chomp_in_tree(...) dict abort function! s:repo_git_chomp_in_tree(...) dict abort
@ -429,11 +444,16 @@ call s:add_methods('repo',['keywordprg'])
" Section: Buffer " Section: Buffer
function! s:DirCommitFile(path) abort function! s:DirCommitFile(path) abort
let vals = matchlist(s:shellslash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\w\+\)\(/.*\)\=$') let vals = matchlist(s:shellslash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40\}\|[0-3]\)\(/.*\)\=$')
if empty(vals) if empty(vals)
return ['', '', ''] return ['', '', '']
endif endif
return [vals[1], (vals[2] =~# '^.$' ? ':' : '') . vals[2], vals[3]] return vals[1:3]
endfunction
function! s:DirRev(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
return [dir, (commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')]
endfunction endfunction
function! fugitive#Path(url) abort function! fugitive#Path(url) abort
@ -454,7 +474,7 @@ endfunction
let s:trees = {} let s:trees = {}
let s:indexes = {} let s:indexes = {}
function! s:TreeInfo(dir, commit) abort function! s:TreeInfo(dir, commit) abort
let git = g:fugitive_git_executable . ' --git-dir=' . s:shellesc(a:dir) let git = fugitive#Prepare(a:dir)
if a:commit =~# '^:\=[0-3]$' if a:commit =~# '^:\=[0-3]$'
let index = get(s:indexes, a:dir, []) let index = get(s:indexes, a:dir, [])
let newftime = getftime(a:dir . '/index') let newftime = getftime(a:dir . '/index')
@ -547,7 +567,7 @@ function! fugitive#getfsize(url) abort
let entry = s:PathInfo(a:url) let entry = s:PathInfo(a:url)
if entry[4] == -2 && entry[2] ==# 'blob' && len(entry[3]) if entry[4] == -2 && entry[2] ==# 'blob' && len(entry[3])
let dir = s:DirCommitFile(a:url)[0] let dir = s:DirCommitFile(a:url)[0]
let size = +system(g:fugitive_git_executable . ' ' . s:shellesc('--git-dir=' . dir) . ' cat-file -s ' . entry[3]) let size = +system(fugitive#Prepare(dir, 'cat-file', '-s', entry[3]))
let entry[4] = v:shell_error ? -1 : size let entry[4] = v:shell_error ? -1 : size
endif endif
return entry[4] return entry[4]
@ -572,9 +592,8 @@ function! fugitive#readfile(url, ...) abort
if entry[2] !=# 'blob' if entry[2] !=# 'blob'
return [] return []
endif endif
let [dir, commit, file] = s:DirCommitFile(a:url) let [dir, rev] = s:DirRev(a:url)
let cmd = g:fugitive_git_executable . ' --git-dir=' . s:shellesc(dir) . let cmd = fugitive#Prepare(dir, 'cat-file', 'blob', rev)
\ ' cat-file blob ' . s:shellesc(commit . ':' . file[1:-1])
if max > 0 && s:executable('head') if max > 0 && s:executable('head')
let cmd .= '|head -' . max let cmd .= '|head -' . max
endif endif
@ -634,7 +653,7 @@ function! s:buffer(...) abort
if buffer.getvar('git_dir') !=# '' if buffer.getvar('git_dir') !=# ''
return buffer return buffer
endif endif
call s:throw('not a git repository: '.expand('%:p')) call s:throw('not a git repository: '.bufname(buffer['#']))
endfunction endfunction
function! fugitive#buffer(...) abort function! fugitive#buffer(...) abort
@ -658,9 +677,9 @@ function! s:buffer_repo() dict abort
endfunction endfunction
function! s:buffer_type(...) dict abort function! s:buffer_type(...) dict abort
if self.getvar('fugitive_type') != '' if !empty(self.getvar('fugitive_type'))
let type = self.getvar('fugitive_type') let type = self.getvar('fugitive_type')
elseif fnamemodify(self.spec(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$' elseif fnamemodify(self.spec(),':p') =~# '\.git/refs/\|\.git/\w*HEAD$'
let type = 'head' let type = 'head'
elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == '' elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == ''
let type = 'tree' let type = 'tree'
@ -843,9 +862,9 @@ function! s:Git(bang, mods, args) abort
let git .= ' --no-pager' let git .= ' --no-pager'
endif endif
let args = matchstr(a:args,'\v\C.{-}%($|\\@<!%(\\\\)*\|)@=') let args = matchstr(a:args,'\v\C.{-}%($|\\@<!%(\\\\)*\|)@=')
if exists(':terminal') && has('nvim') if exists(':terminal') && has('nvim') && !get(g:, 'fugitive_force_bang_command')
let dir = s:repo().tree() let dir = s:repo().tree()
if expand('%') != '' if len(@%)
-tabedit % -tabedit %
else else
-tabnew -tabnew
@ -1239,7 +1258,14 @@ function! s:Commit(mods, args, ...) abort
let args = a:args let args = a:args
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[esp]|--edit|--interactive|--patch|--signoff)%($| )','') let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[esp]|--edit|--interactive|--patch|--signoff)%($| )','')
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-c|--reedit-message|--reuse-message|-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','') let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-c|--reedit-message|--reuse-message|-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','')
let args = s:gsub(args,'%(^| )@<=[%#]%(:\w)*','\=expand(submatch(0))') let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
try
exe cd s:fnameescape(repo.tree())
let args = s:gsub(args,'\\@<!(\%|##=|#\<=\d+)(:\w)*','\=fnamemodify(FugitivePath(expand(submatch(1))),":." . submatch(2))')
finally
exe cd cwd
endtry
let args = s:sub(args, '\ze -- |$', ' --no-edit --no-interactive --no-signoff') let args = s:sub(args, '\ze -- |$', ' --no-edit --no-interactive --no-signoff')
let args = '-F '.s:shellesc(msgfile).' '.args let args = '-F '.s:shellesc(msgfile).' '.args
if args !~# '\%(^\| \)--cleanup\>' if args !~# '\%(^\| \)--cleanup\>'
@ -1560,10 +1586,7 @@ function! s:Edit(cmd, bang, mods, ...) abort
diffupdate diffupdate
return 'redraw|echo '.string(':!'.git.' '.args) return 'redraw|echo '.string(':!'.git.' '.args)
else else
let temp = resolve(tempname()) let temp = s:tempname()
if has('win32')
let temp = fnamemodify(fnamemodify(temp, ':h'), ':p').fnamemodify(temp, ':t')
endif
let s:temp_files[s:cpath(temp)] = { 'dir': buffer.repo().dir(), 'args': arglist } let s:temp_files[s:cpath(temp)] = { 'dir': buffer.repo().dir(), 'args': arglist }
silent execute mods a:cmd temp silent execute mods a:cmd temp
if a:cmd =~# 'pedit' if a:cmd =~# 'pedit'
@ -2113,7 +2136,7 @@ augroup fugitive_blame
autocmd! autocmd!
autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif
autocmd Syntax fugitiveblame call s:BlameSyntax() autocmd Syntax fugitiveblame call s:BlameSyntax()
autocmd User Fugitive if s:buffer().type('file', 'blob') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(<bang>0,<line1>,<line2>,<count>,[<f-args>])" | endif autocmd User Fugitive if s:buffer().type('file', 'blob', 'blame') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(<bang>0,<line1>,<line2>,<count>,[<f-args>])" | endif
autocmd ColorScheme,GUIEnter * call s:RehighlightBlame() autocmd ColorScheme,GUIEnter * call s:RehighlightBlame()
autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave') autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
augroup END augroup END
@ -2157,7 +2180,7 @@ function! s:Blame(bang,line1,line2,count,args) abort
if a:count if a:count
execute 'write !'.substitute(basecmd,' blame ',' blame -L '.a:line1.','.a:line2.' ','g') execute 'write !'.substitute(basecmd,' blame ',' blame -L '.a:line1.','.a:line2.' ','g')
else else
let error = resolve(tempname()) let error = s:tempname()
let temp = error.'.fugitiveblame' let temp = error.'.fugitiveblame'
if &shell =~# 'csh' if &shell =~# 'csh'
silent! execute '%write !('.basecmd.' > '.temp.') >& '.error silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
@ -2197,12 +2220,10 @@ function! s:Blame(bang,line1,line2,count,args) abort
endif endif
let top = line('w0') + &scrolloff let top = line('w0') + &scrolloff
let current = line('.') let current = line('.')
if has('win32')
let temp = fnamemodify(fnamemodify(temp, ':h'), ':p').fnamemodify(temp, ':t')
endif
let s:temp_files[s:cpath(temp)] = { 'dir': s:repo().dir(), 'args': cmd } let s:temp_files[s:cpath(temp)] = { 'dir': s:repo().dir(), 'args': cmd }
exe 'keepalt leftabove vsplit '.temp exe 'keepalt leftabove vsplit '.temp
let b:fugitive_blamed_bufnr = bufnr let b:fugitive_blamed_bufnr = bufnr
let b:fugitive_type = 'blame'
let w:fugitive_leave = restore let w:fugitive_leave = restore
let b:fugitive_blame_arguments = join(a:args,' ') let b:fugitive_blame_arguments = join(a:args,' ')
execute top execute top
@ -2633,12 +2654,10 @@ call extend(g:fugitive_browse_handlers,
" Section: File access " Section: File access
function! s:ReplaceCmd(cmd,...) abort function! s:WriteCmd(out, cmd, ...) abort
let fn = expand('%:p')
let tmp = tempname()
let prefix = '' let prefix = ''
try try
if a:0 && a:1 != '' if a:0 && len(a:1)
if s:winshell() if s:winshell()
let old_index = $GIT_INDEX_FILE let old_index = $GIT_INDEX_FILE
let $GIT_INDEX_FILE = a:1 let $GIT_INDEX_FILE = a:1
@ -2646,23 +2665,29 @@ function! s:ReplaceCmd(cmd,...) abort
let prefix = 'env GIT_INDEX_FILE='.s:shellesc(a:1).' ' let prefix = 'env GIT_INDEX_FILE='.s:shellesc(a:1).' '
endif endif
endif endif
let redir = ' > '.tmp let redir = ' > '.a:out
if &shellpipe =~ '2>&1'
let redir .= ' 2>&1'
endif
if s:winshell() if s:winshell()
let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^' let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^'
call system('cmd /c "'.prefix.s:gsub(a:cmd,'[<>]', cmd_escape_char.'&').redir.'"') return system('cmd /c "'.prefix.s:gsub(a:cmd,'[<>]', cmd_escape_char.'&').redir.'"')
elseif &shell =~# 'fish' elseif &shell =~# 'fish'
call system(' begin;'.prefix.a:cmd.redir.';end ') return system(' begin;'.prefix.a:cmd.redir.';end ')
else else
call system(' ('.prefix.a:cmd.redir.') ') return system(' ('.prefix.a:cmd.redir.') ')
endif endif
finally finally
if exists('old_index') if exists('old_index')
let $GIT_INDEX_FILE = old_index let $GIT_INDEX_FILE = old_index
endif endif
endtry endtry
endfunction
function! s:ReplaceCmd(cmd, ...) abort
let tmp = tempname()
let err = s:WriteCmd(tmp, a:cmd, a:0 ? a:1 : '')
if v:shell_error
call s:throw((len(err) ? err : filereadable(tmp) ? join(readfile(tmp), ' ') : 'unknown error running ' . a:cmd))
endif
let fn = expand('%:p')
silent exe 'doau BufReadPre '.s:fnameescape(fn) silent exe 'doau BufReadPre '.s:fnameescape(fn)
silent exe 'keepalt file '.tmp silent exe 'keepalt file '.tmp
try try
@ -2729,10 +2754,10 @@ function! fugitive#BufReadStatus() abort
nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>StagePrevious(v:count1)<CR> nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>StagePrevious(v:count1)<CR>
nnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR> nnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR>
xnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line("'<"),line("'>"))<CR> xnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line("'<"),line("'>"))<CR>
nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe fugitive#BufReadIndex()<CR> nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe fugitive#BufReadStatus()<CR>
nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe fugitive#BufReadIndex()<CR> nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe fugitive#BufReadStatus()<CR>
nnoremap <buffer> <silent> C :<C-U>Gcommit<CR>:echohl WarningMsg<Bar>echo ':Gstatus C is deprecated in favor of cc'<Bar>echohl NONE<CR> nnoremap <buffer> <silent> C :<C-U>Gcommit<CR>:echohl WarningMsg<Bar>echo ':Gstatus C is deprecated in favor of cc'<Bar>echohl NONE<CR>
nnoremap <buffer> <silent> cA :<C-U>Gcommit --amend --reuse-message=HEAD<CR>:echohl WarningMsg<Bar>echo ':Gstatus cA is deprecated in favor of ce'<CR> nnoremap <buffer> <silent> cA :<C-U>Gcommit --amend --reuse-message=HEAD<CR>:echohl WarningMsg<Bar>echo ':Gstatus cA is deprecated in favor of ce'<Bar>echohl NONE<CR>
nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR> nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR> nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR> nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR>
@ -2761,21 +2786,16 @@ function! fugitive#BufReadStatus() abort
endfunction endfunction
function! fugitive#FileRead() abort function! fugitive#FileRead() abort
try let [dir, rev] = s:DirRev(expand('<amatch>'))
let [dir, commit, file] = s:DirCommitFile(expand('<amatch>')) if empty(dir)
let repo = s:repo(dir) return "noautocmd '[read <amatch>"
let path = commit . substitute(file, '^/', ':', '') endif
let hash = repo.rev_parse(path) if rev !~# ':'
if path =~ '^:' let cmd = fugitive#Prepare(dir, 'log', '--pretty=format:%B', '-1', rev)
let type = 'blob' else
else let cmd = fugitive#Prepare(dir, 'cat-file', '-p', rev)
let type = repo.git_chomp('cat-file','-t',hash) endif
endif return "'[read !" . escape(cmd, '!#%')
" TODO: use count, if possible
return "read !".escape(repo.git_command('cat-file',type,hash),'%#\')
catch /^fugitive:/
return 'echoerr v:errmsg'
endtry
endfunction endfunction
function! fugitive#BufReadIndex() abort function! fugitive#BufReadIndex() abort
@ -2916,10 +2936,6 @@ function! fugitive#BufReadObject() abort
endtry endtry
endfunction endfunction
augroup fugitive_files
autocmd!
augroup END
" Section: Temp files " Section: Temp files
if !exists('s:temp_files') if !exists('s:temp_files')
@ -2931,7 +2947,7 @@ augroup fugitive_temp
autocmd BufNewFile,BufReadPost * autocmd BufNewFile,BufReadPost *
\ if has_key(s:temp_files,s:cpath(expand('<afile>:p'))) | \ if has_key(s:temp_files,s:cpath(expand('<afile>:p'))) |
\ let b:git_dir = s:temp_files[s:cpath(expand('<afile>:p'))].dir | \ let b:git_dir = s:temp_files[s:cpath(expand('<afile>:p'))].dir |
\ let b:git_type = 'temp' | \ call extend(b:, {'fugitive_type': 'temp'}, 'keep') |
\ let b:git_args = s:temp_files[s:cpath(expand('<afile>:p'))].args | \ let b:git_args = s:temp_files[s:cpath(expand('<afile>:p'))].args |
\ call FugitiveDetect(expand('<afile>:p')) | \ call FugitiveDetect(expand('<afile>:p')) |
\ setlocal bufhidden=delete nobuflisted | \ setlocal bufhidden=delete nobuflisted |

View File

@ -54,8 +54,8 @@ that are part of Git repositories).
dv |:Gvdiff| dv |:Gvdiff|
O |:Gtabedit| O |:Gtabedit|
o |:Gsplit| o |:Gsplit|
p |:Git| add --patch P |:Git| add --patch
p |:Git| reset --patch (staged files) P |:Git| reset --patch (staged files)
q close status q close status
r reload status r reload status
S |:Gvsplit| S |:Gvsplit|

View File

@ -1,6 +1,6 @@
" fugitive.vim - A Git wrapper so awesome, it should be illegal " fugitive.vim - A Git wrapper so awesome, it should be illegal
" Maintainer: Tim Pope <http://tpo.pe/> " Maintainer: Tim Pope <http://tpo.pe/>
" Version: 2.3 " Version: 2.4
" GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
if exists('g:loaded_fugitive') if exists('g:loaded_fugitive')
@ -110,7 +110,7 @@ function! FugitiveExtractGitDir(path) abort
endfunction endfunction
function! FugitiveDetect(path) abort function! FugitiveDetect(path) abort
if exists('b:git_dir') && (b:git_dir ==# '' || b:git_dir =~# '/$') if exists('b:git_dir') && b:git_dir =~# '^$\|/$\|^fugitive:'
unlet b:git_dir unlet b:git_dir
endif endif
if !exists('b:git_dir') if !exists('b:git_dir')
@ -156,7 +156,10 @@ augroup fugitive
autocmd BufNewFile,BufReadPost * call FugitiveDetect(expand('%:p')) autocmd BufNewFile,BufReadPost * call FugitiveDetect(expand('%:p'))
autocmd FileType netrw call FugitiveDetect(fnamemodify(get(b:, 'netrw_curdir', @%), ':p')) autocmd FileType netrw call FugitiveDetect(fnamemodify(get(b:, 'netrw_curdir', @%), ':p'))
autocmd User NERDTreeInit,NERDTreeNewRoot call FugitiveDetect(b:NERDTree.root.path.str()) autocmd User NERDTreeInit,NERDTreeNewRoot
\ if exists('b:NERDTree.root.path.str') |
\ call FugitiveDetect(b:NERDTree.root.path.str()) |
\ endif
autocmd VimEnter * if expand('<amatch>')==''|call FugitiveDetect(getcwd())|endif autocmd VimEnter * if expand('<amatch>')==''|call FugitiveDetect(getcwd())|endif
autocmd CmdWinEnter * call FugitiveDetect(expand('#:p')) autocmd CmdWinEnter * call FugitiveDetect(expand('#:p'))

View File

@ -3,11 +3,19 @@ let s:t_string = type('')
" Primary functions {{{ " Primary functions {{{
function! gitgutter#all(force) abort function! gitgutter#all(force) abort
for bufnr in s:uniq(tabpagebuflist()) let visible = tabpagebuflist()
let file = expand('#'.bufnr.':p')
if !empty(file) for bufnr in range(1, bufnr('$') + 1)
call gitgutter#init_buffer(bufnr) if buflisted(bufnr)
call gitgutter#process_buffer(bufnr, a:force) let file = expand('#'.bufnr.':p')
if !empty(file)
if index(visible, bufnr) != -1
call gitgutter#init_buffer(bufnr)
call gitgutter#process_buffer(bufnr, a:force)
elseif a:force
call s:reset_tick(bufnr)
endif
endif
endif endif
endfor endfor
endfunction endfunction
@ -16,10 +24,10 @@ endfunction
" Finds the file's path relative to the repo root. " Finds the file's path relative to the repo root.
function! gitgutter#init_buffer(bufnr) function! gitgutter#init_buffer(bufnr)
if gitgutter#utility#is_active(a:bufnr) if gitgutter#utility#is_active(a:bufnr)
call s:setup_maps()
let p = gitgutter#utility#repo_path(a:bufnr, 0) let p = gitgutter#utility#repo_path(a:bufnr, 0)
if type(p) != s:t_string || empty(p) if type(p) != s:t_string || empty(p)
call gitgutter#utility#set_repo_path(a:bufnr) call gitgutter#utility#set_repo_path(a:bufnr)
call s:setup_maps()
endif endif
endif endif
endfunction endfunction
@ -52,15 +60,12 @@ endfunction
function! gitgutter#disable() abort function! gitgutter#disable() abort
" get list of all buffers (across all tabs) " get list of all buffers (across all tabs)
let buflist = [] for bufnr in range(1, bufnr('$') + 1)
for i in range(tabpagenr('$')) if buflisted(bufnr)
call extend(buflist, tabpagebuflist(i + 1)) let file = expand('#'.bufnr.':p')
endfor if !empty(file)
call s:clear(bufnr)
for bufnr in s:uniq(buflist) endif
let file = expand('#'.bufnr.':p')
if !empty(file)
call s:clear(bufnr)
endif endif
endfor endfor
@ -132,19 +137,3 @@ function! s:clear(bufnr)
call gitgutter#hunk#reset(a:bufnr) call gitgutter#hunk#reset(a:bufnr)
call s:reset_tick(a:bufnr) call s:reset_tick(a:bufnr)
endfunction endfunction
if exists('*uniq') " Vim 7.4.218
function! s:uniq(list)
return uniq(sort(a:list))
endfunction
else
function! s:uniq(list)
let processed = []
for e in a:list
if index(processed, e) == -1
call add(processed, e)
endif
endfor
return processed
endfunction
endif

View File

@ -173,26 +173,10 @@ nnoremap <silent> <Plug>GitGutterPreviewHunk :GitGutterPreviewHunk<CR>
" }}} " }}}
function! s:flag_inactive_tabs()
let active_tab = tabpagenr()
for i in range(1, tabpagenr('$'))
if i != active_tab
call settabvar(i, 'gitgutter_force', 1)
endif
endfor
endfunction
function! s:on_bufenter() function! s:on_bufenter()
if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter
let t:gitgutter_didtabenter = 0 let t:gitgutter_didtabenter = 0
let force = !g:gitgutter_terminal_reports_focus call gitgutter#all(!g:gitgutter_terminal_reports_focus)
if exists('t:gitgutter_force') && t:gitgutter_force
let t:gitgutter_force = 0
let force = 1
endif
call gitgutter#all(force)
else else
call gitgutter#init_buffer(bufnr('')) call gitgutter#init_buffer(bufnr(''))
call gitgutter#process_buffer(bufnr(''), !g:gitgutter_terminal_reports_focus) call gitgutter#process_buffer(bufnr(''), !g:gitgutter_terminal_reports_focus)
@ -208,15 +192,15 @@ augroup gitgutter
autocmd BufEnter * call s:on_bufenter() autocmd BufEnter * call s:on_bufenter()
autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 0) autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 0)
autocmd FileChangedShellPost,ShellCmdPost * call gitgutter#process_buffer(bufnr(''), 1) autocmd FileChangedShellPost * call gitgutter#process_buffer(bufnr(''), 1)
" Ensure that all buffers are processed when opening vim with multiple files, e.g.: " Ensure that all buffers are processed when opening vim with multiple files, e.g.:
" "
" vim -o file1 file2 " vim -o file1 file2
autocmd VimEnter * if winnr() != winnr('$') | call gitgutter#all(0) | endif autocmd VimEnter * if winnr() != winnr('$') | call gitgutter#all(0) | endif
autocmd FocusGained * call gitgutter#all(1) | call s:flag_inactive_tabs() autocmd FocusGained,ShellCmdPost * call gitgutter#all(1)
autocmd ColorScheme * call gitgutter#highlight#define_sign_column_highlight() | call gitgutter#highlight#define_highlights() autocmd ColorScheme * call gitgutter#highlight#define_sign_column_highlight() | call gitgutter#highlight#define_highlights()

View File

@ -8,9 +8,16 @@ matrix:
- env: SCRIPT="test -c" VIM_VERSION=vim-7.4 - env: SCRIPT="test -c" VIM_VERSION=vim-7.4
- env: SCRIPT="test -c" VIM_VERSION=vim-8.0 - env: SCRIPT="test -c" VIM_VERSION=vim-8.0
- env: SCRIPT="test -c" VIM_VERSION=nvim - env: SCRIPT="test -c" VIM_VERSION=nvim
- env: SCRIPT=lint VIM_VERSION=vim-8.0 - env: ENV=vimlint SCRIPT=lint VIM_VERSION=vim-8.0
language: python
python: 3.6
install: install:
- ./scripts/install-vim $VIM_VERSION - ./scripts/install-vim $VIM_VERSION
- pip install --user vim-vint covimerage codecov - |
if [ "$ENV" = "vimlint" ]; then
pip install vim-vint covimerage codecov pathlib
else
pip install --user vim-vint covimerage codecov pathlib
fi
script: script:
- ./scripts/$SCRIPT $VIM_VERSION - ./scripts/$SCRIPT $VIM_VERSION

View File

@ -1,5 +1,11 @@
## unplanned ## unplanned
IMPROVEMENTS:
* Unify async job handling for Vim8 and Neovim.
[[GH-1864]](https://github.com/fatih/vim-go/pull/1864)
## 1.18 - (July 18, 2018)
FEATURES: FEATURES:
* Add **:GoIfErr** command together with the `<Plug>(go-iferr)` plug key to * Add **:GoIfErr** command together with the `<Plug>(go-iferr)` plug key to
@ -7,21 +13,22 @@ FEATURES:
automatically which infer the type of return values and the numbers. automatically which infer the type of return values and the numbers.
For example: For example:
``` ```
func doSomething() (string, error) { func doSomething() (string, error) {
f, err := os.Open("file") f, err := os.Open("file")
} }
``` ```
Becomes:
Becomes:
``` ```
func doSomething() (string, error) { func doSomething() (string, error) {
f, err := os.Open("file") f, err := os.Open("file")
if err != nil { if err != nil {
return "", err return "", err
} }
} }
``` ```
* Two new text objects has been added: * Two new text objects has been added:
* `ic` (inner comment) selects the content of the comment, excluding the start/end markers (i.e: `//`, `/*`) * `ic` (inner comment) selects the content of the comment, excluding the start/end markers (i.e: `//`, `/*`)
@ -56,6 +63,10 @@ IMPROVEMENTS:
manually. [[GH-1861]](https://github.com/fatih/vim-go/pull/1861). manually. [[GH-1861]](https://github.com/fatih/vim-go/pull/1861).
* Cleanup title of terminal window. * Cleanup title of terminal window.
[[GH-1861]](https://github.com/fatih/vim-go/pull/1861). [[GH-1861]](https://github.com/fatih/vim-go/pull/1861).
* Add `:GoImpl` is able to complete interfaces by their full import path in
addition to the current package name (i.e: `:GoImpl t *T github.com/BurntSushi/toml.Unmarshaller`
is now possible)
[[GH-1884]](https://github.com/fatih/vim-go/pull/1884)
BUG FIXES: BUG FIXES:
@ -76,24 +87,14 @@ BUG FIXES:
[[GH-1818]](https://github.com/fatih/vim-go/pull/1818) [[GH-1818]](https://github.com/fatih/vim-go/pull/1818)
* Fix Neovim handling of guru output. * Fix Neovim handling of guru output.
[[GH-1846]](https://github.com/fatih/vim-go/pull/1846) [[GH-1846]](https://github.com/fatih/vim-go/pull/1846)
* Execute commands correctly when they are in $GOBIN but not $PATH.
BACKWARDS INCOMPATIBILITIES: [[GH-1866]](https://github.com/fatih/vim-go/pull/1866)
* Open files correctly with ctrlp.
* We switched to a [maintained fork of * gocode](https://github.com/mdempsky/gocode). [[GH-1878]](https://github.com/fatih/vim-go/pull/1878)
The new fork doesn't support the following settings anymore and therefore are * Fix checking guru binary path
invalid. Please remove them from your vimrc until those are again supported [[GH-1886]](https://github.com/fatih/vim-go/pull/1886)
by `gocode`. * Add build tags to `:GoDef` if only it's present
[[GH-1882]](https://github.com/fatih/vim-go/pull/1882)
```
g:go_gocode_autobuild
g:go_gocode_propose_builtins
g:go_gocode_unimported_packages
```
Checkout the issue for more details [[GH-1851]](https://github.com/fatih/vim-go/pull/1851)
## 1.17 - (March 27, 2018) ## 1.17 - (March 27, 2018)

View File

@ -117,7 +117,7 @@ function! ctrlp#decls#enter() abort
call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s", call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s",
\ decl.ident . space, \ decl.ident . space,
\ decl.keyword, \ decl.keyword,
\ fnamemodify(decl.filename, ":t"), \ fnamemodify(decl.filename, ":."),
\ decl.line, \ decl.line,
\ decl.col, \ decl.col,
\ decl.full, \ decl.full,

View File

@ -30,8 +30,8 @@ function! go#cmd#Build(bang, ...) abort
\ map(copy(a:000), "expand(v:val)") + \ map(copy(a:000), "expand(v:val)") +
\ [".", "errors"] \ [".", "errors"]
" Vim async. " Vim and Neovim async.
if go#util#has_job() if go#util#has_job() || has('nvim')
if go#config#EchoCommandInfo() if go#config#EchoCommandInfo()
call go#util#EchoProgress("building dispatched ...") call go#util#EchoProgress("building dispatched ...")
endif endif
@ -42,14 +42,6 @@ function! go#cmd#Build(bang, ...) abort
\ 'for': 'GoBuild', \ 'for': 'GoBuild',
\}) \})
" Nvim async.
elseif has('nvim')
if go#config#EchoCommandInfo()
call go#util#EchoProgress("building dispatched ...")
endif
call go#jobcontrol#Spawn(a:bang, "build", "GoBuild", args)
" Vim 7.4 without async " Vim 7.4 without async
else else
let default_makeprg = &makeprg let default_makeprg = &makeprg
@ -297,44 +289,7 @@ function s:cmd_job(args) abort
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
function! s:complete(job, exit_status, data) closure abort call go#job#Spawn(a:args.cmd, a:args)
let status = {
\ 'desc': 'last status',
\ 'type': a:args.cmd[1],
\ 'state': "success",
\ }
if a:exit_status
let status.state = "failed"
endif
let elapsed_time = reltimestr(reltime(started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status)
endfunction
let a:args.complete = funcref('s:complete')
let callbacks = go#job#Spawn(a:args)
let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ }
" pre start
let dir = getcwd()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let jobdir = fnameescape(expand("%:p:h"))
execute cd . jobdir
call job_start(a:args.cmd, start_options)
" post start
execute cd . fnameescape(dir)
endfunction endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View File

@ -48,12 +48,13 @@ function! go#coverage#Buffer(bang, ...) abort
call go#util#EchoProgress("testing...") call go#util#EchoProgress("testing...")
endif endif
if go#util#has_job() if go#util#has_job() || has('nvim')
call s:coverage_job({ call s:coverage_job({
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname] + a:000, \ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname] + a:000,
\ 'complete': function('s:coverage_callback', [l:tmpname]), \ 'complete': function('s:coverage_callback', [l:tmpname]),
\ 'bang': a:bang, \ 'bang': a:bang,
\ 'for': 'GoTest', \ 'for': 'GoTest',
\ 'statustype': 'coverage',
\ }) \ })
return return
endif endif
@ -63,24 +64,8 @@ function! go#coverage#Buffer(bang, ...) abort
call extend(args, a:000) call extend(args, a:000)
endif endif
let disabled_term = 0
if go#config#TermEnabled()
let disabled_term = 1
call go#config#SetTermEnabled(0)
endif
let id = call('go#test#Test', args) let id = call('go#test#Test', args)
if disabled_term
call go#config#SetTermEnabled(1)
endif
if has('nvim')
call go#jobcontrol#AddHandler(function('s:coverage_handler'))
let s:coverage_handler_jobs[id] = l:tmpname
return
endif
if go#util#ShellError() == 0 if go#util#ShellError() == 0
call go#coverage#overlay(l:tmpname) call go#coverage#overlay(l:tmpname)
endif endif
@ -104,7 +89,7 @@ endfunction
" a new HTML coverage page from that profile in a new browser " a new HTML coverage page from that profile in a new browser
function! go#coverage#Browser(bang, ...) abort function! go#coverage#Browser(bang, ...) abort
let l:tmpname = tempname() let l:tmpname = tempname()
if go#util#has_job() if go#util#has_job() || has('nvim')
call s:coverage_job({ call s:coverage_job({
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname], \ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname],
\ 'complete': function('s:coverage_browser_callback', [l:tmpname]), \ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
@ -120,11 +105,6 @@ function! go#coverage#Browser(bang, ...) abort
endif endif
let id = call('go#test#Test', args) let id = call('go#test#Test', args)
if has('nvim')
call go#jobcontrol#AddHandler(function('s:coverage_browser_handler'))
let s:coverage_browser_handler_jobs[id] = l:tmpname
return
endif
if go#util#ShellError() == 0 if go#util#ShellError() == 0
call go#tool#ExecuteInDir(['go', 'tool', 'cover', '-html=' . l:tmpname]) call go#tool#ExecuteInDir(['go', 'tool', 'cover', '-html=' . l:tmpname])
@ -275,48 +255,17 @@ function s:coverage_job(args)
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
let status_dir = expand('%:p:h') let disabled_term = 0
let Complete = a:args.complete if go#config#TermEnabled()
function! s:complete(job, exit_status, data) closure let disabled_term = 1
let status = { call go#config#SetTermEnabled(0)
\ 'desc': 'last status', endif
\ 'type': "coverage",
\ 'state': "finished",
\ }
if a:exit_status call go#job#Spawn(a:args.cmd, a:args)
let status.state = "failed"
endif
call go#statusline#Update(status_dir, status) if disabled_term
return Complete(a:job, a:exit_status, a:data) call go#config#SetTermEnabled(1)
endfunction endif
let a:args.complete = funcref('s:complete')
let callbacks = go#job#Spawn(a:args)
let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ }
" pre start
let dir = getcwd()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let jobdir = fnameescape(expand("%:p:h"))
execute cd . jobdir
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': "coverage",
\ 'state': "started",
\})
call job_start(a:args.cmd, start_options)
" post start
execute cd . fnameescape(dir)
endfunction endfunction
" coverage_callback is called when the coverage execution is finished " coverage_callback is called when the coverage execution is finished
@ -336,39 +285,4 @@ function! s:coverage_browser_callback(coverfile, job, exit_status, data)
call delete(a:coverfile) call delete(a:coverfile)
endfunction endfunction
" -----------------------
" | Neovim job handlers |
" -----------------------
let s:coverage_handler_jobs = {}
let s:coverage_browser_handler_jobs = {}
function! s:coverage_handler(job, exit_status, data) abort
if !has_key(s:coverage_handler_jobs, a:job.id)
return
endif
let l:tmpname = s:coverage_handler_jobs[a:job.id]
if a:exit_status == 0
call go#coverage#overlay(l:tmpname)
endif
call delete(l:tmpname)
unlet s:coverage_handler_jobs[a:job.id]
endfunction
function! s:coverage_browser_handler(job, exit_status, data) abort
if !has_key(s:coverage_browser_handler_jobs, a:job.id)
return
endif
let l:tmpname = s:coverage_browser_handler_jobs[a:job.id]
if a:exit_status == 0
call go#tool#ExecuteInDir(['go', 'tool', 'cover', '-html=' . l:tmpname])
endif
call delete(l:tmpname)
unlet s:coverage_browser_handler_jobs[a:job.id]
endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View File

@ -26,7 +26,12 @@ function! go#def#Jump(mode) abort
endif endif
elseif bin_name == 'guru' elseif bin_name == 'guru'
let cmd = [bin_name, '-tags', go#config#BuildTags()] let cmd = [go#path#CheckBinPath(bin_name)]
let buildtags = go#config#BuildTags()
if buildtags isnot ''
let cmd += ['-tags', buildtags]
endif
let stdin_content = "" let stdin_content = ""
if &modified if &modified
@ -278,13 +283,7 @@ function! go#def#Stack(...) abort
endfunction endfunction
function s:def_job(args) abort function s:def_job(args) abort
let callbacks = go#job#Spawn(a:args) let l:start_options = go#job#Options(a:args)
let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ }
if &modified if &modified
let l:tmpname = tempname() let l:tmpname = tempname()
@ -293,7 +292,7 @@ function s:def_job(args) abort
let l:start_options.in_name = l:tmpname let l:start_options.in_name = l:tmpname
endif endif
call job_start(a:args.cmd, start_options) call go#job#Start(a:args.cmd, start_options)
endfunction endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View File

@ -101,13 +101,29 @@ function! s:root_dirs() abort
return dirs return dirs
endfunction endfunction
function! s:go_packages(dirs) abort function! s:go_packages(dirs, arglead) abort
let pkgs = [] let pkgs = []
for d in a:dirs for dir in a:dirs
let pkg_root = expand(d . '/pkg/' . go#util#osarch()) " this may expand to multiple lines
call extend(pkgs, split(globpath(pkg_root, '**/*.a', 1), "\n")) let scr_root = expand(dir . '/src/')
for pkg in split(globpath(scr_root, a:arglead.'*'), "\n")
if isdirectory(pkg)
let pkg .= '/'
elseif pkg !~ '\.a$'
continue
endif
" without this the result can have duplicates in form of
" 'encoding/json' and '/encoding/json/'
let pkg = go#util#StripPathSep(pkg)
" remove the scr root and keep the package in tact
let pkg = substitute(pkg, scr_root, "", "")
call add(pkgs, pkg)
endfor
endfor endfor
return map(pkgs, "fnamemodify(v:val, ':t:r')")
return pkgs
endfunction endfunction
function! s:interface_list(pkg) abort function! s:interface_list(pkg) abort
@ -124,13 +140,24 @@ endfunction
" Complete package and interface for {interface} " Complete package and interface for {interface}
function! go#impl#Complete(arglead, cmdline, cursorpos) abort function! go#impl#Complete(arglead, cmdline, cursorpos) abort
let words = split(a:cmdline, '\s\+', 1) let words = split(a:cmdline, '\s\+', 1)
if words[-1] ==# '' if words[-1] ==# ''
return s:uniq(sort(s:go_packages(s:root_dirs()))) " if no words are given, just start completing the first package we found
elseif words[-1] =~# '^\h\w*$' return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead)))
return s:uniq(sort(filter(s:go_packages(s:root_dirs()), 'stridx(v:val, words[-1]) == 0'))) elseif words[-1] =~# '^\(\h\w.*\.\%(\h\w*\)\=$\)\@!\S*$'
elseif words[-1] =~# '^\h\w*\.\%(\h\w*\)\=$' " start matching go packages. It's negate match of the below match
let [pkg, interface] = split(words[-1], '\.', 1) return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead)))
echomsg pkg elseif words[-1] =~# '^\h\w.*\.\%(\h\w*\)\=$'
" match the following, anything that could indicate an interface candidate
"
" io.
" io.Wr
" github.com/fatih/color.
" github.com/fatih/color.U
" github.com/fatih/color.Un
let splitted = split(words[-1], '\.', 1)
let pkg = join(splitted[:-2], '.')
let interface = splitted[-1]
return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]'))) return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]')))
else else
return [] return []

View File

@ -1,7 +1,25 @@
" Spawn returns callbacks to be used with job_start. It is abstracted to be " Spawn starts an asynchronous job. See the description of go#job#Options to
" understand the args parameter.
"
" Spawn returns a job.
function! go#job#Spawn(cmd, args)
let l:options = go#job#Options(a:args)
return go#job#Start(a:cmd, l:options)
endfunction
" Spawn starts an asynchronous job. See the description of go#job#Options to
" understand the args parameter.
"
" Spawn returns a job.
function! go#job#Spawn(cmd, args)
let l:options = go#job#Options(a:args)
return go#job#Start(a:cmd, l:options)
endfunction
" Options returns callbacks to be used with job_start. It is abstracted to be
" used with various go commands, such as build, test, install, etc.. This " used with various go commands, such as build, test, install, etc.. This
" allows us to avoid writing the same callback over and over for some " allows us to avoid writing the same callback over and over for some
" commands. It's fully customizable so each command can change it to it's own " commands. It's fully customizable so each command can change it to its own
" logic. " logic.
" "
" args is a dictionary with the these keys: " args is a dictionary with the these keys:
@ -10,9 +28,16 @@
" 'bang': " 'bang':
" Set to 0 to jump to the first error in the error list. " Set to 0 to jump to the first error in the error list.
" Defaults to 0. " Defaults to 0.
" 'statustype':
" The status type to use when updating the status.
" See statusline.vim.
" 'for': " 'for':
" The g:go_list_type_command key to use to get the error list type to use. " The g:go_list_type_command key to use to get the error list type to use.
" Defaults to '_job' " Defaults to '_job'
" 'errorformat':
" The errorformat string to use when parsing errors. Defaults to
" &errorformat.
" See :help 'errorformat'.
" 'complete': " 'complete':
" A function to call after the job exits and the channel is closed. The " A function to call after the job exits and the channel is closed. The
" function will be passed three arguments: the job, its exit code, and the " function will be passed three arguments: the job, its exit code, and the
@ -30,22 +55,28 @@
" 'close_cb': " 'close_cb':
" A function suitable to be passed as a job close_cb handler. See " A function suitable to be passed as a job close_cb handler. See
" job-close_cb. " job-close_cb.
function go#job#Spawn(args) " 'cwd':
" The path to the directory which contains the current buffer.
function! go#job#Options(args)
let cbs = {} let cbs = {}
let state = { let state = {
\ 'winid': win_getid(winnr()), \ 'winid': win_getid(winnr()),
\ 'dir': getcwd(), \ 'dir': getcwd(),
\ 'jobdir': fnameescape(expand("%:p:h")), \ 'jobdir': fnameescape(expand("%:p:h")),
\ 'messages': [], \ 'messages': [],
\ 'args': a:args.cmd,
\ 'bang': 0, \ 'bang': 0,
\ 'for': "_job", \ 'for': "_job",
\ 'exited': 0, \ 'exited': 0,
\ 'exit_status': 0, \ 'exit_status': 0,
\ 'closed': 0, \ 'closed': 0,
\ 'errorformat': &errorformat \ 'errorformat': &errorformat,
\ 'statustype' : ''
\ } \ }
if has("patch-8.0.0902") || has('nvim')
let cbs.cwd = state.jobdir
endif
if has_key(a:args, 'bang') if has_key(a:args, 'bang')
let state.bang = a:args.bang let state.bang = a:args.bang
endif endif
@ -54,14 +85,78 @@ function go#job#Spawn(args)
let state.for = a:args.for let state.for = a:args.for
endif endif
if has_key(a:args, 'statustype')
let state.statustype = a:args.statustype
endif
if has_key(a:args, 'errorformat')
let state.errorformat = a:args.errorformat
endif
" do nothing in state.complete by default. " do nothing in state.complete by default.
function state.complete(job, exit_status, data) function state.complete(job, exit_status, data)
endfunction endfunction
function state.show_status(job, exit_status) dict
if go#config#EchoCommandInfo()
let prefix = ""
if self.statustype != ''
let prefix = '[' . self.statustype . '] '
endif
if a:exit_status == 0
call go#util#EchoSuccess(prefix . "SUCCESS")
else
call go#util#EchoError(prefix . "FAIL")
endif
endif
if self.statustype == ''
return
endif
let status = {
\ 'desc': 'last status',
\ 'type': self.statustype,
\ 'state': "success",
\ }
if a:exit_status
let status.state = "failed"
endif
if has_key(self, 'started_at')
let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
endif
call go#statusline#Update(self.jobdir, status)
endfunction
if has_key(a:args, 'complete') if has_key(a:args, 'complete')
let state.complete = a:args.complete let state.complete = a:args.complete
endif endif
function! s:start(args) dict
if self.statustype != ''
let status = {
\ 'desc': 'current status',
\ 'type': self.statustype,
\ 'state': "started",
\ }
call go#statusline#Update(self.jobdir, status)
endif
let self.started_at = reltime()
endfunction
" explicitly bind _start to state so that within it, self will
" always refer to state. See :help Partial for more information.
"
" _start is intended only for internal use and should not be referenced
" outside of this file.
let cbs._start = function('s:start', [''], state)
function! s:callback(chan, msg) dict function! s:callback(chan, msg) dict
call add(self.messages, a:msg) call add(self.messages, a:msg)
endfunction endfunction
@ -73,15 +168,9 @@ function go#job#Spawn(args)
let self.exit_status = a:exitval let self.exit_status = a:exitval
let self.exited = 1 let self.exited = 1
if go#config#EchoCommandInfo() call self.show_status(a:job, a:exitval)
if a:exitval == 0
call go#util#EchoSuccess("SUCCESS")
else
call go#util#EchoError("FAILED")
endif
endif
if self.closed if self.closed || has('nvim')
call self.complete(a:job, self.exit_status, self.messages) call self.complete(a:job, self.exit_status, self.messages)
call self.show_errors(a:job, self.exit_status, self.messages) call self.show_errors(a:job, self.exit_status, self.messages)
endif endif
@ -123,14 +212,14 @@ function go#job#Spawn(args)
let out = join(self.messages, "\n") let out = join(self.messages, "\n")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
try try
" parse the errors relative to self.jobdir " parse the errors relative to self.jobdir
execute cd self.jobdir execute l:cd self.jobdir
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for) call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
finally finally
execute cd . fnameescape(self.dir) execute l:cd fnameescape(self.dir)
endtry endtry
@ -149,7 +238,178 @@ function go#job#Spawn(args)
endif endif
endfunction endfunction
if has('nvim')
return s:neooptions(cbs)
endif
return cbs return cbs
endfunction endfunction
function! go#job#Start(cmd, options)
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let l:options = copy(a:options)
if !has_key(l:options, 'cwd')
" pre start
let dir = getcwd()
execute l:cd fnameescape(expand("%:p:h"))
endif
if has_key(l:options, '_start')
call l:options._start()
" remove _start to play nicely with vim (when vim encounters an unexpected
" job option it reports an "E475: invalid argument" error.
unlet l:options._start
endif
if has('nvim')
let l:input = []
if has_key(l:options, 'in_io') && l:options.in_io ==# 'file' && !empty(l:options.in_name)
let l:input = readfile(l:options.in_name, 1)
endif
let job = jobstart(a:cmd, l:options)
if len(l:input) > 0
call chansend(job, l:input)
" close stdin to signal that no more bytes will be sent.
call chanclose(job, 'stdin')
endif
else
let job = job_start(a:cmd, l:options)
endif
if !has_key(l:options, 'cwd')
" post start
execute l:cd fnameescape(dir)
endif
return job
endfunction
" s:neooptions returns a dictionary of job options suitable for use by Neovim
" based on a dictionary of job options suitable for Vim8.
function! s:neooptions(options)
let l:options = {}
let l:options['stdout_buf'] = ''
let l:options['stderr_buf'] = ''
for key in keys(a:options)
if key == 'callback'
let l:options['callback'] = a:options['callback']
if !has_key(a:options, 'out_cb')
let l:options['stdout_buffered'] = v:true
function! s:callback2on_stdout(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stdout_buf . l:data[0]
let self.stdout_buf = ""
if l:data[-1] != ""
let self.stdout_buf = l:data[-1]
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.callback(a:ch, join(l:data, "\n"))
endfunction
let l:options['on_stdout'] = function('s:callback2on_stdout', [], l:options)
endif
if !has_key(a:options, 'err_cb')
let l:options['stderr_buffered'] = v:true
function! s:callback2on_stderr(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stderr_buf . l:data[0]
let self.stderr_buf = ""
if l:data[-1] != ""
let self.stderr_buf = l:data[-1]
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.callback(a:ch, join(l:data, "\n"))
endfunction
let l:options['on_stderr'] = function('s:callback2on_stderr', [], l:options)
endif
continue
endif
if key == 'out_cb'
let l:options['out_cb'] = a:options['out_cb']
let l:options['stdout_buffered'] = v:true
function! s:on_stdout(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stdout_buf . l:data[0]
let self.stdout_buf = ""
if l:data[-1] != ""
let self.stdout_buf = l:data[-1]
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.out_cb(a:ch, join(l:data, "\n"))
endfunction
let l:options['on_stdout'] = function('s:on_stdout', [], l:options)
continue
endif
if key == 'err_cb'
let l:options['err_cb'] = a:options['err_cb']
let l:options['stderr_buffered'] = v:true
function! s:on_stderr(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stderr_buf . l:data[0]
let self.stderr_buf = ""
if l:data[-1] != ""
let self.stderr_buf = l:data[-1]
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.err_cb(a:ch, join(l:data, "\n"))
endfunction
let l:options['on_stderr'] = function('s:on_stderr', [], l:options)
continue
endif
if key == 'exit_cb'
let l:options['exit_cb'] = a:options['exit_cb']
function! s:on_exit(jobid, exitval, event) dict
call self.exit_cb(a:jobid, a:exitval)
endfunction
let l:options['on_exit'] = function('s:on_exit', [], l:options)
continue
endif
if key == 'close_cb'
continue
endif
endfor
return l:options
endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View File

@ -1,195 +0,0 @@
" s:jobs is a global reference to all jobs started with Spawn() or with the
" internal function s:spawn
let s:jobs = {}
" s:handlers is a global event handlers for all jobs started with Spawn() or
" with the internal function s:spawn
let s:handlers = {}
" Spawn is a wrapper around s:spawn. It can be executed by other files and
" scripts if needed. Desc defines the description for printing the status
" during the job execution (useful for statusline integration).
function! go#jobcontrol#Spawn(bang, desc, for, args) abort
" autowrite is not enabled for jobs
call go#cmd#autowrite()
let job = s:spawn(a:bang, a:desc, a:for, a:args)
return job.id
endfunction
" AddHandler adds a on_exit callback handler and returns the id.
function! go#jobcontrol#AddHandler(handler) abort
let i = len(s:handlers)
while has_key(s:handlers, string(i))
let i += 1
break
endwhile
let s:handlers[string(i)] = a:handler
return string(i)
endfunction
" RemoveHandler removes a callback handler by id.
function! go#jobcontrol#RemoveHandler(id) abort
unlet s:handlers[a:id]
endfunction
" spawn spawns a go subcommand with the name and arguments with jobstart. Once a
" job is started a reference will be stored inside s:jobs. The job is started
" inside the current files folder.
function! s:spawn(bang, desc, for, args) abort
let status_type = a:args[0]
let status_dir = expand('%:p:h')
let started_at = reltime()
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': status_type,
\ 'state': "started",
\})
let job = {
\ 'desc': a:desc,
\ 'bang': a:bang,
\ 'winid': win_getid(winnr()),
\ 'importpath': go#package#ImportPath(),
\ 'state': "RUNNING",
\ 'stderr' : [],
\ 'stdout' : [],
\ 'on_stdout': function('s:on_stdout'),
\ 'on_stderr': function('s:on_stderr'),
\ 'on_exit' : function('s:on_exit'),
\ 'status_type' : status_type,
\ 'status_dir' : status_dir,
\ 'started_at' : started_at,
\ 'for' : a:for,
\ 'errorformat': &errorformat,
\ }
" execute go build in the files directory
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
" cleanup previous jobs for this file
for jb in values(s:jobs)
if jb.importpath == job.importpath
unlet s:jobs[jb.id]
endif
endfor
let dir = getcwd()
let jobdir = fnameescape(expand("%:p:h"))
execute cd . jobdir
" append the subcommand, such as 'build'
let argv = ['go'] + a:args
" run, forrest, run!
let id = jobstart(argv, job)
let job.id = id
let job.dir = jobdir
let s:jobs[id] = job
execute cd . fnameescape(dir)
return job
endfunction
" on_exit is the exit handler for jobstart(). It handles cleaning up the job
" references and also displaying errors in the quickfix window collected by
" on_stderr handler. If there are no errors and a quickfix window is open,
" it'll be closed.
function! s:on_exit(job_id, exit_status, event) dict abort
let l:winid = win_getid(winnr())
call win_gotoid(self.winid)
let status = {
\ 'desc': 'last status',
\ 'type': self.status_type,
\ 'state': "success",
\ }
if a:exit_status
let status.state = "failed"
endif
let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(self.status_dir, status)
let std_combined = self.stderr + self.stdout
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
execute cd self.dir
call s:callback_handlers_on_exit(s:jobs[a:job_id], a:exit_status, std_combined)
let l:listtype = go#list#Type(self.for)
if a:exit_status == 0
call go#list#Clean(l:listtype)
let self.state = "SUCCESS"
if go#config#EchoCommandInfo()
call go#util#EchoSuccess("[" . self.status_type . "] SUCCESS")
endif
execute cd . fnameescape(dir)
call win_gotoid(l:winid)
return
endif
let self.state = "FAILED"
if go#config#EchoCommandInfo()
call go#util#EchoError("[" . self.status_type . "] FAILED")
endif
" parse the errors relative to self.jobdir
call go#list#ParseFormat(l:listtype, self.errorformat, std_combined, self.for)
let errors = go#list#Get(l:listtype)
execute cd . fnameescape(dir)
if !len(errors)
" failed to parse errors, output the original content
call go#util#EchoError(std_combined[0])
call win_gotoid(l:winid)
return
endif
" if we are still in the same windows show the list
if self.winid == l:winid
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !self.bang
call go#list#JumpToFirst(l:listtype)
endif
endif
endfunction
" callback_handlers_on_exit runs all handlers for job on exit event.
function! s:callback_handlers_on_exit(job, exit_status, data) abort
if empty(s:handlers)
return
endif
for s:handler in values(s:handlers)
call s:handler(a:job, a:exit_status, a:data)
endfor
endfunction
" on_stdout is the stdout handler for jobstart(). It collects the output of
" stderr and stores them to the jobs internal stdout list.
function! s:on_stdout(job_id, data, event) dict abort
call extend(self.stdout, a:data)
endfunction
" on_stderr is the stderr handler for jobstart(). It collects the output of
" stderr and stores them to the jobs internal stderr list.
function! s:on_stderr(job_id, data, event) dict abort
call extend(self.stderr, a:data)
endfunction
" vim: sw=2 ts=2 et

View File

@ -34,28 +34,21 @@ function! go#test#Test(bang, compile, ...) abort
endif endif
endif endif
if go#util#has_job() if go#util#has_job() || has('nvim')
" use vim's job functionality to call it asynchronously " use vim's job functionality to call it asynchronously
let job_args = { let job_options = {
\ 'cmd': ['go'] + args,
\ 'bang': a:bang, \ 'bang': a:bang,
\ 'winid': win_getid(winnr()), \ 'for': 'GoTest',
\ 'dir': getcwd(), \ 'statustype': 'test',
\ 'compile_test': a:compile, \ 'errorformat': s:errorformat(),
\ 'jobdir': fnameescape(expand("%:p:h")),
\ } \ }
call s:test_job(job_args) if a:compile
return let job_options.statustype = 'compile ' . job_options.statustype
elseif has('nvim')
" use nvims's job functionality
if go#config#TermEnabled()
let id = go#term#new(a:bang, ["go"] + args)
else
let id = go#jobcontrol#Spawn(a:bang, "test", "GoTest", args)
endif endif
return id call s:test_job(['go'] + args, job_options)
return
endif endif
call go#cmd#autowrite() call go#cmd#autowrite()
@ -129,156 +122,13 @@ function! go#test#Func(bang, ...) abort
call call('go#test#Test', args) call call('go#test#Test', args)
endfunction endfunction
function! s:test_job(args) abort function! s:test_job(cmd, args) abort
let status = {
\ 'desc': 'current status',
\ 'type': "test",
\ 'state': "started",
\ }
if a:args.compile_test
let status.state = "compiling"
endif
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
let state = { call go#job#Spawn(a:cmd, a:args)
\ 'exited': 0,
\ 'closed': 0,
\ 'exitval': 0,
\ 'messages': [],
\ 'args': a:args,
\ 'compile_test': a:args.compile_test,
\ 'status_dir': expand('%:p:h'),
\ 'started_at': reltime()
\ }
call go#statusline#Update(state.status_dir, status)
function! s:callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction
function! s:exit_cb(job, exitval) dict
let self.exited = 1
let self.exitval = a:exitval
let status = {
\ 'desc': 'last status',
\ 'type': "test",
\ 'state': "pass",
\ }
if self.compile_test
let status.state = "success"
endif
if a:exitval
let status.state = "failed"
endif
if go#config#EchoCommandInfo()
if a:exitval == 0
if self.compile_test
call go#util#EchoSuccess("[test] SUCCESS")
else
call go#util#EchoSuccess("[test] PASS")
endif
else
call go#util#EchoError("[test] FAIL")
endif
endif
let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(self.status_dir, status)
if self.closed
call s:show_errors(self.args, self.exitval, self.messages)
endif
endfunction
function! s:close_cb(ch) dict
let self.closed = 1
if self.exited
call s:show_errors(self.args, self.exitval, self.messages)
endif
endfunction
" explicitly bind the callbacks to state so that self within them always
" refers to state. See :help Partial for more information.
let start_options = {
\ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb", [], state)
\ }
" pre start
let dir = getcwd()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let jobdir = fnameescape(expand("%:p:h"))
execute cd . jobdir
call job_start(a:args.cmd, start_options)
" post start
execute cd . fnameescape(dir)
endfunction endfunction
" show_errors parses the given list of lines of a 'go test' output and returns
" a quickfix compatible list of errors. It's intended to be used only for go
" test output.
function! s:show_errors(args, exit_val, messages) abort
let l:winid = win_getid(winnr())
call win_gotoid(a:args.winid)
let l:listtype = go#list#Type("GoTest")
if a:exit_val == 0
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
return
endif
" TODO(bc): When messages is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
let l:listtype = go#list#Type("GoTest")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
try
execute cd a:args.jobdir
call go#list#ParseFormat(l:listtype, s:errorformat(), a:messages, join(a:args.cmd))
let errors = go#list#Get(l:listtype)
finally
execute cd . fnameescape(a:args.dir)
endtry
if !len(errors)
" failed to parse errors, output the original content
call go#util#EchoError(a:messages)
call go#util#EchoError(a:args.dir)
call win_gotoid(l:winid)
return
endif
if a:args.winid != l:winid
call win_gotoid(l:winid)
return
endif
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:args.bang
call go#list#JumpToFirst(l:listtype)
endif
endfunction
let s:efm = "" let s:efm = ""
let s:go_test_show_name = 0 let s:go_test_show_name = 0

View File

@ -89,15 +89,6 @@ func! Test_GoTestTestCompilerError() abort
endfunc endfunc
func! s:test(file, expected, ...) abort func! s:test(file, expected, ...) abort
if has('nvim')
" nvim mostly shows test errors correctly, but the the expected errors are
" slightly different; buffer numbers are not the same and stderr doesn't
" seem to be redirected to the job, so the lines from the panic aren't in
" the output to be parsed, and hence are not in the quickfix lists. Once
" those two issues are resolved, this early return should be removed so
" the tests will run for Neovim, too.
return
endif
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test' let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
silent exe 'e ' . $GOPATH . '/src/' . a:file silent exe 'e ' . $GOPATH . '/src/' . a:file

View File

@ -167,13 +167,16 @@ function! go#util#Exec(cmd, ...) abort
let l:bin = a:cmd[0] let l:bin = a:cmd[0]
" Lookup the full path, respecting settings such as 'go_bin_path'. On errors,
" CheckBinPath will show a warning for us. " CheckBinPath will show a warning for us.
let l:bin = go#path#CheckBinPath(l:bin) let l:bin = go#path#CheckBinPath(l:bin)
if empty(l:bin) if empty(l:bin)
return ['', 1] return ['', 1]
endif endif
return call('s:exec', [a:cmd] + a:000) " Finally execute the command using the full, resolved path. Do not pass the
" unmodified command as the correct program might not exist in $PATH.
return call('s:exec', [[l:bin] + a:cmd[1:]] + a:000)
endfunction endfunction
function! s:exec(cmd, ...) abort function! s:exec(cmd, ...) abort

View File

@ -39,9 +39,23 @@ Vim command sequence: `df[$r,0f,v<C-n>…<C-n>c<CR><Up><Del><Right><Right><Right
To see what keystrokes are used for the above examples, see [the wiki page](https://github.com/terryma/vim-multiple-cursors/wiki/Keystrokes-for-example-gifs). To see what keystrokes are used for the above examples, see [the wiki page](https://github.com/terryma/vim-multiple-cursors/wiki/Keystrokes-for-example-gifs).
## Installation ## Installation
Install using [Pathogen], [Vundle], [Neobundle], or your favorite Vim package manager. Install using [Pathogen], [Vundle], [Neobundle], [vim-plug], or your favorite Vim package manager.
Requires vim 7.4 or later for full functionality. Requires vim 7.4 or newer for full functionality.
### vim-plug instructions
1. Paste this block into the top of `~/.vimrc`.
```vim script
call plug#begin()
Plug 'terryma/vim-multiple-cursors'
call plug#end()
```
2. Start vim and execute `:PlugInstall`.
## Quick Start ## Quick Start
### normal mode / visual mode ### normal mode / visual mode
@ -213,4 +227,5 @@ Obviously inspired by Sublime Text's [multiple selection][sublime-multiple-selec
[Pathogen]:http://github.com/tpope/vim-pathogen [Pathogen]:http://github.com/tpope/vim-pathogen
[Vundle]:http://github.com/gmarik/vundle [Vundle]:http://github.com/gmarik/vundle
[Neobundle]:http://github.com/Shougo/neobundle.vim [Neobundle]:http://github.com/Shougo/neobundle.vim
[vim-plug]:https://github.com/junegunn/vim-plug
[emacs-multiple-cursors]:https://github.com/magnars/multiple-cursors.el [emacs-multiple-cursors]:https://github.com/magnars/multiple-cursors.el

View File

@ -9,94 +9,10 @@ ${1:pattern}${2: when ${3:guard}} ->
${4:body} ${4:body}
endsnippet endsnippet
snippet beh "Behaviour Directive" b
-behaviour(${1:behaviour}).
endsnippet
snippet case "Case Expression"
case ${1:expression} of
${2:pattern}${3: when ${4:guard}} ->
${5:body}
end
endsnippet
snippet def "Define Directive" b
-define(${1:macro}${2: (${3:param})}, ${4:body}).
endsnippet
snippet exp "Export Directive" b
-export([${1:function}/${2:arity}]).
endsnippet
snippet fun "Fun Expression"
fun
(${1:pattern})${2: when ${3:guard}} ->
${4:body}
end
endsnippet
snippet fu "Function"
${1:function}(${2:param})${3: when ${4:guard}} ->
${5:body}
endsnippet
snippet if "If Expression"
if
${1:guard} ->
${2:body}
end
endsnippet
snippet ifdef "Ifdef Directive" b
-ifdef(${1:macro}).
endsnippet
snippet ifndef "Ifndef Directive" b
-ifndef(${1:macro}).
endsnippet
snippet imp "Import Directive" b
-import(${1:module}, [${2:function}/${3:arity}]).
endsnippet
snippet inc "Include Directive" b
-include("${1:file}").
endsnippet
snippet mod "Module Directive" b snippet mod "Module Directive" b
-module(${1:`!p snip.rv = snip.basename or "module"`}). -module(${1:`!p snip.rv = snip.basename or "module"`}).
endsnippet endsnippet
snippet rcv "Receive Expression"
receive
${1: ${2:pattern}${3: when ${4:guard}} ->
${5:body}}
${6:after
${7:expression} ->
${8:body}}
end
endsnippet
snippet rec "Record Directive" b
-record(${1:record}, {${2:field}${3: = ${4:value}}}).
endsnippet
snippet try "Try Expression"
try${1: ${2:expression}${3: of
${4:pattern}${5: when ${6:guard}} ->
${7:body}}}
${8:catch
${9:pattern}${10: when ${11:guard}} ->
${12:body}}
${13:after
${14:body}}
end
endsnippet
snippet undef "Undef Directive" b
-undef(${1:macro}).
endsnippet
snippet || "List Comprehension" snippet || "List Comprehension"
[${1:X} || ${2:X} <- ${3:List}${4:, gen}] [${1:X} || ${2:X} <- ${3:List}${4:, gen}]
endsnippet endsnippet

View File

@ -50,6 +50,23 @@ snippet spar "Paragraph" b
$0 $0
endsnippet endsnippet
###################
# Text formatting #
###################
snippet * "italics"
*${1:${VISUAL}}*$0
endsnippet
snippet ** "bold"
**${1:${VISUAL}}**$0
endsnippet
snippet *** "bold italics"
***${1:${VISUAL}}***$0
endsnippet
################ ################
# Common stuff # # Common stuff #
################ ################

View File

@ -7,10 +7,10 @@ endsnippet
snippet t "Simple tag" b snippet t "Simple tag" b
<${1:tag}> <${1:tag}>
${2:content} ${2:${VISUAL}}
</${1/([\w:._-]+).*/$1/}> </${1/([\w:._-]+).*/$1/}>
endsnippet endsnippet
snippet ti "Inline tag" b snippet ti "Inline tag" b
<${1:tag}>${2:content}</${1/([\w:._-]+).*/$1/}> <${1:tag}>${2:${VISUAL}}</${1/([\w:._-]+).*/$1/}>
endsnippet endsnippet

View File

@ -5,12 +5,16 @@ snippet mod
snippet modall snippet modall
-module(${1:`vim_snippets#Filename()`}). -module(${1:`vim_snippets#Filename()`}).
-compile([export_all]). -compile([export_all]).
start() -> start() ->
${0} ${0}
stop() -> stop() ->
ok. ok.
snippet d
erlang:display(${0}),
snippet dt
erlang:display({${1}, ${0}}),
# define directive # define directive
snippet def snippet def
-define(${1:macro}, ${2:body}). -define(${1:macro}, ${2:body}).
@ -30,17 +34,23 @@ snippet ifd
-ifdef(${1:TEST}). -ifdef(${1:TEST}).
${0} ${0}
-endif. -endif.
snippet ifnd
-ifndef(${1:TEST}).
${0}
-endif.
snippet undef
-undef(${1:macro}).
# if expression # if expression
snippet if snippet if
if if
${1:guard} -> ${1:guard} ->
${0:body} ${0:body}
end end
# case expression # case expression
snippet case snippet case
case ${1:expression} of case ${1:expression} of
${2:pattern} -> ${2:pattern} ->
${0:body}; ${0:body};
end end
# anonymous function # anonymous function
snippet fun snippet fun
@ -48,14 +58,21 @@ snippet fun
# try...catch # try...catch
snippet try snippet try
try try
${1:${VISUAL}} ${1:${VISUAL}}
catch catch
${2:_:_} -> ${0:got_some_exception} ${2:_:_} -> ${0:got_some_exception}
end
snippet rcv "Receive Expression"
receive
${1: ${2:pattern}${3: when ${4:guard}} ->
${5:body}}
${6:after
${7:expression} ->
${8:body}}
end end
# record directive # record directive
snippet rec snippet rec
-record(${1:record}, { -record(${1:record}, {${2:field}=${3:value}}).
${2:field}=${3:value}}).
# todo comment # todo comment
snippet todo snippet todo
%% TODO: ${0} %% TODO: ${0}
@ -82,15 +99,15 @@ snippet application
-export([start/2, stop/1]). -export([start/2, stop/1]).
start(_Type, _StartArgs) -> start(_Type, _StartArgs) ->
case ${0:root_supervisor}:start_link() of case ${0:root_supervisor}:start_link() of
{ok, Pid} -> {ok, Pid} ->
{ok, Pid}; {ok, Pid};
Other -> Other ->
{error, Other} {error, Other}
end. end.
stop(_State) -> stop(_State) ->
ok. ok.
# OTP supervisor # OTP supervisor
snippet supervisor snippet supervisor
-module(${1:`vim_snippets#Filename()`}). -module(${1:`vim_snippets#Filename()`}).
@ -106,14 +123,14 @@ snippet supervisor
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
start_link() -> start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []). supervisor:start_link({local, ?SERVER}, ?MODULE, []).
init([]) -> init([]) ->
Server = {${0:my_server}, {${2}, start_link, []}, Server = {${0:my_server}, {${2}, start_link, []},
permanent, 2000, worker, [${2}]}, permanent, 2000, worker, [${2}]},
Children = [Server], Children = [Server],
RestartStrategy = {one_for_one, 0, 1}, RestartStrategy = {one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}. {ok, {RestartStrategy, Children}}.
# OTP gen_server # OTP gen_server
snippet gen_server snippet gen_server
-module(${0:`vim_snippets#Filename()`}). -module(${0:`vim_snippets#Filename()`}).
@ -121,17 +138,10 @@ snippet gen_server
-behaviour(gen_server). -behaviour(gen_server).
%% API %% API
-export([ -export([start_link/0]).
start_link/0
]).
%% gen_server callbacks %% gen_server callbacks
-export([init/1, -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
@ -142,30 +152,30 @@ snippet gen_server
%%%=================================================================== %%%===================================================================
start_link() -> start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%%=================================================================== %%%===================================================================
%%% gen_server callbacks %%% gen_server callbacks
%%%=================================================================== %%%===================================================================
init([]) -> init([]) ->
{ok, #state{}}. {ok, #state{}}.
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
Reply = ok, Reply = ok,
{reply, Reply, State}. {reply, Reply, State}.
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State}.
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.
terminate(_Reason, _State) -> terminate(_Reason, _State) ->
ok. ok.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%%=================================================================== %%%===================================================================
%%% Internal functions %%% Internal functions
@ -180,14 +190,8 @@ snippet gen_fsm
-export([start_link/0]). -export([start_link/0]).
%% gen_fsm callbacks %% gen_fsm callbacks
-export([init/1, -export([init/1, state_name/2, state_name/3, handle_event/3, handle_sync_event/4,
state_name/2, handle_info/3, terminate/3, code_change/4]).
state_name/3,
handle_event/3,
handle_sync_event/4,
handle_info/3,
terminate/3,
code_change/4]).
-record(state, {}). -record(state, {}).
@ -356,15 +360,15 @@ snippet gen_event
%% API %% API
-export([start_link/0, -export([start_link/0,
add_handler/2]). add_handler/2]).
%% gen_event callbacks %% gen_event callbacks
-export([init/1, -export([init/1,
handle_event/2, handle_event/2,
handle_call/2, handle_call/2,
handle_info/2, handle_info/2,
terminate/2, terminate/2,
code_change/3]). code_change/3]).
-record(state, {}). -record(state, {}).
@ -497,6 +501,16 @@ snippet ieunit
${0} ${0}
-endif. -endif.
snippet itest
-ifdef(TEST).
${1}_test() ->
${0}
-endif.
snippet test
${1}_test() ->
${0}
snippet as snippet as
?assert(${0}) ?assert(${0})
snippet asn snippet asn
@ -523,9 +537,9 @@ snippet testsuite
%% Test server callbacks %% Test server callbacks
-export([suite/0, all/0, groups/0, -export([suite/0, all/0, groups/0,
init_per_suite/1, end_per_suite/1, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2, init_per_group/2, end_per_group/2,
init_per_testcase/2, end_per_testcase/2]). init_per_testcase/2, end_per_testcase/2]).
%% Test cases %% Test cases
-export([ -export([

View File

@ -0,0 +1 @@
extends typescript