mirror of
1
0
Fork 0

Updated plugins

This commit is contained in:
Amir Salihefendic 2018-07-30 23:18:16 +02:00
parent 46f1a1bd52
commit 587a185a98
89 changed files with 2619 additions and 1634 deletions

View File

@ -31,6 +31,6 @@ call ale#linter#Define('apiblueprint', {
\ 'name': 'drafter', \ 'name': 'drafter',
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'drafter', \ 'executable': 'drafter',
\ 'command': 'drafter --use-line-num --validate %t', \ 'command': 'drafter --use-line-num --validate',
\ 'callback': 'ale_linters#apiblueprint#drafter#HandleErrors', \ 'callback': 'ale_linters#apiblueprint#drafter#HandleErrors',
\}) \})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for AsciiDoc files " Description: write-good for AsciiDoc files
call ale#linter#Define('asciidoc', { call ale#handlers#writegood#DefineLinter('asciidoc')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -14,10 +14,10 @@ function! ale_linters#c#clang#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer)) return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer))
\ . ' -S -x c -fsyntax-only ' \ . ' -S -x c -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'c_clang_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'c_clang_options')) . ' -'
endfunction endfunction
call ale#linter#Define('c', { call ale#linter#Define('c', {
@ -28,5 +28,5 @@ call ale#linter#Define('c', {
\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
\ {'callback': 'ale_linters#c#clang#GetCommand'} \ {'callback': 'ale_linters#c#clang#GetCommand'}
\ ], \ ],
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\}) \})

View File

@ -0,0 +1,29 @@
" Author: Andrey Melentyev <andrey.melentyev@protonmail.com>
" Description: Clangd language server
call ale#Set('c_clangd_executable', 'clangd')
call ale#Set('c_clangd_options', '')
function! ale_linters#c#clangd#GetProjectRoot(buffer) abort
let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json')
return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : ''
endfunction
function! ale_linters#c#clangd#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'c_clangd_executable')
endfunction
function! ale_linters#c#clangd#GetCommand(buffer) abort
let l:executable = ale_linters#c#clangd#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'c_clangd_options')
return ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '')
endfunction
call ale#linter#Define('c', {
\ 'name': 'clangd',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#c#clangd#GetExecutable',
\ 'command_callback': 'ale_linters#c#clangd#GetCommand',
\ 'project_root_callback': 'ale_linters#c#clangd#GetProjectRoot',
\})

View File

@ -35,7 +35,7 @@ function! s:GetBuildDirectory(buffer) abort
return l:build_dir return l:build_dir
endif endif
return ale#c#FindCompileCommands(a:buffer) return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer))
endfunction endfunction
function! ale_linters#c#clangtidy#GetCommand(buffer) abort function! ale_linters#c#clangtidy#GetCommand(buffer) abort

View File

@ -0,0 +1,32 @@
" Author: Ben Falconer <ben@falconers.me.uk>, jtalowell <jtalowell@protonmail.com>
" Description: A language server for C
call ale#Set('c_cquery_executable', 'cquery')
call ale#Set('c_cquery_cache_directory', expand('~/.cache/cquery'))
function! ale_linters#c#cquery#GetProjectRoot(buffer) abort
let l:project_root = ale#path#FindNearestFile(a:buffer, 'compile_commands.json')
return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : ''
endfunction
function! ale_linters#c#cquery#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'c_cquery_executable')
endfunction
function! ale_linters#c#cquery#GetCommand(buffer) abort
let l:executable = ale_linters#c#cquery#GetExecutable(a:buffer)
return ale#Escape(l:executable)
endfunction
function! ale_linters#c#cquery#GetInitializationOptions(buffer) abort
return {'cacheDirectory': ale#Var(a:buffer, 'c_cquery_cache_directory')}
endfunction
call ale#linter#Define('c', {
\ 'name': 'cquery',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#c#cquery#GetExecutable',
\ 'command_callback': 'ale_linters#c#cquery#GetCommand',
\ 'project_root_callback': 'ale_linters#c#cquery#GetProjectRoot',
\ 'initialization_options_callback': 'ale_linters#c#cquery#GetInitializationOptions',
\})

View File

@ -14,10 +14,10 @@ function! ale_linters#c#gcc#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer)) return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer))
\ . ' -S -x c -fsyntax-only ' \ . ' -S -x c -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'c_gcc_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'c_gcc_options')) . ' -'
endfunction endfunction
call ale#linter#Define('c', { call ale#linter#Define('c', {
@ -28,5 +28,5 @@ call ale#linter#Define('c', {
\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
\ {'callback': 'ale_linters#c#gcc#GetCommand'} \ {'callback': 'ale_linters#c#gcc#GetCommand'}
\ ], \ ],
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\}) \})

View File

@ -27,6 +27,6 @@ call ale#linter#Define('clojure', {
\ 'name': 'joker', \ 'name': 'joker',
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'joker', \ 'executable': 'joker',
\ 'command': 'joker --lint %t', \ 'command': 'joker --working-dir %s --lint %t',
\ 'callback': 'ale_linters#clojure#joker#HandleJokerFormat', \ 'callback': 'ale_linters#clojure#joker#HandleJokerFormat',
\}) \})

View File

@ -14,10 +14,10 @@ function! ale_linters#cpp#clang#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#cpp#clang#GetExecutable(a:buffer)) return ale#Escape(ale_linters#cpp#clang#GetExecutable(a:buffer))
\ . ' -S -x c++ -fsyntax-only ' \ . ' -S -x c++ -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'cpp_clang_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'cpp_clang_options')) . ' -'
endfunction endfunction
call ale#linter#Define('cpp', { call ale#linter#Define('cpp', {
@ -28,5 +28,5 @@ call ale#linter#Define('cpp', {
\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
\ {'callback': 'ale_linters#cpp#clang#GetCommand'}, \ {'callback': 'ale_linters#cpp#clang#GetCommand'},
\ ], \ ],
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\}) \})

View File

@ -16,7 +16,7 @@ function! ale_linters#cpp#clangcheck#GetCommand(buffer) abort
let l:build_dir = ale#Var(a:buffer, 'c_build_dir') let l:build_dir = ale#Var(a:buffer, 'c_build_dir')
if empty(l:build_dir) if empty(l:build_dir)
let l:build_dir = ale#c#FindCompileCommands(a:buffer) let l:build_dir = ale#path#Dirname(ale#c#FindCompileCommands(a:buffer))
endif endif
" The extra arguments in the command are used to prevent .plist files from " The extra arguments in the command are used to prevent .plist files from

View File

@ -29,7 +29,7 @@ function! s:GetBuildDirectory(buffer) abort
return l:build_dir return l:build_dir
endif endif
return ale#c#FindCompileCommands(a:buffer) return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer))
endfunction endfunction
function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort

View File

@ -14,10 +14,10 @@ function! ale_linters#cpp#gcc#GetCommand(buffer, output) abort
" -iquote with the directory the file is in makes #include work for " -iquote with the directory the file is in makes #include work for
" headers in the same directory. " headers in the same directory.
return ale#Escape(ale_linters#cpp#gcc#GetExecutable(a:buffer)) return ale#Escape(ale_linters#cpp#gcc#GetExecutable(a:buffer))
\ . ' -S -x c++ -fsyntax-only ' \ . ' -S -x c++ -fsyntax-only'
\ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
\ . l:cflags \ . ale#Pad(l:cflags)
\ . ale#Var(a:buffer, 'cpp_gcc_options') . ' -' \ . ale#Pad(ale#Var(a:buffer, 'cpp_gcc_options')) . ' -'
endfunction endfunction
call ale#linter#Define('cpp', { call ale#linter#Define('cpp', {
@ -29,5 +29,5 @@ call ale#linter#Define('cpp', {
\ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'},
\ {'callback': 'ale_linters#cpp#gcc#GetCommand'}, \ {'callback': 'ale_linters#cpp#gcc#GetCommand'},
\ ], \ ],
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\}) \})

View File

@ -28,7 +28,7 @@ endfunction
call ale#linter#Define('elixir', { call ale#linter#Define('elixir', {
\ 'name': 'dialyxir', \ 'name': 'dialyxir',
\ 'executable': 'mix', \ 'executable': 'mix',
\ 'command': 'mix dialyzer', \ 'command': 'mix help dialyzer && mix dialyzer',
\ 'callback': 'ale_linters#elixir#dialyxir#Handle', \ 'callback': 'ale_linters#elixir#dialyxir#Handle',
\}) \})

View File

@ -136,7 +136,7 @@ function! ale_linters#elm#make#ParseMessage(message) abort
endfunction endfunction
function! ale_linters#elm#make#ParseMessageItem(item) abort function! ale_linters#elm#make#ParseMessageItem(item) abort
if type(a:item) == type('') if type(a:item) is v:t_string
return a:item return a:item
else else
return a:item.string return a:item.string

View File

@ -0,0 +1,27 @@
" Author: unpairedbracket ben.spiers22@gmail.com
" Description: A language server for fortran
call ale#Set('fortran_language_server_executable', 'fortls')
call ale#Set('fortran_language_server_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#fortran#language_server#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'fortran_language_server_executable')
endfunction
function! ale_linters#fortran#language_server#GetCommand(buffer) abort
return ale#Escape(ale_linters#fortran#language_server#GetExecutable(a:buffer))
endfunction
function! ale_linters#fortran#language_server#GetProjectRoot(buffer) abort
let l:fortls_file = ale#path#FindNearestFile(a:buffer, '.fortls')
return !empty(l:fortls_file) ? fnamemodify(l:fortls_file, ':h') : ''
endfunction
call ale#linter#Define('fortran', {
\ 'name': 'language_server',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#fortran#language_server#GetExecutable',
\ 'command_callback': 'ale_linters#fortran#language_server#GetCommand',
\ 'project_root_callback': 'ale_linters#fortran#language_server#GetProjectRoot',
\})

View File

@ -70,7 +70,8 @@ function! ale_linters#go#gobuild#Handler(buffer, lines) abort
endfunction endfunction
call ale#linter#Define('go', { call ale#linter#Define('go', {
\ 'name': 'go build', \ 'name': 'gobuild',
\ 'aliases': ['go build'],
\ 'executable': 'go', \ 'executable': 'go',
\ 'command_chain': [ \ 'command_chain': [
\ {'callback': 'ale_linters#go#gobuild#GoEnv', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#go#gobuild#GoEnv', 'output_stream': 'stdout'},

View File

@ -9,7 +9,8 @@ function! ale_linters#go#govet#GetCommand(buffer) abort
endfunction endfunction
call ale#linter#Define('go', { call ale#linter#Define('go', {
\ 'name': 'go vet', \ 'name': 'govet',
\ 'aliases': ['go vet'],
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'go', \ 'executable': 'go',
\ 'command_callback': 'ale_linters#go#govet#GetCommand', \ 'command_callback': 'ale_linters#go#govet#GetCommand',

View File

@ -0,0 +1,19 @@
" Author: Eric Wolf <ericwolf42@gmail.com>
" Description: ghc for Haskell files called with cabal exec
call ale#Set('haskell_cabal_ghc_options', '-fno-code -v0')
function! ale_linters#haskell#cabal_ghc#GetCommand(buffer) abort
return 'cabal exec -- ghc '
\ . ale#Var(a:buffer, 'haskell_cabal_ghc_options')
\ . ' %t'
endfunction
call ale#linter#Define('haskell', {
\ 'name': 'cabal_ghc',
\ 'aliases': ['cabal-ghc'],
\ 'output_stream': 'stderr',
\ 'executable': 'cabal',
\ 'command_callback': 'ale_linters#haskell#cabal_ghc#GetCommand',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\})

View File

@ -2,14 +2,16 @@
" Description: ghc-mod for Haskell files " Description: ghc-mod for Haskell files
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'ghc-mod', \ 'name': 'ghc_mod',
\ 'aliases': ['ghc-mod'],
\ 'executable': 'ghc-mod', \ 'executable': 'ghc-mod',
\ 'command': 'ghc-mod --map-file %s=%t check %s', \ 'command': 'ghc-mod --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat', \ 'callback': 'ale#handlers#haskell#HandleGHCFormat',
\}) \})
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'stack-ghc-mod', \ 'name': 'stack_ghc_mod',
\ 'aliases': ['stack-ghc-mod'],
\ 'executable': 'stack', \ 'executable': 'stack',
\ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s', \ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s',
\ 'callback': 'ale#handlers#haskell#HandleGHCFormat', \ 'callback': 'ale#handlers#haskell#HandleGHCFormat',

View File

@ -13,7 +13,8 @@ function! ale_linters#haskell#stack_build#GetCommand(buffer) abort
endfunction endfunction
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'stack-build', \ 'name': 'stack_build',
\ 'aliases': ['stack-build'],
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'stack', \ 'executable': 'stack',
\ 'command_callback': 'ale_linters#haskell#stack_build#GetCommand', \ 'command_callback': 'ale_linters#haskell#stack_build#GetCommand',

View File

@ -2,7 +2,8 @@
" Description: ghc for Haskell files, using Stack " Description: ghc for Haskell files, using Stack
call ale#linter#Define('haskell', { call ale#linter#Define('haskell', {
\ 'name': 'stack-ghc', \ 'name': 'stack_ghc',
\ 'aliases': ['stack-ghc'],
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'stack', \ 'executable': 'stack',
\ 'command': 'stack ghc -- -fno-code -v0 %t', \ 'command': 'stack ghc -- -fno-code -v0 %t',

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for vim Help files " Description: write-good for vim Help files
call ale#linter#Define('help', { call ale#handlers#writegood#DefineLinter('help')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for nroff files " Description: write-good for html files
call ale#linter#Define('html', { call ale#handlers#writegood#DefineLinter('html')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,3 +1,4 @@
scriptencoding utf-8
" 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_executable', 'remark')
@ -43,7 +44,8 @@ function! ale_linters#markdown#remark_lint#Handle(buffer, lines) abort
endfunction endfunction
call ale#linter#Define('markdown', { call ale#linter#Define('markdown', {
\ 'name': 'remark-lint', \ 'name': 'remark_lint',
\ 'aliases': ['remark-lint'],
\ 'executable_callback': 'ale_linters#markdown#remark_lint#GetExecutable', \ 'executable_callback': 'ale_linters#markdown#remark_lint#GetExecutable',
\ 'command_callback': 'ale_linters#markdown#remark_lint#GetCommand', \ 'command_callback': 'ale_linters#markdown#remark_lint#GetCommand',
\ 'callback': 'ale_linters#markdown#remark_lint#Handle', \ 'callback': 'ale_linters#markdown#remark_lint#Handle',

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for Markdown files " Description: write-good for Markdown files
call ale#linter#Define('markdown', { call ale#handlers#writegood#DefineLinter('markdown')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for nroff files " Description: write-good for nroff files
call ale#linter#Define('nroff', { call ale#handlers#writegood#DefineLinter('nroff')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -19,5 +19,5 @@ call ale#linter#Define('objc', {
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'clang', \ 'executable': 'clang',
\ 'command_callback': 'ale_linters#objc#clang#GetCommand', \ 'command_callback': 'ale_linters#objc#clang#GetCommand',
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\}) \})

View File

@ -19,5 +19,5 @@ call ale#linter#Define('objcpp', {
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'clang++', \ 'executable': 'clang++',
\ 'command_callback': 'ale_linters#objcpp#clang#GetCommand', \ 'command_callback': 'ale_linters#objcpp#clang#GetCommand',
\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', \ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes',
\}) \})

View File

@ -1,9 +1,4 @@
" Author: Cian Butler https://github.com/butlerx " Author: Cian Butler https://github.com/butlerx
" Description: write-good for PO files " Description: write-good for PO files
call ale#linter#Define('po', { call ale#handlers#writegood#DefineLinter('po')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for Pod files " Description: write-good for Pod files
call ale#linter#Define('pod', { call ale#handlers#writegood#DefineLinter('pod')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -0,0 +1,85 @@
" Author: Yauheni Kirylau <actionless.loveless@gmail.com>
" Description: vulture linting for python files
call ale#Set('python_vulture_executable', 'vulture')
call ale#Set('python_vulture_options', '')
call ale#Set('python_vulture_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_vulture_change_directory', 1)
" The directory to change to before running vulture
function! s:GetDir(buffer) abort
let l:project_root = ale#python#FindProjectRoot(a:buffer)
return !empty(l:project_root)
\ ? l:project_root
\ : expand('#' . a:buffer . ':p:h')
endfunction
function! ale_linters#python#vulture#GetExecutable(buffer) abort
return ale#python#FindExecutable(a:buffer, 'python_vulture', ['vulture'])
endfunction
function! ale_linters#python#vulture#GetCommand(buffer) abort
let l:change_dir = ale#Var(a:buffer, 'python_vulture_change_directory')
\ ? ale#path#CdString(s:GetDir(a:buffer))
\ : ''
let l:executable = ale_linters#python#vulture#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run vulture'
\ : ''
let l:lint_dest = ale#Var(a:buffer, 'python_vulture_change_directory')
\ ? ' .'
\ : ' %s'
return l:change_dir
\ . ale#Escape(l:executable) . l:exec_args
\ . ' '
\ . ale#Var(a:buffer, 'python_vulture_options')
\ . l:lint_dest
endfunction
function! ale_linters#python#vulture#Handle(buffer, lines) abort
for l:line in a:lines[:10]
if match(l:line, '^Traceback') >= 0
return [{
\ 'lnum': 1,
\ 'text': 'An exception was thrown. See :ALEDetail',
\ 'detail': join(a:lines, "\n"),
\}]
endif
endfor
" Matches patterns line the following:
let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+): (.*)$'
let l:output = []
let l:dir = s:GetDir(a:buffer)
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:abspath = ale#path#GetAbsPath(l:dir, l:match[1])
let l:item = {
\ 'filename': l:abspath,
\ 'lnum': l:match[2] + 0,
\ 'text': l:match[3],
\ 'type': 'W',
\}
call add(l:output, l:item)
endfor
return l:output
endfunction
call ale#linter#Define('python', {
\ 'name': 'vulture',
\ 'executable_callback': 'ale_linters#python#vulture#GetExecutable',
\ 'command_callback': 'ale_linters#python#vulture#GetCommand',
\ 'callback': 'ale_linters#python#vulture#Handle',
\ 'lint_file': 1,
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for reStructuredText files " Description: write-good for reStructuredText files
call ale#linter#Define('rst', { call ale#handlers#writegood#DefineLinter('rst')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -9,7 +9,8 @@ endfunction
" Using CM requires that we set "lint_file: 1", since it reads the files " Using CM requires that we set "lint_file: 1", since it reads the files
" from the disk itself. " from the disk itself.
call ale#linter#Define('sml', { call ale#linter#Define('sml', {
\ 'name': 'smlnj-cm', \ 'name': 'smlnj_cm',
\ 'aliases': ['smlnj-cm'],
\ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjCm', \ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjCm',
\ 'lint_file': 1, \ 'lint_file': 1,
\ 'command_callback': 'ale_linters#sml#smlnj_cm#GetCommand', \ 'command_callback': 'ale_linters#sml#smlnj_cm#GetCommand',

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for TeX files " Description: write-good for TeX files
call ale#linter#Define('tex', { call ale#handlers#writegood#DefineLinter('tex')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for Texinfo files " Description: write-good for Texinfo files
call ale#linter#Define('texinfo', { call ale#handlers#writegood#DefineLinter('texinfo')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for text files " Description: write-good for text files
call ale#linter#Define('text', { call ale#handlers#writegood#DefineLinter('text')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

@ -1,9 +1,4 @@
" Author: Sumner Evans <sumner.evans98@gmail.com> " Author: Sumner Evans <sumner.evans98@gmail.com>
" Description: write-good for XHTML files " Description: write-good for XHTML files
call ale#linter#Define('xhtml', { call ale#handlers#writegood#DefineLinter('xhtml')
\ 'name': 'write-good',
\ 'executable_callback': 'ale#handlers#writegood#GetExecutable',
\ 'command_callback': 'ale#handlers#writegood#GetCommand',
\ 'callback': 'ale#handlers#writegood#Handle',
\})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -68,37 +68,46 @@ function! s:FindReferences(linter) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
let [l:line, l:column] = getcurpos()[1:2] let [l:line, l:column] = getcurpos()[1:2]
let l:Callback = a:linter.lsp is# 'tsserver' if a:linter.lsp isnot# 'tsserver'
\ ? function('ale#references#HandleTSServerResponse') let l:column = min([l:column, len(getline(l:line))])
\ : function('ale#references#HandleLSPResponse') endif
let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver' function! OnReady(...) abort closure
let l:message = ale#lsp#tsserver_message#References( let l:Callback = a:linter.lsp is# 'tsserver'
\ l:buffer, \ ? function('ale#references#HandleTSServerResponse')
\ l:line, \ : function('ale#references#HandleLSPResponse')
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" references position probably won't make sense.
call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([l:column, len(getline(l:line))]) call ale#lsp#RegisterCallback(l:id, l:Callback)
let l:message = ale#lsp#message#References(l:buffer, l:line, l:column) if a:linter.lsp is# 'tsserver'
endif let l:message = ale#lsp#tsserver_message#References(
\ l:buffer,
\ l:line,
\ l:column
\)
else
" Send a message saying the buffer has changed first, or the
" references position probably won't make sense.
call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer)
let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) let l:message = ale#lsp#message#References(l:buffer, l:line, l:column)
endif
let s:references_map[l:request_id] = {} let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:references_map[l:request_id] = {}
endfunction
call ale#lsp#WaitForCapability(l:id, l:root, 'references', function('OnReady'))
endfunction endfunction
function! ale#references#Find() abort function! ale#references#Find() abort

View File

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

View File

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

View File

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

View File

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

View File

@ -25,13 +25,28 @@ g:ale_c_build_dir *g:ale_c_build_dir*
Type: |String| Type: |String|
Default: `''` Default: `''`
A path to the directory containing the `compile_commands.json` file to use For programs that can read `compile_commands.json` files, this option can be
with c-family linters. Usually setting this option to a non-empty string set to the directory containing the file for the project. ALE will try to
will override the |g:ale_c_build_dir_names| option to impose a compilation determine the location of `compile_commands.json` automatically, but if your
database (it can be useful if multiple builds are in multiple build file exists in some other directory, you can set this option so ALE will
subdirectories in the project tree). know where it is.
This feature is also most useful for the clang tools linters, wrapped
around LibTooling (namely clang-tidy here) This directory will be searched instead of |g:ale_c_build_dir_names|.
g:ale_c_parse_compile_commands *g:ale_c_parse_compile_commands*
*b:ale_c_parse_compile_commands*
Type: |Number|
Default: `0`
If set to `1`, ALE will parse `compile_commands.json` files to automatically
determine flags for C or C++ compilers. ALE will first search for the
nearest `compile_commands.json` file, and then look for
`compile_commands.json` files in the directories for
|g:ale_c_build_dir_names|.
If |g:ale_c_parse_makefile| or |b:ale_c_parse_makefile| is set to `1`, the
output of `make -n` will be preferred over `compile_commands.json` files.
g:ale_c_parse_makefile *g:ale_c_parse_makefile* g:ale_c_parse_makefile *g:ale_c_parse_makefile*
@ -63,6 +78,25 @@ g:ale_c_clang_options *g:ale_c_clang_options*
This variable can be changed to modify flags given to clang. This variable can be changed to modify flags given to clang.
===============================================================================
clangd *ale-c-clangd*
g:ale_c_clangd_executable *g:ale_c_clangd_executable*
*b:ale_c_clangd_executable*
Type: |String|
Default: `'clangd'`
This variable can be changed to use a different executable for clangd.
g:ale_c_clangd_options *g:ale_c_clangd_options*
*b:ale_c_clangd_options*
Type: |String|
Default: `''`
This variable can be changed to modify flags given to clangd.
=============================================================================== ===============================================================================
clang-format *ale-c-clangformat* clang-format *ale-c-clangformat*
@ -154,6 +188,26 @@ g:ale_c_cppcheck_options *g:ale_c_cppcheck_options*
This variable can be changed to modify flags given to cppcheck. This variable can be changed to modify flags given to cppcheck.
===============================================================================
cquery *ale-c-cquery*
g:ale_c_cquery_executable *g:ale_c_cquery_executable*
*b:ale_c_cquery_executable*
Type: |String|
Default: `'cquery'`
This variable can be changed to use a different executable for cquery.
g:ale_cpp_cquery_cache_directory *g:ale_c_cquery_cache_directory*
*b:ale_c_cquery_cache_directory*
Type: |String|
Default: `'~/.cache/cquery'`
This variable can be changed to decide which directory cquery uses for its
cache.
=============================================================================== ===============================================================================
flawfinder *ale-c-flawfinder* flawfinder *ale-c-flawfinder*

View File

@ -10,6 +10,7 @@ The following C options also apply to some C++ linters too.
* |g:ale_c_build_dir_names| * |g:ale_c_build_dir_names|
* |g:ale_c_build_dir| * |g:ale_c_build_dir|
* |g:ale_c_parse_makefile| * |g:ale_c_parse_makefile|
* |g:ale_c_parse_compile_commands|
=============================================================================== ===============================================================================

View File

@ -115,6 +115,10 @@ these are reported with ALE's `custom-linting-rules` script. See
* Don't use the `tempname()` function. It doesn't work when `$TMPDIR` isn't * Don't use the `tempname()` function. It doesn't work when `$TMPDIR` isn't
set. Use `ale#util#Tempname()` instead, which temporarily sets `$TMPDIR` set. Use `ale#util#Tempname()` instead, which temporarily sets `$TMPDIR`
appropriately where needed. appropriately where needed.
* Use `snake_case` names for linter names, so they can be used as part of
variable names. You can define `aliases` for linters, for other names people
might try to configure linters with.
* Use |v:t_TYPE| variables instead of `type()`, which are more readable.
Apply the following guidelines when writing Vader test files. Apply the following guidelines when writing Vader test files.
@ -145,9 +149,10 @@ ALE is tested with a suite of tests executed in Travis CI and AppVeyor. ALE
runs tests with the following versions of Vim in the following environments. runs tests with the following versions of Vim in the following environments.
1. Vim 8.0.0027 on Linux via Travis CI. 1. Vim 8.0.0027 on Linux via Travis CI.
2. NeoVim 0.2.0 on Linux via Travis CI. 2. Vim 8.1.0204 on Linux via Travis CI.
3. NeoVim 0.3.0 on Linux via Travis CI. 3. NeoVim 0.2.0 on Linux via Travis CI.
4. Vim 8 (stable builds) on Windows via AppVeyor. 4. NeoVim 0.3.0 on Linux via Travis CI.
5. Vim 8 (stable builds) on Windows via AppVeyor.
If you are developing ALE code on Linux, Mac OSX, or BSD, you can run ALEs If you are developing ALE code on Linux, Mac OSX, or BSD, you can run ALEs
tests by installing Docker and running the `run-tests` script. Follow the tests by installing Docker and running the `run-tests` script. Follow the

View File

@ -32,5 +32,24 @@ g:ale_fortran_gcc_use_free_form *g:ale_fortran_gcc_use_free_form*
instead, for checking files with fixed form layouts. instead, for checking files with fixed form layouts.
===============================================================================
language_server *ale-fortran-language-server*
g:ale_fortran_language_server_executable *g:ale_fortran_language_server_executable*
*b:ale_fortran_language_server_executable*
Type: |String|
Default: `'fortls'`
This variable can be changed to modify the executable used for the Fortran
Language Server.
g:ale_fortran_language_server_use_global *g:ale_fortran_language_server_use_global*
*b:ale_fortran_language_server_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

@ -22,6 +22,17 @@ g:ale_haskell_ghc_options *g:ale_haskell_ghc_options*
This variable can be changed to modify flags given to ghc. This variable can be changed to modify flags given to ghc.
===============================================================================
cabal-ghc *ale-haskell-cabal-ghc*
g:ale_haskell_cabal_ghc_options *g:ale_haskell_cabal_ghc_options*
*b:ale_haskell_cabal_ghc_options*
Type: |String|
Default: `'-fno-code -v0'`
This variable can be changed to modify flags given to ghc through cabal
exec.
=============================================================================== ===============================================================================
hdevtools *ale-haskell-hdevtools* hdevtools *ale-haskell-hdevtools*

View File

@ -397,6 +397,36 @@ g:ale_python_pyre_use_global *g:ale_python_pyre_use_global*
See |ale-integrations-local-executables| See |ale-integrations-local-executables|
===============================================================================
vulture *ale-python-vulture*
g:ale_python_vulture_change_directory *g:ale_python_vulture_change_directory*
*b:ale_python_vulture_change_directory*
Type: |Number|
Default: `1`
If set to `1`, ALE will switch to the directory the Python file being
checked with `vulture` is in before checking it and check the whole project
directory instead of checking only the file opened in the current buffer.
This helps `vulture` to know the context and avoid false-negative results.
g:ale_python_vulture_executable *g:ale_python_vulture_executable*
*b:ale_python_vulture_executable*
Type: |String|
Default: `'vulture'`
See |ale-integrations-local-executables|
g:ale_python_vulture_use_global *g:ale_python_vulture_use_global*
*b:ale_python_vulture_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
=============================================================================== ===============================================================================
yapf *ale-python-yapf* yapf *ale-python-yapf*

View File

@ -26,9 +26,11 @@ CONTENTS *ale-contents*
gawk................................|ale-awk-gawk| gawk................................|ale-awk-gawk|
c.....................................|ale-c-options| c.....................................|ale-c-options|
clang...............................|ale-c-clang| clang...............................|ale-c-clang|
clangd..............................|ale-c-clangd|
clang-format........................|ale-c-clangformat| clang-format........................|ale-c-clangformat|
clangtidy...........................|ale-c-clangtidy| clangtidy...........................|ale-c-clangtidy|
cppcheck............................|ale-c-cppcheck| cppcheck............................|ale-c-cppcheck|
cquery..............................|ale-c-cquery|
flawfinder..........................|ale-c-flawfinder| flawfinder..........................|ale-c-flawfinder|
gcc.................................|ale-c-gcc| gcc.................................|ale-c-gcc|
chef..................................|ale-chef-options| chef..................................|ale-chef-options|
@ -76,6 +78,7 @@ CONTENTS *ale-contents*
fish..................................|ale-fish-options| fish..................................|ale-fish-options|
fortran...............................|ale-fortran-options| fortran...............................|ale-fortran-options|
gcc.................................|ale-fortran-gcc| gcc.................................|ale-fortran-gcc|
language_server.....................|ale-fortran-language-server|
fountain..............................|ale-fountain-options| fountain..............................|ale-fountain-options|
fusionscript..........................|ale-fuse-options| fusionscript..........................|ale-fuse-options|
fusion-lint.........................|ale-fuse-fusionlint| fusion-lint.........................|ale-fuse-fusionlint|
@ -98,6 +101,7 @@ CONTENTS *ale-contents*
haskell...............................|ale-haskell-options| haskell...............................|ale-haskell-options|
brittany............................|ale-haskell-brittany| brittany............................|ale-haskell-brittany|
ghc.................................|ale-haskell-ghc| ghc.................................|ale-haskell-ghc|
cabal-ghc...........................|ale-haskell-cabal-ghc|
hdevtools...........................|ale-haskell-hdevtools| hdevtools...........................|ale-haskell-hdevtools|
hfmt................................|ale-haskell-hfmt| hfmt................................|ale-haskell-hfmt|
stack-build.........................|ale-haskell-stack-build| stack-build.........................|ale-haskell-stack-build|
@ -203,6 +207,7 @@ CONTENTS *ale-contents*
pylint..............................|ale-python-pylint| pylint..............................|ale-python-pylint|
pyls................................|ale-python-pyls| pyls................................|ale-python-pyls|
pyre................................|ale-python-pyre| pyre................................|ale-python-pyre|
vulture.............................|ale-python-vulture|
yapf................................|ale-python-yapf| yapf................................|ale-python-yapf|
qml...................................|ale-qml-options| qml...................................|ale-qml-options|
qmlfmt..............................|ale-qml-qmlfmt| qmlfmt..............................|ale-qml-qmlfmt|
@ -333,7 +338,7 @@ Notes:
* Awk: `gawk` * Awk: `gawk`
* Bash: `language-server`, `shell` (-n flag), `shellcheck`, `shfmt` * Bash: `language-server`, `shell` (-n flag), `shellcheck`, `shfmt`
* Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt` * Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt`
* C: `cppcheck`, `cpplint`!!, `clang`, `clangtidy`!!, `clang-format`, `flawfinder`, `gcc` * C: `cppcheck`, `cpplint`!!, `clang`, `clangd`, `clangtidy`!!, `clang-format`, `cquery`, `flawfinder`, `gcc`
* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc` * C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc`
* CUDA: `nvcc`!! * CUDA: `nvcc`!!
* C#: `mcs`, `mcsc`!! * C#: `mcs`, `mcsc`!!
@ -355,7 +360,7 @@ Notes:
* Erb: `erb`, `erubi`, `erubis` * Erb: `erb`, `erubi`, `erubis`
* Erlang: `erlc`, `SyntaxErl` * Erlang: `erlc`, `SyntaxErl`
* Fish: `fish` (-n flag) * Fish: `fish` (-n flag)
* Fortran: `gcc` * Fortran: `gcc`, `language_server`
* Fountain: `proselint` * Fountain: `proselint`
* FusionScript: `fusion-lint` * FusionScript: `fusion-lint`
* Git Commit Messages: `gitlint` * Git Commit Messages: `gitlint`
@ -364,7 +369,7 @@ Notes:
* GraphQL: `eslint`, `gqlint`, `prettier` * GraphQL: `eslint`, `gqlint`, `prettier`
* Haml: `haml-lint` * Haml: `haml-lint`
* Handlebars: `ember-template-lint` * Handlebars: `ember-template-lint`
* Haskell: `brittany`, `ghc`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `stack-ghc-mod`, `hlint`, `hdevtools`, `hfmt` * Haskell: `brittany`, `ghc`, `cabal-ghc`, `stack-ghc`, `stack-build`!!, `ghc-mod`, `stack-ghc-mod`, `hlint`, `hdevtools`, `hfmt`
* HTML: `alex`!!, `HTMLHint`, `proselint`, `tidy`, `write-good` * HTML: `alex`!!, `HTMLHint`, `proselint`, `tidy`, `write-good`
* Idris: `idris` * Idris: `idris`
* Java: `checkstyle`, `javac`, `google-java-format`, `PMD` * Java: `checkstyle`, `javac`, `google-java-format`, `PMD`
@ -395,7 +400,7 @@ Notes:
* proto: `protoc-gen-lint` * proto: `protoc-gen-lint`
* Pug: `pug-lint` * Pug: `pug-lint`
* Puppet: `languageserver`, `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`!!, `vulture`!!, `yapf`
* QML: `qmlfmt`, `qmllint` * QML: `qmlfmt`, `qmllint`
* R: `lintr` * R: `lintr`
* ReasonML: `merlin`, `ols`, `refmt` * ReasonML: `merlin`, `ols`, `refmt`
@ -1413,8 +1418,7 @@ g:ale_set_balloons *g:ale_set_balloons*
*b:ale_set_balloons* *b:ale_set_balloons*
Type: |Number| Type: |Number|
Default: `(has('balloon_eval') && has('gui_running'))` Default: `has('balloon_eval') && has('gui_running')`
`|| (has('balloon_eval_term') && !has('gui_running'))`
When this option is set to `1`, balloon messages will be displayed for When this option is set to `1`, balloon messages will be displayed for
problems or hover information if available. problems or hover information if available.
@ -1424,6 +1428,12 @@ g:ale_set_balloons *g:ale_set_balloons*
supporting "Hover" information, per |ale-hover|, then brief information supporting "Hover" information, per |ale-hover|, then brief information
about the symbol under the cursor will be displayed in a balloon. about the symbol under the cursor will be displayed in a balloon.
Balloons can be enabled for terminal versions of Vim that support balloons,
but some versions of Vim will produce strange mouse behavior when balloons
are enabled. To configure balloons for your terminal, you should first
configure your |ttymouse| setting, and then consider setting
`g:ale_set_balloons` to `1` before ALE is loaded.
`b:ale_set_balloons` can be set to `0` to disable balloons for a buffer. `b:ale_set_balloons` can be set to `0` to disable balloons for a buffer.
Balloons cannot be enabled for a specific buffer when not initially enabled Balloons cannot be enabled for a specific buffer when not initially enabled
globally. globally.

View File

@ -110,10 +110,7 @@ let g:ale_set_highlights = get(g:, 'ale_set_highlights', has('syntax'))
let g:ale_echo_cursor = get(g:, 'ale_echo_cursor', 1) let g:ale_echo_cursor = get(g:, 'ale_echo_cursor', 1)
" This flag can be set to 0 to disable balloon support. " This flag can be set to 0 to disable balloon support.
let g:ale_set_balloons = get(g:, 'ale_set_balloons', let g:ale_set_balloons = get(g:, 'ale_set_balloons', has('balloon_eval') && has('gui_running'))
\ (has('balloon_eval') && has('gui_running'))
\ || (has('balloon_eval_term') && !has('gui_running'))
\)
" This flag can be set to 0 to disable warnings for trailing whitespace " This flag can be set to 0 to disable warnings for trailing whitespace
let g:ale_warn_about_trailing_whitespace = get(g:, 'ale_warn_about_trailing_whitespace', 1) let g:ale_warn_about_trailing_whitespace = get(g:, 'ale_warn_about_trailing_whitespace', 1)

View File

@ -9,8 +9,8 @@ endif
let g:loaded_commentary = 1 let g:loaded_commentary = 1
function! s:surroundings() abort function! s:surroundings() abort
return split(get(b:, 'commentary_format', substitute(substitute( return split(get(b:, 'commentary_format', substitute(substitute(substitute(
\ &commentstring, '\S\zs%s',' %s','') ,'%s\ze\S', '%s ', '')), '%s', 1) \ &commentstring, '^$', '%s', ''), '\S\zs%s',' %s', '') ,'%s\ze\S', '%s ', '')), '%s', 1)
endfunction endfunction
function! s:strip_white_space(l,r,line) abort function! s:strip_white_space(l,r,line) abort

File diff suppressed because it is too large Load Diff

View File

@ -199,11 +199,7 @@ that are part of Git repositories).
*fugitive-:Gmove* *fugitive-:Gmove*
:Gmove {destination} Wrapper around git-mv that renames the buffer :Gmove {destination} Wrapper around git-mv that renames the buffer
afterward. The destination is relative to the current afterward. Add a ! to pass -f.
directory except when started with a /, in which case
it is relative to the work tree. (This is a holdover
from before |:Grename| and will be removed.) Add a !
to pass -f.
*fugitive-:Grename* *fugitive-:Grename*
:Grename {destination} Like |:Gmove| but operates relative to the parent :Grename {destination} Like |:Gmove| but operates relative to the parent
@ -234,12 +230,11 @@ that are part of Git repositories).
<CR> q, then open commit <CR> q, then open commit
o open commit in horizontal split o open commit in horizontal split
O open commit in new tab O open commit in new tab
p open commit in preview window
- reblame at commit - reblame at commit
~ reblame at [count]th first grandparent ~ reblame at [count]th first grandparent
P reblame at [count]th parent (like HEAD^[count]) P reblame at [count]th parent (like HEAD^[count])
:[range]Gblame [flags] Run git-blame on the given range.
*fugitive-:Gbrowse* *fugitive-:Gbrowse*
:Gbrowse Open the current file, blob, tree, commit, or tag :Gbrowse Open the current file, blob, tree, commit, or tag
in your browser at the upstream hosting provider. in your browser at the upstream hosting provider.
@ -251,12 +246,6 @@ that are part of Git repositories).
supported by installing rhubarb.vim, available at supported by installing rhubarb.vim, available at
<https://github.com/tpope/vim-rhubarb>. <https://github.com/tpope/vim-rhubarb>.
The hosting provider is determined by looking at the
remote for the current or specified branch and falls
back to "origin". In the special case of a "."
remote, a local instance of git-instaweb will be
started and used.
:Gbrowse {revision} Like :Gbrowse, but for a given |fugitive-revision|. A :Gbrowse {revision} Like :Gbrowse, but for a given |fugitive-revision|. A
useful value here is -, which ties the URL to the useful value here is -, which ties the URL to the
latest commit rather than a volatile branch. latest commit rather than a volatile branch.
@ -334,19 +323,19 @@ refs/heads/x .git/refs/heads/x
@ The commit referenced by @ aka HEAD @ The commit referenced by @ aka HEAD
master^ The parent of the commit referenced by master master^ The parent of the commit referenced by master
master: The tree referenced by master master: The tree referenced by master
/master The file named master in the work tree ./master The file named master in the work tree
Makefile The file named Makefile in the work tree Makefile The file named Makefile in the work tree
@^:Makefile The file named Makefile in the parent of HEAD @^:Makefile The file named Makefile in the parent of HEAD
:Makefile The file named Makefile in the index (writable) :Makefile The file named Makefile in the index (writable)
@:% The current file in HEAD
- The current file in HEAD - The current file in HEAD
^ The current file in the previous commit ^ The current file in the previous commit
~3 The current file 3 commits ago ~3 The current file 3 commits ago
: .git/index (Same as |:Gstatus|) : .git/index (Same as |:Gstatus|)
:0 The current file in the index :% The current file in the index
:1 The current file's common ancestor during a conflict :1:% The current file's common ancestor during a conflict
:2 The current file in the target branch during a conflict :2:% The current file in the target branch during a conflict
:3 The current file in the merged branch during a conflict :3:% The current file in the merged branch during a conflict
:/foo The most recent commit with "foo" in the message
STATUSLINE *fugitive-statusline* STATUSLINE *fugitive-statusline*

View File

@ -16,6 +16,18 @@ function! s:shellslash(path) abort
endif endif
endfunction endfunction
function! FugitiveGitDir(...) abort
if !a:0 || a:1 ==# -1
return get(b:, 'git_dir', '')
elseif type(a:1) == type(0)
return getbufvar(a:1, 'git_dir')
elseif type(a:1) == type('')
return substitute(s:shellslash(a:1), '/$', '', '')
else
return ''
endif
endfunction
function! FugitiveIsGitDir(path) abort function! FugitiveIsGitDir(path) abort
let path = substitute(a:path, '[\/]$', '', '') . '/' let path = substitute(a:path, '[\/]$', '', '') . '/'
return getfsize(path.'HEAD') > 10 && ( return getfsize(path.'HEAD') > 10 && (
@ -25,8 +37,8 @@ endfunction
let s:worktree_for_dir = {} let s:worktree_for_dir = {}
let s:dir_for_worktree = {} let s:dir_for_worktree = {}
function! FugitiveTreeForGitDir(git_dir) abort function! FugitiveWorkTree(...) abort
let dir = substitute(s:shellslash(a:git_dir), '/$', '', '') let dir = substitute(s:shellslash(a:0 ? a:1 : get(b:, 'git_dir', '')), '/$', '', '')
if dir =~# '/\.git$' if dir =~# '/\.git$'
return len(dir) ==# 5 ? '/' : dir[0:-6] return len(dir) ==# 5 ? '/' : dir[0:-6]
endif endif
@ -57,6 +69,73 @@ function! FugitiveTreeForGitDir(git_dir) abort
endif endif
endfunction endfunction
function! FugitiveReal(...) abort
let file = a:0 ? a:1 : @%
if file =~? '^fugitive:' || a:0 > 1
return call('fugitive#Real', [file] + a:000[1:-1])
elseif file =~# '^/\|^\a\+:\|^$'
return file
else
return fnamemodify(file, ':p' . (file =~# '[\/]$' ? '' : ':s?[\/]$??'))
endif
endfunction
function! FugitivePath(...) abort
if a:0 > 1
return fugitive#Path(a:1, a:2, FugitiveGitDir(a:0 > 2 ? a:3 : -1))
else
return FugitiveReal(a:0 ? a:1 : @%)
endif
endfunction
function! FugitiveGenerate(...) abort
if a:0 && s:shellslash(a:0) =~# '^\%(\a\a\+:\)\=\%(a:\)\=/\|^[~$]'
return a:1
endif
return fugitive#repo(FugitiveGitDir(a:0 > 1 ? a:2 : -1)).translate(a:0 ? a:1 : '', 1)
endfunction
function! FugitiveRoute(...) abort
return call('FugitiveGenerate', a:000)
endfunction
function! FugitiveParse(...) abort
let path = s:shellslash(a:0 ? a:1 : @%)
let vals = matchlist(path, '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40\}\|[0-3]\)\(/.*\)\=$')
if len(vals)
return [(vals[2] =~# '^.$' ? ':' : '') . vals[2] . substitute(vals[3], '^/', ':', ''), vals[1]]
endif
let v:errmsg = 'fugitive: invalid Fugitive URL ' . path
throw v:errmsg
endfunction
function! FugitiveConfig(key, ...) abort
return fugitive#Config(a:key, FugitiveGitDir(a:0 ? a:1 : -1))
endfunction
function! FugitiveRemoteUrl(...) abort
return fugitive#RemoteUrl(a:0 ? a:1 : '', FugitiveGitDir(a:0 > 1 ? a:2 : -1))
endfunction
function! FugitiveHead(...) abort
let dir = FugitiveGitDir(a:0 > 1 ? a:2 : -1)
if empty(dir)
return ''
endif
return fugitive#repo(dir).head(a:0 ? a:1 : 0)
endfunction
function! FugitiveStatusline(...) abort
if !exists('b:git_dir')
return ''
endif
return fugitive#Statusline()
endfunction
function! FugitiveTreeForGitDir(path) abort
return FugitiveWorkTree(a:path)
endfunction
function! FugitiveExtractGitDir(path) abort function! FugitiveExtractGitDir(path) abort
let path = s:shellslash(a:path) let path = s:shellslash(a:path)
if path =~# '^fugitive:' if path =~# '^fugitive:'
@ -66,6 +145,10 @@ function! FugitiveExtractGitDir(path) abort
else else
let path = fnamemodify(path, ':p:h:s?/$??') let path = fnamemodify(path, ':p:h:s?/$??')
endif endif
let pre = substitute(matchstr(path, '^\a\a\+\ze:'), '^.', '\u&', '')
if len(pre) && exists('*' . pre . 'Real')
let path = s:shellslash({pre}Real(path))
endif
let root = resolve(path) let root = resolve(path)
if root !=# path if root !=# path
silent! exe haslocaldir() ? 'lcd .' : 'cd .' silent! exe haslocaldir() ? 'lcd .' : 'cd .'
@ -82,7 +165,7 @@ function! FugitiveExtractGitDir(path) abort
return simplify(fnamemodify($GIT_DIR, ':p:s?[\/]$??')) return simplify(fnamemodify($GIT_DIR, ':p:s?[\/]$??'))
endif endif
if FugitiveIsGitDir($GIT_DIR) if FugitiveIsGitDir($GIT_DIR)
call FugitiveTreeForGitDir(simplify(fnamemodify($GIT_DIR, ':p:s?[\/]$??'))) call FugitiveWorkTree(simplify(fnamemodify($GIT_DIR, ':p:s?[\/]$??')))
if has_key(s:dir_for_worktree, root) if has_key(s:dir_for_worktree, root)
return s:dir_for_worktree[root] return s:dir_for_worktree[root]
endif endif
@ -124,66 +207,50 @@ function! FugitiveDetect(path) abort
endif endif
endfunction endfunction
function! FugitiveStatusline(...) abort
if !exists('b:git_dir')
return ''
endif
return fugitive#Statusline()
endfunction
function! FugitiveHead(...) abort
if !exists('b:git_dir')
return ''
endif
return fugitive#repo().head(a:0 ? a:1 : 0)
endfunction
function! FugitivePath(...) abort
let file = fnamemodify(a:0 ? a:1 : @%, ':p')
if file =~? '^fugitive:'
return fugitive#Path(file)
else
return file
endif
endfunction
function! FugitiveReal(...) abort
return call('FugitivePath', a:000)
endfunction
augroup fugitive augroup fugitive
autocmd! autocmd!
autocmd BufNewFile,BufReadPost * call FugitiveDetect(expand('%:p')) autocmd BufNewFile,BufReadPost * call FugitiveDetect(expand('<amatch>:p'))
autocmd FileType netrw call FugitiveDetect(fnamemodify(get(b:, 'netrw_curdir', @%), ':p')) autocmd FileType netrw call FugitiveDetect(fnamemodify(get(b:, 'netrw_curdir', expand('<amatch>')), ':p'))
autocmd User NERDTreeInit,NERDTreeNewRoot autocmd User NERDTreeInit,NERDTreeNewRoot
\ if exists('b:NERDTree.root.path.str') | \ if exists('b:NERDTree.root.path.str') |
\ call FugitiveDetect(b:NERDTree.root.path.str()) | \ call FugitiveDetect(b:NERDTree.root.path.str()) |
\ endif \ endif
autocmd VimEnter * if expand('<amatch>')==''|call FugitiveDetect(getcwd())|endif autocmd VimEnter * if empty(expand('<amatch>'))|call FugitiveDetect(getcwd())|endif
autocmd CmdWinEnter * call FugitiveDetect(expand('#:p')) autocmd CmdWinEnter * call FugitiveDetect(expand('#:p'))
autocmd FileType git autocmd FileType git
\ if exists('b:git_dir') | \ if exists('b:git_dir') |
\ call fugitive#MapJumps() | \ call fugitive#MapJumps() |
\ endif
autocmd FileType git,gitcommit,gitrebase
\ if exists('b:git_dir') |
\ call fugitive#MapCfile() | \ call fugitive#MapCfile() |
\ endif \ endif
autocmd FileType gitcommit
\ if exists('b:git_dir') |
\ call fugitive#MapCfile('fugitive#StatusCfile()') |
\ endif
autocmd FileType gitrebase
\ let &l:include = '^\%(pick\|squash\|edit\|reword\|fixup\|drop\|[pserfd]\)\>' |
\ if exists('b:git_dir') |
\ let &l:includeexpr = 'v:fname =~# ''^\x\{4,40\}$'' ? FugitiveGenerate(v:fname) : ' .
\ (len(&l:includeexpr) ? &l:includeexpr : 'v:fname') |
\ endif |
\ let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|setl inex= inc='
autocmd BufReadCmd index{,.lock} autocmd BufReadCmd index{,.lock}
\ if FugitiveIsGitDir(expand('<amatch>:p:h')) | \ if FugitiveIsGitDir(expand('<amatch>:p:h')) |
\ let b:git_dir = s:shellslash(expand('<amatch>:p:h')) |
\ exe fugitive#BufReadStatus() | \ exe fugitive#BufReadStatus() |
\ elseif filereadable(expand('<amatch>')) | \ elseif filereadable(expand('<amatch>')) |
\ read <amatch> | \ read <amatch> |
\ 1delete | \ 1delete_ |
\ endif \ endif
autocmd FileReadCmd fugitive://**//[0-3]/** exe fugitive#FileRead() autocmd BufReadCmd fugitive://*//* exe fugitive#BufReadCmd()
autocmd BufReadCmd fugitive://**//[0-3]/** exe fugitive#BufReadIndex() autocmd BufWriteCmd fugitive://*//[0-3]/* exe fugitive#BufWriteCmd()
autocmd BufWriteCmd fugitive://**//[0-3]/** exe fugitive#BufWriteIndex() autocmd FileReadCmd fugitive://*//* exe fugitive#FileReadCmd()
autocmd BufReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe fugitive#BufReadObject() autocmd FileWriteCmd fugitive://*//[0-3]/* exe fugitive#FileWriteCmd()
autocmd FileReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe fugitive#FileRead() if exists('##SourceCmd')
autocmd SourceCmd fugitive://*//* nested exe fugitive#SourceCmd()
endif
autocmd User Flags call Hoist('buffer', function('FugitiveStatusline')) autocmd User Flags call Hoist('buffer', function('FugitiveStatusline'))
augroup END augroup END

View File

@ -100,7 +100,7 @@ function! gitgutter#diff#run_diff(bufnr, preserve_full_diff) abort
call s:write_buffer(a:bufnr, buff_file) call s:write_buffer(a:bufnr, buff_file)
" Call git-diff with the temporary files. " Call git-diff with the temporary files.
let cmd .= g:gitgutter_git_executable.' --no-pager' let cmd .= g:gitgutter_git_executable.' --git-dir="" --no-pager'
if s:c_flag if s:c_flag
let cmd .= ' -c "diff.autorefreshindex=0"' let cmd .= ' -c "diff.autorefreshindex=0"'
let cmd .= ' -c "diff.noprefix=false"' let cmd .= ' -c "diff.noprefix=false"'

View File

@ -1,3 +1,3 @@
[run] [run]
plugins = covimerage plugins = covimerage
data_file = .coverage.covimerage data_file = .coverage_covimerage

View File

@ -3,6 +3,12 @@
IMPROVEMENTS: IMPROVEMENTS:
* Unify async job handling for Vim8 and Neovim. * Unify async job handling for Vim8 and Neovim.
[[GH-1864]](https://github.com/fatih/vim-go/pull/1864) [[GH-1864]](https://github.com/fatih/vim-go/pull/1864)
* Document Vim and Neovim requirements in README.md and help file.
[[GH-1889]](https://github.com/fatih/vim-go/pull/1889)
BUG FIXES:
* Fix `:GoRun %` on Windows.
[[GH-1900]](github.com/fatih/vim-go/pull/1900)
## 1.18 - (July 18, 2018) ## 1.18 - (July 18, 2018)

View File

@ -33,10 +33,13 @@ This plugin adds Go language support for Vim, with the following main features:
## Install ## Install
vim-go requires at least Vim 7.4.1689 or Neovim 0.2.2.
The [**latest stable release**](https://github.com/fatih/vim-go/releases/latest) is the The [**latest stable release**](https://github.com/fatih/vim-go/releases/latest) is the
recommended version to use. If you choose to use the master branch instead, recommended version to use. If you choose to use the master branch instead,
please do so with caution; it is a _development_ branch. please do so with caution; it is a _development_ branch.
vim-go follows the standard runtime path structure. Below are some helper lines vim-go follows the standard runtime path structure. Below are some helper lines
for popular package managers: for popular package managers:
@ -46,6 +49,8 @@ for popular package managers:
* `git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go` * `git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go`
* [vim-plug](https://github.com/junegunn/vim-plug) * [vim-plug](https://github.com/junegunn/vim-plug)
* `Plug 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }` * `Plug 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }`
* [Vundle](https://github.com/VundleVim/Vundle.vim)
* `Plugin 'fatih/vim-go'`
You will also need to install all the necessary binaries. vim-go makes it easy You will also need to install all the necessary binaries. vim-go makes it easy
to install all of them by providing a command, `:GoInstallBinaries`, which will to install all of them by providing a command, `:GoInstallBinaries`, which will

View File

@ -143,7 +143,12 @@ function! go#cmd#Run(bang, ...) abort
endif endif
if go#util#IsWin() if go#util#IsWin()
exec '!' . cmd . go#util#Shelljoin(go#tool#Files()) if a:0 == 0
exec '!' . cmd . go#util#Shelljoin(go#tool#Files(), 1)
else
exec '!' . cmd . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
endif
if v:shell_error if v:shell_error
redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None
else else

View File

@ -7,15 +7,6 @@ function! go#job#Spawn(cmd, args)
return go#job#Start(a:cmd, l:options) return go#job#Start(a:cmd, l:options)
endfunction 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 " 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

View File

@ -76,6 +76,12 @@ tools developed by the Go community to provide a seamless Vim experience.
============================================================================== ==============================================================================
INSTALL *go-install* INSTALL *go-install*
vim-go requires at least Vim 7.4.1689 or Neovim 0.2.2. On macOS, if you are
still using your system version of vim, you can use homebrew to keep your
version of Vim up-to-date with the following terminal command:
>
brew install vim
The latest stable release, https://github.com/fatih/vim-go/releases/latest, is The latest stable release, https://github.com/fatih/vim-go/releases/latest, is
the recommended version to use. If you choose to use the master branch the recommended version to use. If you choose to use the master branch
instead, please do so with caution; it is a _development_ branch. instead, please do so with caution; it is a _development_ branch.

View File

@ -26,7 +26,6 @@ before_script: |
fi fi
if [ "$TRAVIS_OS_NAME" = "osx" ]; then if [ "$TRAVIS_OS_NAME" = "osx" ]; then
curl https://bootstrap.pypa.io/get-pip.py | sudo python curl https://bootstrap.pypa.io/get-pip.py | sudo python
sudo -H easy_install pip
fi fi
sudo -H pip install virtualenv sudo -H pip install virtualenv

View File

@ -5,6 +5,7 @@
Syntax highlighting, matching rules and mappings for [the original Markdown](http://daringfireball.net/projects/markdown/) and extensions. Syntax highlighting, matching rules and mappings for [the original Markdown](http://daringfireball.net/projects/markdown/) and extensions.
1. [Installation](#installation) 1. [Installation](#installation)
1. [Basic usage](#basic-usage)
1. [Options](#options) 1. [Options](#options)
1. [Mappings](#mappings) 1. [Mappings](#mappings)
1. [Commands](#commands) 1. [Commands](#commands)
@ -52,6 +53,37 @@ cd ~/.vim
tar --strip=1 -zxf vim-markdown-master.tar.gz tar --strip=1 -zxf vim-markdown-master.tar.gz
``` ```
## Basic usage
### Folding
Folding is enabled for headers by default.
The following commands are useful to open and close folds:
- `zr`: reduces fold level throughout the buffer
- `zR`: opens all folds
- `zm`: increases fold level throughout the buffer
- `zM`: folds everything all the way
- `za`: open a fold your cursor is on
- `zA`: open a fold your cursor is on recursively
- `zc`: close a fold your cursor is on
- `zC`: close a fold your cursor is on recursively
[Options](#options) are available to disable folding or change folding style.
Try `:help fold-expr` and `:help fold-commands` for details.
### Concealing
Concealing is set for some syntax such as bold, italic, code block and link.
Concealing lets you conceal text with other text. The actual source text is not modified. If you put your cursor on the concealed line, the conceal goes away.
[Options](#options) are available to disable or change concealing.
Try `:help concealcursor` and `:help conceallevel` for details.
## Options ## Options
### Disable Folding ### Disable Folding
@ -293,6 +325,30 @@ If you would like to use a file extension other than `.md` you may do so using t
let g:vim_markdown_auto_extension_ext = 'txt' let g:vim_markdown_auto_extension_ext = 'txt'
``` ```
### Do not automatically insert bulletpoints
Automatically inserting bulletpoints can lead to problems when wrapping text
(see issue #232 for details), so it can be disabled:
```vim
let g:vim_markdown_auto_insert_bullets = 0
```
In that case, you probably also want to set the new list item indent to 0 as
well, or you will have to remove an indent each time you add a new list item:
```vim
let g:vim_markdown_new_list_item_indent = 0
```
### Change how to open new files
By default when following a link the target file will be opened in your current buffer. This behavior can change if you prefer using splits or tabs by using the `vim_markdown_edit_url_in` variable. Possible values are `tab`, `vsplit`, `hsplit`, `current` opening in a new tab, vertical split, horizontal split, and current buffer respectively. Defaults to current buffer if not set:
```vim
let g:vim_markdown_edit_url_in = 'tab'
```
## Mappings ## Mappings
The following work on normal and visual modes: The following work on normal and visual modes:

View File

@ -5,7 +5,10 @@ Contents ~
1. Introduction |vim-markdown-introduction| 1. Introduction |vim-markdown-introduction|
2. Installation |vim-markdown-installation| 2. Installation |vim-markdown-installation|
3. Options |vim-markdown-options| 3. Basic usage |vim-markdown-basic-usage|
1. Folding |vim-markdown-folding|
2. Concealing |vim-markdown-concealing|
4. Options |vim-markdown-options|
1. Disable Folding |vim-markdown-disable-folding| 1. Disable Folding |vim-markdown-disable-folding|
2. Change fold style |vim-markdown-change-fold-style| 2. Change fold style |vim-markdown-change-fold-style|
3. Set header folding level |vim-markdown-set-header-folding-level| 3. Set header folding level |vim-markdown-set-header-folding-level|
@ -26,12 +29,16 @@ Contents ~
|vim-markdown-do-not-require-.md-extensions-for-markdown-links| |vim-markdown-do-not-require-.md-extensions-for-markdown-links|
13. Auto-write when following link 13. Auto-write when following link
|vim-markdown-auto-write-when-following-link| |vim-markdown-auto-write-when-following-link|
14. Change default file extension |vim-markdown-auto-extension-ext| 14. Change default file extension
4. Mappings |vim-markdown-mappings| |vim-markdown-change-default-file-extension|
5. Commands |vim-markdown-commands| 15. Do not automatically insert bulletpoints
6. Credits |vim-markdown-credits| |vim-markdown-do-not-automatically-insert-bulletpoints|
7. License |vim-markdown-license| 16. Change how to open new files |vim-markdown-change-how-to-open-new-files|
8. References |vim-markdown-references| 5. Mappings |vim-markdown-mappings|
6. Commands |vim-markdown-commands|
7. Credits |vim-markdown-credits|
8. License |vim-markdown-license|
9. References |vim-markdown-references|
=============================================================================== ===============================================================================
*vim-markdown-introduction* *vim-markdown-introduction*
@ -73,6 +80,52 @@ If you are not using any package manager, download the tarball [5] and do this:
cd ~/.vim cd ~/.vim
tar --strip=1 -zxf vim-markdown-master.tar.gz tar --strip=1 -zxf vim-markdown-master.tar.gz
< <
===============================================================================
*vim-markdown-basic-usage*
Basic usage ~
-------------------------------------------------------------------------------
*vim-markdown-folding*
Folding ~
Folding is enabled for headers by default.
The following commands are useful to open and close folds:
*vim-markdown-zr*
- 'zr': reduces fold level throughout the buffer
*vim-markdown-zR*
- 'zR': opens all folds
*vim-markdown-zm*
- 'zm': increases fold level throughout the buffer
*vim-markdown-zM*
- 'zM': folds everything all the way
*vim-markdown-za*
- 'za': open a fold your cursor is on
*vim-markdown-zA*
- 'zA': open a fold your cursor is on recursively
*vim-markdown-zc*
- 'zc': close a fold your cursor is on
*vim-markdown-zC*
- 'zC': close a fold your cursor is on recursively
Options are available to disable folding or change folding style.
Try ':help fold-expr' and ':help fold-commands' for details.
-------------------------------------------------------------------------------
*vim-markdown-concealing*
Concealing ~
Concealing is set for some syntax such as bold, italic, code block and link.
Concealing lets you conceal text with other text. The actual source text is not
modified. If you put your cursor on the concealed line, the conceal goes away.
Options are available to disable or change concealing.
Try ':help concealcursor' and ':help conceallevel' for details.
=============================================================================== ===============================================================================
*vim-markdown-options* *vim-markdown-options*
Options ~ Options ~
@ -144,7 +197,7 @@ never increases its default size (half screen), it only shrinks.
Text emphasis restriction to single-lines ~ Text emphasis restriction to single-lines ~
By default text emphasis works across multiple lines until a closing token is By default text emphasis works across multiple lines until a closing token is
found. However, it's possible to restrict text emphasis to a single line (ie, found. However, it's possible to restrict text emphasis to a single line (i.e.,
for it to be applied a closing token must be found on the same line). To do so: for it to be applied a closing token must be found on the same line). To do so:
> >
let g:vim_markdown_emphasis_multiline = 0 let g:vim_markdown_emphasis_multiline = 0
@ -197,9 +250,9 @@ Default is "['c++=cpp', 'viml=vim', 'bash=sh', 'ini=dosini']".
*vim-markdown-follow-named-anchors* *vim-markdown-follow-named-anchors*
Follow named anchors ~ Follow named anchors ~
This feature allows ge to follow named anchors in links of the form This feature allows the 'ge' command to follow named anchors in links of the
'file#anchor' or just '#anchor', where file may omit the '.md' extension as form 'file#anchor' or just '#anchor', where file may omit the '.md' extension
usual. Two variables control its operation: as usual. Two variables control its operation:
> >
let g:vim_markdown_follow_anchor = 1 let g:vim_markdown_follow_anchor = 1
< <
@ -311,7 +364,7 @@ this option will automatically save any edits you made before moving you:
let g:vim_markdown_autowrite = 1 let g:vim_markdown_autowrite = 1
< <
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*vim-markdown-auto-extension-ext* *vim-markdown-change-default-file-extension*
Change default file extension ~ Change default file extension ~
If you would like to use a file extension other than '.md' you may do so using If you would like to use a file extension other than '.md' you may do so using
@ -319,6 +372,32 @@ the 'vim_markdown_auto_extension_ext' variable:
> >
let g:vim_markdown_auto_extension_ext = 'txt' let g:vim_markdown_auto_extension_ext = 'txt'
< <
-------------------------------------------------------------------------------
*vim-markdown-do-not-automatically-insert-bulletpoints*
Do not automatically insert bulletpoints ~
Automatically inserting bulletpoints can lead to problems when wrapping text
(see issue #232 for details), so it can be disabled:
>
let g:vim_markdown_auto_insert_bullets = 0
<
In that case, you probably also want to set the new list item indent to 0 as
well, or you will have to remove an indent each time you add a new list item:
>
let g:vim_markdown_new_list_item_indent = 0
<
-------------------------------------------------------------------------------
*vim-markdown-change-how-to-open-new-files*
Change how to open new files ~
By default when following a link the target file will be opened in your current
buffer. This behavior can change if you prefer using splits or tabs by using
the 'vim_markdown_edit_url_in' variable. Possible values are 'tab', 'vsplit',
'hsplit', 'current' opening in a new tab, vertical split, horizontal split, and
current buffer respectively. Defaults to current buffer if not set:
>
let g:vim_markdown_edit_url_in = 'tab'
<
=============================================================================== ===============================================================================
*vim-markdown-mappings* *vim-markdown-mappings*
Mappings ~ Mappings ~

View File

@ -609,7 +609,23 @@ if !exists('*s:EditUrlUnderCursor')
endif endif
endif endif
let l:url = fnameescape(fnamemodify(expand('%:h').'/'.l:url.l:ext, ':.')) let l:url = fnameescape(fnamemodify(expand('%:h').'/'.l:url.l:ext, ':.'))
execute 'edit' l:url let l:editmethod = ''
" determine how to open the linked file (split, tab, etc)
if exists('g:vim_markdown_edit_url_in')
if g:vim_markdown_edit_url_in == 'tab'
let l:editmethod = 'tabnew'
elseif g:vim_markdown_edit_url_in == 'vsplit'
let l:editmethod = 'vsp'
elseif g:vim_markdown_edit_url_in == 'hsplit'
let l:editmethod = 'sp'
else
let l:editmethod = 'edit'
endif
else
" default to current buffer
let l:editmethod = 'edit'
endif
execute l:editmethod l:url
endif endif
if l:anchor != '' if l:anchor != ''
silent! execute '/'.l:anchor silent! execute '/'.l:anchor

View File

@ -5,15 +5,15 @@ setlocal indentexpr=GetMarkdownIndent()
setlocal nolisp setlocal nolisp
setlocal autoindent setlocal autoindent
" Automatically insert bullets
setlocal formatoptions+=r
" Do not automatically insert bullets when auto-wrapping with text-width
setlocal formatoptions-=c
" Accept various markers as bullets
setlocal comments=b:*,b:+,b:-
" Automatically continue blockquote on line break " Automatically continue blockquote on line break
setlocal comments+=b:> setlocal formatoptions+=r
setlocal comments=b:>
if get(g:, "vim_markdown_auto_insert_bullets", 1)
" Do not automatically insert bullets when auto-wrapping with text-width
setlocal formatoptions-=c
" Accept various markers as bullets
setlocal comments+=b:*,b:+,b:-
endif
" Only define the function once " Only define the function once
if exists("*GetMarkdownIndent") | finish | endif if exists("*GetMarkdownIndent") | finish | endif

View File

@ -82,25 +82,25 @@ syn region mkdLinkTitle matchgroup=mkdDelimiter start=+'+ end=+'+ contained
syn region mkdLinkTitle matchgroup=mkdDelimiter start=+(+ end=+)+ contained syn region mkdLinkTitle matchgroup=mkdDelimiter start=+(+ end=+)+ contained
"HTML headings "HTML headings
syn region htmlH1 start="^\s*#" end="$" contains=mkdLink,mkdInlineURL,@Spell syn region htmlH1 matchgroup=mkdHeading start="^\s*#" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH2 start="^\s*##" end="$" contains=mkdLink,mkdInlineURL,@Spell syn region htmlH2 matchgroup=mkdHeading start="^\s*##" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH3 start="^\s*###" end="$" contains=mkdLink,mkdInlineURL,@Spell syn region htmlH3 matchgroup=mkdHeading start="^\s*###" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH4 start="^\s*####" end="$" contains=mkdLink,mkdInlineURL,@Spell syn region htmlH4 matchgroup=mkdHeading start="^\s*####" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH5 start="^\s*#####" end="$" contains=mkdLink,mkdInlineURL,@Spell syn region htmlH5 matchgroup=mkdHeading start="^\s*#####" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH6 start="^\s*######" end="$" contains=mkdLink,mkdInlineURL,@Spell syn region htmlH6 matchgroup=mkdHeading start="^\s*######" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn match htmlH1 /^.\+\n=\+$/ contains=mkdLink,mkdInlineURL,@Spell syn match htmlH1 /^.\+\n=\+$/ contains=mkdLink,mkdInlineURL,@Spell
syn match htmlH2 /^.\+\n-\+$/ contains=mkdLink,mkdInlineURL,@Spell syn match htmlH2 /^.\+\n-\+$/ contains=mkdLink,mkdInlineURL,@Spell
"define Markdown groups "define Markdown groups
syn match mkdLineBreak / \+$/ syn match mkdLineBreak / \+$/
syn region mkdBlockquote start=/^\s*>/ end=/$/ contains=mkdLink,mkdInlineURL,mkdLineBreak,@Spell syn region mkdBlockquote start=/^\s*>/ end=/$/ contains=mkdLink,mkdInlineURL,mkdLineBreak,@Spell
syn region mkdCode start=/\(\([^\\]\|^\)\\\)\@<!`/ end=/\(\([^\\]\|^\)\\\)\@<!`/ execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/\(\([^\\]\|^\)\\\)\@<!`/ end=/\(\([^\\]\|^\)\\\)\@<!`/' . s:concealends
syn region mkdCode start=/\s*``[^`]*/ end=/[^`]*``\s*/ execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/\(\([^\\]\|^\)\\\)\@<!``/ skip=/[^`]`[^`]/ end=/\(\([^\\]\|^\)\\\)\@<!``/' . s:concealends
syn region mkdCode start=/^\s*\z(`\{3,}\)[^`]*$/ end=/^\s*\z1`*\s*$/ execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/^\s*\z(`\{3,}\)[^`]*$/ end=/^\s*\z1`*\s*$/' . s:concealends
syn region mkdCode start=/\s*\~\~[^\~]*/ end=/[^\~]*\~\~\s*/ execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/\(\([^\\]\|^\)\\\)\@<!\~\~/ end=/\(\([^\\]\|^\)\\\)\@<!\~\~/' . s:concealends
syn region mkdCode start=/^\s*\z(\~\{3,}\)\s*[0-9A-Za-z_+-]*\s*$/ end=/^\s*\z1\~*\s*$/ execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/^\s*\z(\~\{3,}\)\s*[0-9A-Za-z_+-]*\s*$/ end=/^\s*\z1\~*\s*$/' . s:concealends
syn region mkdCode start="<pre[^>]*\\\@<!>" end="</pre>" execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start="<pre[^>]*\\\@<!>" end="</pre>"' . s:concealends
syn region mkdCode start="<code[^>]*\\\@<!>" end="</code>" execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start="<code[^>]*\\\@<!>" end="</code>"' . s:concealends
syn region mkdFootnote start="\[^" end="\]" syn region mkdFootnote start="\[^" end="\]"
syn match mkdCode /^\s*\n\(\(\s\{8,}[^ ]\|\t\t\+[^\t]\).*\n\)\+/ syn match mkdCode /^\s*\n\(\(\s\{8,}[^ ]\|\t\t\+[^\t]\).*\n\)\+/
syn match mkdCode /\%^\(\(\s\{4,}[^ ]\|\t\+[^\t]\).*\n\)\+/ syn match mkdCode /\%^\(\(\s\{4,}[^ ]\|\t\+[^\t]\).*\n\)\+/
@ -115,7 +115,7 @@ syn match mkdRule /^\s*_\s\{0,1}_\s\{0,1}_\(_\|\s\)*$/
" YAML frontmatter " YAML frontmatter
if get(g:, 'vim_markdown_frontmatter', 0) if get(g:, 'vim_markdown_frontmatter', 0)
syn include @yamlTop syntax/yaml.vim syn include @yamlTop syntax/yaml.vim
syn region Comment matchgroup=mkdDelimiter start="\%^---$" end="^---$" contains=@yamlTop keepend syn region Comment matchgroup=mkdDelimiter start="\%^---$" end="^\(---\|...\)$" contains=@yamlTop keepend
unlet! b:current_syntax unlet! b:current_syntax
endif endif
@ -150,6 +150,7 @@ syn cluster mkdNonListItem contains=@htmlTop,htmlItalic,htmlBold,htmlBoldItalic,
"highlighting for Markdown groups "highlighting for Markdown groups
HtmlHiLink mkdString String HtmlHiLink mkdString String
HtmlHiLink mkdCode String HtmlHiLink mkdCode String
HtmlHiLink mkdCodeDelimiter String
HtmlHiLink mkdCodeStart String HtmlHiLink mkdCodeStart String
HtmlHiLink mkdCodeEnd String HtmlHiLink mkdCodeEnd String
HtmlHiLink mkdFootnote Comment HtmlHiLink mkdFootnote Comment

View File

@ -1139,19 +1139,19 @@ Given markdown;
###### h6 ###### h6
Execute (atx headers): Execute (atx headers):
AssertEqual SyntaxOf('# h1 space'), 'htmlH1' AssertEqual SyntaxOf(' h1 space'), 'htmlH1'
AssertEqual SyntaxOf('#h1 nospace'), 'htmlH1' AssertEqual SyntaxOf('h1 nospace'), 'htmlH1'
AssertEqual SyntaxOf('# h1 2 spaces'), 'htmlH1' AssertEqual SyntaxOf(' h1 2 spaces'), 'htmlH1'
AssertEqual SyntaxOf('# h1 trailing hash #'), 'htmlH1' AssertEqual SyntaxOf(' h1 trailing hash '), 'htmlH1'
AssertEqual SyntaxOf('## h2 space'), 'htmlH2' AssertEqual SyntaxOf(' h2 space'), 'htmlH2'
AssertEqual SyntaxOf('##h2 nospace'), 'htmlH2' AssertEqual SyntaxOf('h2 nospace'), 'htmlH2'
AssertEqual SyntaxOf('## h2 trailing hash ##'), 'htmlH2' AssertEqual SyntaxOf(' h2 trailing hash '), 'htmlH2'
AssertEqual SyntaxOf('### h3 space'), 'htmlH3' AssertEqual SyntaxOf(' h3 space'), 'htmlH3'
AssertEqual SyntaxOf('###h3 nospace'), 'htmlH3' AssertEqual SyntaxOf('h3 nospace'), 'htmlH3'
AssertEqual SyntaxOf('### h3 trailing hash ###'), 'htmlH3' AssertEqual SyntaxOf(' h3 trailing hash '), 'htmlH3'
AssertEqual SyntaxOf('#### h4'), 'htmlH4' AssertEqual SyntaxOf(' h4'), 'htmlH4'
AssertEqual SyntaxOf('##### h5'), 'htmlH5' AssertEqual SyntaxOf(' h5'), 'htmlH5'
AssertEqual SyntaxOf('###### h6'), 'htmlH6' AssertEqual SyntaxOf(' h6'), 'htmlH6'
Given markdown; Given markdown;
# h1 before h2 # h1 before h2
@ -1161,9 +1161,9 @@ Given markdown;
# h1 after h2 # h1 after h2
Execute (atx headers relative positions): Execute (atx headers relative positions):
AssertEqual SyntaxOf('# h1 before h2'), 'htmlH1' AssertEqual SyntaxOf(' h1 before h2'), 'htmlH1'
AssertEqual SyntaxOf('## h2 between h1s'), 'htmlH2' AssertEqual SyntaxOf(' h2 between h1s'), 'htmlH2'
AssertEqual SyntaxOf('# h1 after h2'), 'htmlH1' AssertEqual SyntaxOf(' h1 after h2'), 'htmlH1'
Given markdown; Given markdown;
setex h1 setex h1
@ -1214,9 +1214,9 @@ setex h2
Execute (mixed atx and setex headers): Execute (mixed atx and setex headers):
AssertEqual SyntaxOf('setex h1 before atx'), 'htmlH1' AssertEqual SyntaxOf('setex h1 before atx'), 'htmlH1'
AssertEqual SyntaxOf('==================='), 'htmlH1' AssertEqual SyntaxOf('==================='), 'htmlH1'
AssertEqual SyntaxOf('## atx h2'), 'htmlH2' AssertEqual SyntaxOf(' atx h2'), 'htmlH2'
AssertEqual SyntaxOf('### atx h3'), 'htmlH3' AssertEqual SyntaxOf(' atx h3'), 'htmlH3'
AssertEqual SyntaxOf('# atx h1'), 'htmlH1' AssertEqual SyntaxOf(' atx h1'), 'htmlH1'
AssertEqual SyntaxOf('setex h2'), 'htmlH2' AssertEqual SyntaxOf('setex h2'), 'htmlH2'
AssertEqual SyntaxOf('------------------'), 'htmlH2' AssertEqual SyntaxOf('------------------'), 'htmlH2'

View File

@ -263,11 +263,16 @@ function! s:wrap(string,char,type,removed,special)
elseif keeper =~ '\n$' && after =~ '^\n' elseif keeper =~ '\n$' && after =~ '^\n'
let after = strpart(after,1) let after = strpart(after,1)
endif endif
if before !~ '\n\s*$' if keeper !~ '^\n' && before !~ '\n\s*$'
let before .= "\n" let before .= "\n"
if a:special if a:special
let before .= "\t" let before .= "\t"
endif endif
elseif keeper =~ '^\n' && before =~ '\n\s*$'
let keeper = strcharpart(keeper,1)
endif
if type ==# 'V' && keeper =~ '\n\s*\n$'
let keeper = strcharpart(keeper,0,strchars(keeper) - 1)
endif endif
endif endif
if type ==# 'V' if type ==# 'V'