diff --git a/sources_non_forked/ale/ale_linters/apiblueprint/drafter.vim b/sources_non_forked/ale/ale_linters/apiblueprint/drafter.vim index 9cded359..198709f9 100644 --- a/sources_non_forked/ale/ale_linters/apiblueprint/drafter.vim +++ b/sources_non_forked/ale/ale_linters/apiblueprint/drafter.vim @@ -31,6 +31,6 @@ call ale#linter#Define('apiblueprint', { \ 'name': 'drafter', \ 'output_stream': 'stderr', \ 'executable': 'drafter', -\ 'command': 'drafter --use-line-num --validate %t', +\ 'command': 'drafter --use-line-num --validate', \ 'callback': 'ale_linters#apiblueprint#drafter#HandleErrors', \}) diff --git a/sources_non_forked/ale/ale_linters/asciidoc/writegood.vim b/sources_non_forked/ale/ale_linters/asciidoc/writegood.vim index c986cc6c..a29b7e9c 100644 --- a/sources_non_forked/ale/ale_linters/asciidoc/writegood.vim +++ b/sources_non_forked/ale/ale_linters/asciidoc/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for AsciiDoc files -call ale#linter#Define('asciidoc', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('asciidoc') diff --git a/sources_non_forked/ale/ale_linters/c/clang.vim b/sources_non_forked/ale/ale_linters/c/clang.vim index ddec4fcb..9d49fabb 100644 --- a/sources_non_forked/ale/ale_linters/c/clang.vim +++ b/sources_non_forked/ale/ale_linters/c/clang.vim @@ -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 " headers in the same directory. return ale#Escape(ale_linters#c#clang#GetExecutable(a:buffer)) - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'c_clang_options') . ' -' + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'c_clang_options')) . ' -' endfunction call ale#linter#Define('c', { @@ -28,5 +28,5 @@ call ale#linter#Define('c', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#c#clang#GetCommand'} \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/sources_non_forked/ale/ale_linters/c/clangd.vim b/sources_non_forked/ale/ale_linters/c/clangd.vim new file mode 100644 index 00000000..5aa2e221 --- /dev/null +++ b/sources_non_forked/ale/ale_linters/c/clangd.vim @@ -0,0 +1,29 @@ +" Author: Andrey Melentyev +" 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', +\}) diff --git a/sources_non_forked/ale/ale_linters/c/clangtidy.vim b/sources_non_forked/ale/ale_linters/c/clangtidy.vim index 47faa1ef..d456a5a2 100644 --- a/sources_non_forked/ale/ale_linters/c/clangtidy.vim +++ b/sources_non_forked/ale/ale_linters/c/clangtidy.vim @@ -35,7 +35,7 @@ function! s:GetBuildDirectory(buffer) abort return l:build_dir endif - return ale#c#FindCompileCommands(a:buffer) + return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer)) endfunction function! ale_linters#c#clangtidy#GetCommand(buffer) abort diff --git a/sources_non_forked/ale/ale_linters/c/cquery.vim b/sources_non_forked/ale/ale_linters/c/cquery.vim new file mode 100644 index 00000000..208c226f --- /dev/null +++ b/sources_non_forked/ale/ale_linters/c/cquery.vim @@ -0,0 +1,32 @@ +" Author: Ben Falconer , jtalowell +" 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', +\}) diff --git a/sources_non_forked/ale/ale_linters/c/gcc.vim b/sources_non_forked/ale/ale_linters/c/gcc.vim index 98563952..e2dff65d 100644 --- a/sources_non_forked/ale/ale_linters/c/gcc.vim +++ b/sources_non_forked/ale/ale_linters/c/gcc.vim @@ -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 " headers in the same directory. return ale#Escape(ale_linters#c#gcc#GetExecutable(a:buffer)) - \ . ' -S -x c -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'c_gcc_options') . ' -' + \ . ' -S -x c -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'c_gcc_options')) . ' -' endfunction call ale#linter#Define('c', { @@ -28,5 +28,5 @@ call ale#linter#Define('c', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#c#gcc#GetCommand'} \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/sources_non_forked/ale/ale_linters/clojure/joker.vim b/sources_non_forked/ale/ale_linters/clojure/joker.vim index e78066fe..7a3330a8 100644 --- a/sources_non_forked/ale/ale_linters/clojure/joker.vim +++ b/sources_non_forked/ale/ale_linters/clojure/joker.vim @@ -27,6 +27,6 @@ call ale#linter#Define('clojure', { \ 'name': 'joker', \ 'output_stream': 'stderr', \ 'executable': 'joker', -\ 'command': 'joker --lint %t', +\ 'command': 'joker --working-dir %s --lint %t', \ 'callback': 'ale_linters#clojure#joker#HandleJokerFormat', \}) diff --git a/sources_non_forked/ale/ale_linters/cpp/clang.vim b/sources_non_forked/ale/ale_linters/cpp/clang.vim index e8d96187..72793a71 100644 --- a/sources_non_forked/ale/ale_linters/cpp/clang.vim +++ b/sources_non_forked/ale/ale_linters/cpp/clang.vim @@ -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 " headers in the same directory. return ale#Escape(ale_linters#cpp#clang#GetExecutable(a:buffer)) - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'cpp_clang_options') . ' -' + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'cpp_clang_options')) . ' -' endfunction call ale#linter#Define('cpp', { @@ -28,5 +28,5 @@ call ale#linter#Define('cpp', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#cpp#clang#GetCommand'}, \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/sources_non_forked/ale/ale_linters/cpp/clangcheck.vim b/sources_non_forked/ale/ale_linters/cpp/clangcheck.vim index a109d5d3..687991bd 100644 --- a/sources_non_forked/ale/ale_linters/cpp/clangcheck.vim +++ b/sources_non_forked/ale/ale_linters/cpp/clangcheck.vim @@ -16,7 +16,7 @@ function! ale_linters#cpp#clangcheck#GetCommand(buffer) abort let l:build_dir = ale#Var(a:buffer, 'c_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 " The extra arguments in the command are used to prevent .plist files from diff --git a/sources_non_forked/ale/ale_linters/cpp/clangtidy.vim b/sources_non_forked/ale/ale_linters/cpp/clangtidy.vim index 1d5fb77a..d683327f 100644 --- a/sources_non_forked/ale/ale_linters/cpp/clangtidy.vim +++ b/sources_non_forked/ale/ale_linters/cpp/clangtidy.vim @@ -29,7 +29,7 @@ function! s:GetBuildDirectory(buffer) abort return l:build_dir endif - return ale#c#FindCompileCommands(a:buffer) + return ale#path#Dirname(ale#c#FindCompileCommands(a:buffer)) endfunction function! ale_linters#cpp#clangtidy#GetCommand(buffer) abort diff --git a/sources_non_forked/ale/ale_linters/cpp/gcc.vim b/sources_non_forked/ale/ale_linters/cpp/gcc.vim index a663eaa3..17f5acf5 100644 --- a/sources_non_forked/ale/ale_linters/cpp/gcc.vim +++ b/sources_non_forked/ale/ale_linters/cpp/gcc.vim @@ -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 " headers in the same directory. return ale#Escape(ale_linters#cpp#gcc#GetExecutable(a:buffer)) - \ . ' -S -x c++ -fsyntax-only ' - \ . '-iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) . ' ' - \ . l:cflags - \ . ale#Var(a:buffer, 'cpp_gcc_options') . ' -' + \ . ' -S -x c++ -fsyntax-only' + \ . ' -iquote ' . ale#Escape(fnamemodify(bufname(a:buffer), ':p:h')) + \ . ale#Pad(l:cflags) + \ . ale#Pad(ale#Var(a:buffer, 'cpp_gcc_options')) . ' -' endfunction call ale#linter#Define('cpp', { @@ -29,5 +29,5 @@ call ale#linter#Define('cpp', { \ {'callback': 'ale#c#GetMakeCommand', 'output_stream': 'stdout'}, \ {'callback': 'ale_linters#cpp#gcc#GetCommand'}, \ ], -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/sources_non_forked/ale/ale_linters/elixir/dialyxir.vim b/sources_non_forked/ale/ale_linters/elixir/dialyxir.vim index 5ef3a047..bba0ae14 100644 --- a/sources_non_forked/ale/ale_linters/elixir/dialyxir.vim +++ b/sources_non_forked/ale/ale_linters/elixir/dialyxir.vim @@ -28,7 +28,7 @@ endfunction call ale#linter#Define('elixir', { \ 'name': 'dialyxir', \ 'executable': 'mix', -\ 'command': 'mix dialyzer', +\ 'command': 'mix help dialyzer && mix dialyzer', \ 'callback': 'ale_linters#elixir#dialyxir#Handle', \}) diff --git a/sources_non_forked/ale/ale_linters/elm/make.vim b/sources_non_forked/ale/ale_linters/elm/make.vim index d5bc19eb..8231ad47 100644 --- a/sources_non_forked/ale/ale_linters/elm/make.vim +++ b/sources_non_forked/ale/ale_linters/elm/make.vim @@ -136,7 +136,7 @@ function! ale_linters#elm#make#ParseMessage(message) abort endfunction function! ale_linters#elm#make#ParseMessageItem(item) abort - if type(a:item) == type('') + if type(a:item) is v:t_string return a:item else return a:item.string diff --git a/sources_non_forked/ale/ale_linters/fortran/language_server.vim b/sources_non_forked/ale/ale_linters/fortran/language_server.vim new file mode 100644 index 00000000..fd763fcf --- /dev/null +++ b/sources_non_forked/ale/ale_linters/fortran/language_server.vim @@ -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', +\}) diff --git a/sources_non_forked/ale/ale_linters/go/gobuild.vim b/sources_non_forked/ale/ale_linters/go/gobuild.vim index 068877a3..c4608071 100644 --- a/sources_non_forked/ale/ale_linters/go/gobuild.vim +++ b/sources_non_forked/ale/ale_linters/go/gobuild.vim @@ -70,7 +70,8 @@ function! ale_linters#go#gobuild#Handler(buffer, lines) abort endfunction call ale#linter#Define('go', { -\ 'name': 'go build', +\ 'name': 'gobuild', +\ 'aliases': ['go build'], \ 'executable': 'go', \ 'command_chain': [ \ {'callback': 'ale_linters#go#gobuild#GoEnv', 'output_stream': 'stdout'}, diff --git a/sources_non_forked/ale/ale_linters/go/govet.vim b/sources_non_forked/ale/ale_linters/go/govet.vim index edf9eb66..e94e6ecd 100644 --- a/sources_non_forked/ale/ale_linters/go/govet.vim +++ b/sources_non_forked/ale/ale_linters/go/govet.vim @@ -9,7 +9,8 @@ function! ale_linters#go#govet#GetCommand(buffer) abort endfunction call ale#linter#Define('go', { -\ 'name': 'go vet', +\ 'name': 'govet', +\ 'aliases': ['go vet'], \ 'output_stream': 'stderr', \ 'executable': 'go', \ 'command_callback': 'ale_linters#go#govet#GetCommand', diff --git a/sources_non_forked/ale/ale_linters/haskell/cabal_ghc.vim b/sources_non_forked/ale/ale_linters/haskell/cabal_ghc.vim new file mode 100644 index 00000000..003adf5d --- /dev/null +++ b/sources_non_forked/ale/ale_linters/haskell/cabal_ghc.vim @@ -0,0 +1,19 @@ +" Author: Eric Wolf +" 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', +\}) diff --git a/sources_non_forked/ale/ale_linters/haskell/ghc-mod.vim b/sources_non_forked/ale/ale_linters/haskell/ghc-mod.vim index 1b15d8c2..eb032f50 100644 --- a/sources_non_forked/ale/ale_linters/haskell/ghc-mod.vim +++ b/sources_non_forked/ale/ale_linters/haskell/ghc-mod.vim @@ -2,14 +2,16 @@ " Description: ghc-mod for Haskell files call ale#linter#Define('haskell', { -\ 'name': 'ghc-mod', +\ 'name': 'ghc_mod', +\ 'aliases': ['ghc-mod'], \ 'executable': 'ghc-mod', \ 'command': 'ghc-mod --map-file %s=%t check %s', \ 'callback': 'ale#handlers#haskell#HandleGHCFormat', \}) call ale#linter#Define('haskell', { -\ 'name': 'stack-ghc-mod', +\ 'name': 'stack_ghc_mod', +\ 'aliases': ['stack-ghc-mod'], \ 'executable': 'stack', \ 'command': 'stack exec ghc-mod -- --map-file %s=%t check %s', \ 'callback': 'ale#handlers#haskell#HandleGHCFormat', diff --git a/sources_non_forked/ale/ale_linters/haskell/stack_build.vim b/sources_non_forked/ale/ale_linters/haskell/stack_build.vim index 44e2e86f..f5bdf4b8 100644 --- a/sources_non_forked/ale/ale_linters/haskell/stack_build.vim +++ b/sources_non_forked/ale/ale_linters/haskell/stack_build.vim @@ -13,7 +13,8 @@ function! ale_linters#haskell#stack_build#GetCommand(buffer) abort endfunction call ale#linter#Define('haskell', { -\ 'name': 'stack-build', +\ 'name': 'stack_build', +\ 'aliases': ['stack-build'], \ 'output_stream': 'stderr', \ 'executable': 'stack', \ 'command_callback': 'ale_linters#haskell#stack_build#GetCommand', diff --git a/sources_non_forked/ale/ale_linters/haskell/stack_ghc.vim b/sources_non_forked/ale/ale_linters/haskell/stack_ghc.vim index 0367dc24..d702aa68 100644 --- a/sources_non_forked/ale/ale_linters/haskell/stack_ghc.vim +++ b/sources_non_forked/ale/ale_linters/haskell/stack_ghc.vim @@ -2,7 +2,8 @@ " Description: ghc for Haskell files, using Stack call ale#linter#Define('haskell', { -\ 'name': 'stack-ghc', +\ 'name': 'stack_ghc', +\ 'aliases': ['stack-ghc'], \ 'output_stream': 'stderr', \ 'executable': 'stack', \ 'command': 'stack ghc -- -fno-code -v0 %t', diff --git a/sources_non_forked/ale/ale_linters/help/writegood.vim b/sources_non_forked/ale/ale_linters/help/writegood.vim index 11254cd2..eeb21a73 100644 --- a/sources_non_forked/ale/ale_linters/help/writegood.vim +++ b/sources_non_forked/ale/ale_linters/help/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for vim Help files -call ale#linter#Define('help', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('help') diff --git a/sources_non_forked/ale/ale_linters/html/writegood.vim b/sources_non_forked/ale/ale_linters/html/writegood.vim index 9fae8821..6a2bd8e5 100644 --- a/sources_non_forked/ale/ale_linters/html/writegood.vim +++ b/sources_non_forked/ale/ale_linters/html/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans -" Description: write-good for nroff files +" Description: write-good for html files -call ale#linter#Define('html', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('html') diff --git a/sources_non_forked/ale/ale_linters/markdown/remark_lint.vim b/sources_non_forked/ale/ale_linters/markdown/remark_lint.vim index 195b0f5a..d9c2efb6 100644 --- a/sources_non_forked/ale/ale_linters/markdown/remark_lint.vim +++ b/sources_non_forked/ale/ale_linters/markdown/remark_lint.vim @@ -1,3 +1,4 @@ +scriptencoding utf-8 " Author rhysd https://rhysd.github.io/, Dirk Roorda (dirkroorda), Adrián González Rus (@adrigzr) " Description: remark-lint for Markdown files call ale#Set('markdown_remark_lint_executable', 'remark') @@ -43,7 +44,8 @@ function! ale_linters#markdown#remark_lint#Handle(buffer, lines) abort endfunction call ale#linter#Define('markdown', { -\ 'name': 'remark-lint', +\ 'name': 'remark_lint', +\ 'aliases': ['remark-lint'], \ 'executable_callback': 'ale_linters#markdown#remark_lint#GetExecutable', \ 'command_callback': 'ale_linters#markdown#remark_lint#GetCommand', \ 'callback': 'ale_linters#markdown#remark_lint#Handle', diff --git a/sources_non_forked/ale/ale_linters/markdown/writegood.vim b/sources_non_forked/ale/ale_linters/markdown/writegood.vim index 21dbff1a..7108e7ac 100644 --- a/sources_non_forked/ale/ale_linters/markdown/writegood.vim +++ b/sources_non_forked/ale/ale_linters/markdown/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for Markdown files -call ale#linter#Define('markdown', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('markdown') diff --git a/sources_non_forked/ale/ale_linters/nroff/writegood.vim b/sources_non_forked/ale/ale_linters/nroff/writegood.vim index d318fb28..bcf344f6 100644 --- a/sources_non_forked/ale/ale_linters/nroff/writegood.vim +++ b/sources_non_forked/ale/ale_linters/nroff/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for nroff files -call ale#linter#Define('nroff', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('nroff') diff --git a/sources_non_forked/ale/ale_linters/objc/clang.vim b/sources_non_forked/ale/ale_linters/objc/clang.vim index f4725a0e..4e80ac5c 100644 --- a/sources_non_forked/ale/ale_linters/objc/clang.vim +++ b/sources_non_forked/ale/ale_linters/objc/clang.vim @@ -19,5 +19,5 @@ call ale#linter#Define('objc', { \ 'output_stream': 'stderr', \ 'executable': 'clang', \ 'command_callback': 'ale_linters#objc#clang#GetCommand', -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/sources_non_forked/ale/ale_linters/objcpp/clang.vim b/sources_non_forked/ale/ale_linters/objcpp/clang.vim index 0e9cefe9..d1474f17 100644 --- a/sources_non_forked/ale/ale_linters/objcpp/clang.vim +++ b/sources_non_forked/ale/ale_linters/objcpp/clang.vim @@ -19,5 +19,5 @@ call ale#linter#Define('objcpp', { \ 'output_stream': 'stderr', \ 'executable': 'clang++', \ 'command_callback': 'ale_linters#objcpp#clang#GetCommand', -\ 'callback': 'ale#handlers#gcc#HandleGCCFormat', +\ 'callback': 'ale#handlers#gcc#HandleGCCFormatWithIncludes', \}) diff --git a/sources_non_forked/ale/ale_linters/po/writegood.vim b/sources_non_forked/ale/ale_linters/po/writegood.vim index 5a01cb66..14686473 100644 --- a/sources_non_forked/ale/ale_linters/po/writegood.vim +++ b/sources_non_forked/ale/ale_linters/po/writegood.vim @@ -1,9 +1,4 @@ " Author: Cian Butler https://github.com/butlerx " Description: write-good for PO files -call ale#linter#Define('po', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('po') diff --git a/sources_non_forked/ale/ale_linters/pod/writegood.vim b/sources_non_forked/ale/ale_linters/pod/writegood.vim index 14ed5c0c..9f5461e6 100644 --- a/sources_non_forked/ale/ale_linters/pod/writegood.vim +++ b/sources_non_forked/ale/ale_linters/pod/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for Pod files -call ale#linter#Define('pod', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('pod') diff --git a/sources_non_forked/ale/ale_linters/python/vulture.vim b/sources_non_forked/ale/ale_linters/python/vulture.vim new file mode 100644 index 00000000..80828013 --- /dev/null +++ b/sources_non_forked/ale/ale_linters/python/vulture.vim @@ -0,0 +1,85 @@ +" Author: Yauheni Kirylau +" 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, +\}) diff --git a/sources_non_forked/ale/ale_linters/rst/writegood.vim b/sources_non_forked/ale/ale_linters/rst/writegood.vim index 12137dbf..26b1152a 100644 --- a/sources_non_forked/ale/ale_linters/rst/writegood.vim +++ b/sources_non_forked/ale/ale_linters/rst/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for reStructuredText files -call ale#linter#Define('rst', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('rst') diff --git a/sources_non_forked/ale/ale_linters/sml/smlnj_cm.vim b/sources_non_forked/ale/ale_linters/sml/smlnj_cm.vim index 96cd7bd9..7a482307 100644 --- a/sources_non_forked/ale/ale_linters/sml/smlnj_cm.vim +++ b/sources_non_forked/ale/ale_linters/sml/smlnj_cm.vim @@ -9,7 +9,8 @@ endfunction " Using CM requires that we set "lint_file: 1", since it reads the files " from the disk itself. call ale#linter#Define('sml', { -\ 'name': 'smlnj-cm', +\ 'name': 'smlnj_cm', +\ 'aliases': ['smlnj-cm'], \ 'executable_callback': 'ale#handlers#sml#GetExecutableSmlnjCm', \ 'lint_file': 1, \ 'command_callback': 'ale_linters#sml#smlnj_cm#GetCommand', diff --git a/sources_non_forked/ale/ale_linters/tex/writegood.vim b/sources_non_forked/ale/ale_linters/tex/writegood.vim index dc59de2e..c1aeace9 100644 --- a/sources_non_forked/ale/ale_linters/tex/writegood.vim +++ b/sources_non_forked/ale/ale_linters/tex/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for TeX files -call ale#linter#Define('tex', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('tex') diff --git a/sources_non_forked/ale/ale_linters/texinfo/writegood.vim b/sources_non_forked/ale/ale_linters/texinfo/writegood.vim index 8104c634..4427f056 100644 --- a/sources_non_forked/ale/ale_linters/texinfo/writegood.vim +++ b/sources_non_forked/ale/ale_linters/texinfo/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for Texinfo files -call ale#linter#Define('texinfo', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('texinfo') diff --git a/sources_non_forked/ale/ale_linters/text/writegood.vim b/sources_non_forked/ale/ale_linters/text/writegood.vim index ff76ce42..81b935d4 100644 --- a/sources_non_forked/ale/ale_linters/text/writegood.vim +++ b/sources_non_forked/ale/ale_linters/text/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for text files -call ale#linter#Define('text', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('text') diff --git a/sources_non_forked/ale/ale_linters/xhtml/writegood.vim b/sources_non_forked/ale/ale_linters/xhtml/writegood.vim index 83d1863b..1fcba182 100644 --- a/sources_non_forked/ale/ale_linters/xhtml/writegood.vim +++ b/sources_non_forked/ale/ale_linters/xhtml/writegood.vim @@ -1,9 +1,4 @@ " Author: Sumner Evans " Description: write-good for XHTML files -call ale#linter#Define('xhtml', { -\ 'name': 'write-good', -\ 'executable_callback': 'ale#handlers#writegood#GetExecutable', -\ 'command_callback': 'ale#handlers#writegood#GetCommand', -\ 'callback': 'ale#handlers#writegood#Handle', -\}) +call ale#handlers#writegood#DefineLinter('xhtml') diff --git a/sources_non_forked/ale/autoload/ale.vim b/sources_non_forked/ale/autoload/ale.vim index 26c73547..ee32a9a6 100644 --- a/sources_non_forked/ale/autoload/ale.vim +++ b/sources_non_forked/ale/autoload/ale.vim @@ -95,7 +95,7 @@ function! ale#Queue(delay, ...) abort throw "linting_flag must be either '' or 'lint_file'" endif - if type(l:buffer) != type(0) + if type(l:buffer) isnot v:t_number throw 'buffer_number must be a Number' endif @@ -192,12 +192,7 @@ endfunction " Every variable name will be prefixed with 'ale_'. function! ale#Var(buffer, variable_name) abort let l:full_name = 'ale_' . a:variable_name - let l:vars = getbufvar(str2nr(a:buffer), '', 0) - - if l:vars is 0 - " Look for variables from deleted buffers, saved from :ALEFix - let l:vars = get(get(g:ale_fix_buffer_data, a:buffer, {}), 'vars', {}) - endif + let l:vars = getbufvar(str2nr(a:buffer), '', {}) return get(l:vars, l:full_name, g:[l:full_name]) endfunction diff --git a/sources_non_forked/ale/autoload/ale/assert.vim b/sources_non_forked/ale/autoload/ale/assert.vim index 55c39ee3..a6753a1a 100644 --- a/sources_non_forked/ale/autoload/ale/assert.vim +++ b/sources_non_forked/ale/autoload/ale/assert.vim @@ -30,7 +30,7 @@ function! ale#assert#Linter(expected_executable, expected_command) abort let l:callbacks = map(copy(l:linter.command_chain), 'v:val.callback') " If the expected command is a string, just check the last one. - if type(a:expected_command) is type('') + if type(a:expected_command) is v:t_string if len(l:callbacks) is 1 let l:command = call(l:callbacks[0], [l:buffer]) else diff --git a/sources_non_forked/ale/autoload/ale/balloon.vim b/sources_non_forked/ale/autoload/ale/balloon.vim index 7a380da2..72f6b91c 100644 --- a/sources_non_forked/ale/autoload/ale/balloon.vim +++ b/sources_non_forked/ale/autoload/ale/balloon.vim @@ -34,26 +34,25 @@ function! ale#balloon#Expr() abort endfunction function! ale#balloon#Disable() abort - if has('balloon_eval_term') - set noballoonevalterm + if has('balloon_eval') + set noballooneval + set balloonexpr= endif - set noballooneval - set balloonexpr= + if has('balloon_eval_term') + set noballoonevalterm + set balloonexpr= + endif endfunction function! ale#balloon#Enable() abort - if !has('balloon_eval') && !has('balloon_eval_term') - return - endif - if has('balloon_eval') set ballooneval + set balloonexpr=ale#balloon#Expr() endif if has('balloon_eval_term') set balloonevalterm + set balloonexpr=ale#balloon#Expr() endif - - set balloonexpr=ale#balloon#Expr() endfunction diff --git a/sources_non_forked/ale/autoload/ale/c.vim b/sources_non_forked/ale/autoload/ale/c.vim index 5ab10f00..19bb0d5e 100644 --- a/sources_non_forked/ale/autoload/ale/c.vim +++ b/sources_non_forked/ale/autoload/ale/c.vim @@ -2,10 +2,14 @@ " Description: Functions for integrating with C-family linters. call ale#Set('c_parse_makefile', 0) +call ale#Set('c_parse_compile_commands', 0) let s:sep = has('win32') ? '\' : '/' +" Set just so tests can override it. +let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt'] + function! ale#c#FindProjectRoot(buffer) abort - for l:project_filename in ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt'] + for l:project_filename in g:__ale_c_project_filenames let l:full_path = ale#path#FindNearestFile(a:buffer, l:project_filename) if !empty(l:full_path) @@ -23,11 +27,11 @@ function! ale#c#FindProjectRoot(buffer) abort return '' endfunction -function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort +function! ale#c#ParseCFlags(path_prefix, cflag_line) abort let l:cflags_list = [] let l:previous_options = [] - for l:option in a:cflags + for l:option in split(a:cflag_line, '-') call add(l:previous_options, l:option) " Check if cflag contained a '-' and should not have been splitted let l:option_list = split(l:option, '\zs') @@ -59,32 +63,122 @@ function! ale#c#ParseCFlagsToList(path_prefix, cflags) abort endif endfor - return l:cflags_list + return join(l:cflags_list, ' ') endfunction -function! ale#c#ParseCFlags(buffer, stdout_make) abort +function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort if !g:ale_c_parse_makefile - return [] + return '' endif let l:buffer_filename = expand('#' . a:buffer . ':t') - let l:cflags = [] - for l:lines in split(a:stdout_make, '\\n') - if stridx(l:lines, l:buffer_filename) >= 0 - let l:cflags = split(l:lines, '-') + let l:cflag_line = '' + + " Find a line matching this buffer's filename in the make output. + for l:line in a:make_output + if stridx(l:line, l:buffer_filename) >= 0 + let l:cflag_line = l:line break endif endfor let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') - return ale#c#ParseCFlagsToList(fnamemodify(l:makefile_path, ':p:h'), l:cflags) + let l:makefile_dir = fnamemodify(l:makefile_path, ':p:h') + + return ale#c#ParseCFlags(l:makefile_dir, l:cflag_line) +endfunction + +" Given a buffer number, find the build subdirectory with compile commands +" The subdirectory is returned without the trailing / +function! ale#c#FindCompileCommands(buffer) abort + " Look above the current source file to find compile_commands.json + let l:json_file = ale#path#FindNearestFile(a:buffer, 'compile_commands.json') + + if !empty(l:json_file) + return l:json_file + endif + + " Search in build directories if we can't find it in the project. + for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h')) + for l:dirname in ale#Var(a:buffer, 'c_build_dir_names') + let l:c_build_dir = l:path . s:sep . l:dirname + let l:json_file = l:c_build_dir . s:sep . 'compile_commands.json' + + if filereadable(l:json_file) + return l:json_file + endif + endfor + endfor + + return '' +endfunction + +" Cache compile_commands.json data in a Dictionary, so we don't need to read +" the same files over and over again. The key in the dictionary will include +" the last modified time of the file. +if !exists('s:compile_commands_cache') + let s:compile_commands_cache = {} +endif + +function! s:GetListFromCompileCommandsFile(compile_commands_file) abort + if empty(a:compile_commands_file) + return [] + endif + + let l:time = getftime(a:compile_commands_file) + + if l:time < 0 + return [] + endif + + let l:key = a:compile_commands_file . ':' . l:time + + if has_key(s:compile_commands_cache, l:key) + return s:compile_commands_cache[l:key] + endif + + let l:data = [] + silent! let l:data = json_decode(join(readfile(a:compile_commands_file), '')) + + if !empty(l:data) + let s:compile_commands_cache[l:key] = l:data + + return l:data + endif + + return [] +endfunction + +function! ale#c#ParseCompileCommandsFlags(buffer, dir, json_list) abort + for l:item in a:json_list + if bufnr(l:item.file) is a:buffer + return ale#c#ParseCFlags(a:dir, l:item.command) + endif + endfor + + return '' +endfunction + +function! ale#c#FlagsFromCompileCommands(buffer, compile_commands_file) abort + let l:dir = ale#path#Dirname(a:compile_commands_file) + let l:json_list = s:GetListFromCompileCommandsFile(a:compile_commands_file) + + return ale#c#ParseCompileCommandsFlags(a:buffer, l:dir, l:json_list) endfunction function! ale#c#GetCFlags(buffer, output) abort let l:cflags = ' ' - if g:ale_c_parse_makefile && !empty(a:output) - let l:cflags = join(ale#c#ParseCFlags(a:buffer, join(a:output, '\n')), ' ') . ' ' + if ale#Var(a:buffer, 'c_parse_makefile') && !empty(a:output) + let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output) + endif + + if ale#Var(a:buffer, 'c_parse_compile_commands') + let l:json_file = ale#c#FindCompileCommands(a:buffer) + + if !empty(l:json_file) + let l:cflags = ale#c#FlagsFromCompileCommands(a:buffer, l:json_file) + endif endif if l:cflags is# ' ' @@ -95,8 +189,9 @@ function! ale#c#GetCFlags(buffer, output) abort endfunction function! ale#c#GetMakeCommand(buffer) abort - if g:ale_c_parse_makefile + if ale#Var(a:buffer, 'c_parse_makefile') let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') + if !empty(l:makefile_path) return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n' endif @@ -158,19 +253,3 @@ let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [ \ 'build', \ 'bin', \]) - -" Given a buffer number, find the build subdirectory with compile commands -" The subdirectory is returned without the trailing / -function! ale#c#FindCompileCommands(buffer) abort - for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h')) - for l:dirname in ale#Var(a:buffer, 'c_build_dir_names') - let l:c_build_dir = l:path . s:sep . l:dirname - - if filereadable(l:c_build_dir . '/compile_commands.json') - return l:c_build_dir - endif - endfor - endfor - - return '' -endfunction diff --git a/sources_non_forked/ale/autoload/ale/completion.vim b/sources_non_forked/ale/autoload/ale/completion.vim index e7da4028..900d1871 100644 --- a/sources_non_forked/ale/autoload/ale/completion.vim +++ b/sources_non_forked/ale/autoload/ale/completion.vim @@ -113,7 +113,7 @@ function! ale#completion#Filter(buffer, suggestions, prefix) abort for l:item in a:suggestions " A List of String values or a List of completion item Dictionaries " is accepted here. - let l:word = type(l:item) == type('') ? l:item : l:item.word + let l:word = type(l:item) is v:t_string ? l:item : l:item.word " Add suggestions if the suggestion starts with a case-insensitive " match for the prefix. @@ -133,7 +133,7 @@ function! ale#completion#Filter(buffer, suggestions, prefix) abort " Remove suggestions with words in the exclusion List. call filter( \ l:filtered_suggestions, - \ 'index(l:excluded_words, type(v:val) is type('''') ? v:val : v:val.word) < 0', + \ 'index(l:excluded_words, type(v:val) is v:t_string ? v:val : v:val.word) < 0', \) endif @@ -214,8 +214,10 @@ function! ale#completion#Show(response, completion_parser) abort " function, and then start omni-completion. let b:ale_completion_response = a:response let b:ale_completion_parser = a:completion_parser + " Replace completion options shortly before opening the menu. call s:ReplaceCompletionOptions() - call ale#util#FeedKeys("\(ale_show_completion_menu)") + + call timer_start(0, {-> ale#util#FeedKeys("\(ale_show_completion_menu)")}) endfunction function! s:CompletionStillValid(request_id) abort @@ -315,10 +317,10 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:item_list = [] - if type(get(a:response, 'result')) is type([]) + if type(get(a:response, 'result')) is v:t_list let l:item_list = a:response.result - elseif type(get(a:response, 'result')) is type({}) - \&& type(get(a:response.result, 'items')) is type([]) + elseif type(get(a:response, 'result')) is v:t_dict + \&& type(get(a:response.result, 'items')) is v:t_list let l:item_list = a:response.result.items endif @@ -336,7 +338,9 @@ function! ale#completion#ParseLSPCompletions(response) abort endif " See :help complete-items for Vim completion kinds - if l:item.kind is s:LSP_COMPLETION_METHOD_KIND + if !has_key(l:item, 'kind') + let l:kind = 'v' + elseif l:item.kind is s:LSP_COMPLETION_METHOD_KIND let l:kind = 'm' elseif l:item.kind is s:LSP_COMPLETION_CONSTRUCTOR_KIND let l:kind = 'm' @@ -422,54 +426,65 @@ endfunction function! s:GetLSPCompletions(linter) abort let l:buffer = bufnr('') - let l:Callback = a:linter.lsp is# 'tsserver' - \ ? function('ale#completion#HandleTSServerResponse') - \ : function('ale#completion#HandleLSPResponse') - - let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) + let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter) if empty(l:lsp_details) return 0 endif let l:id = l:lsp_details.connection_id + let l:root = l:lsp_details.project_root - if a:linter.lsp is# 'tsserver' - let l:message = ale#lsp#tsserver_message#Completions( - \ l:buffer, - \ b:ale_completion_info.line, - \ b:ale_completion_info.column, - \ b:ale_completion_info.prefix, - \) - else - " Send a message saying the buffer has changed first, otherwise - " completions won't know what text is nearby. - call ale#lsp#NotifyForChanges(l:lsp_details) - - " For LSP completions, we need to clamp the column to the length of - " the line. python-language-server and perhaps others do not implement - " this correctly. - let l:message = ale#lsp#message#Completion( - \ l:buffer, - \ b:ale_completion_info.line, - \ min([ - \ b:ale_completion_info.line_length, - \ b:ale_completion_info.column, - \ ]), - \ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix), - \) - endif - - let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) - - if l:request_id - let b:ale_completion_info.conn_id = l:id - let b:ale_completion_info.request_id = l:request_id - - if has_key(a:linter, 'completion_filter') - let b:ale_completion_info.completion_filter = a:linter.completion_filter + function! OnReady(...) abort closure + " If we have sent a completion request already, don't send another. + if b:ale_completion_info.request_id + return endif - endif + + let l:Callback = a:linter.lsp is# 'tsserver' + \ ? function('ale#completion#HandleTSServerResponse') + \ : function('ale#completion#HandleLSPResponse') + call ale#lsp#RegisterCallback(l:id, l:Callback) + + if a:linter.lsp is# 'tsserver' + let l:message = ale#lsp#tsserver_message#Completions( + \ l:buffer, + \ b:ale_completion_info.line, + \ b:ale_completion_info.column, + \ b:ale_completion_info.prefix, + \) + else + " Send a message saying the buffer has changed first, otherwise + " completions won't know what text is nearby. + call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer) + + " For LSP completions, we need to clamp the column to the length of + " the line. python-language-server and perhaps others do not implement + " this correctly. + let l:message = ale#lsp#message#Completion( + \ l:buffer, + \ b:ale_completion_info.line, + \ min([ + \ b:ale_completion_info.line_length, + \ b:ale_completion_info.column, + \ ]), + \ ale#completion#GetTriggerCharacter(&filetype, b:ale_completion_info.prefix), + \) + endif + + let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) + + if l:request_id + let b:ale_completion_info.conn_id = l:id + let b:ale_completion_info.request_id = l:request_id + + if has_key(a:linter, 'completion_filter') + let b:ale_completion_info.completion_filter = a:linter.completion_filter + endif + endif + endfunction + + call ale#lsp#WaitForCapability(l:id, l:root, 'completion', function('OnReady')) endfunction function! ale#completion#GetCompletions() abort diff --git a/sources_non_forked/ale/autoload/ale/definition.vim b/sources_non_forked/ale/autoload/ale/definition.vim index 6c70b64c..3ef24e28 100644 --- a/sources_non_forked/ale/autoload/ale/definition.vim +++ b/sources_non_forked/ale/autoload/ale/definition.vim @@ -40,9 +40,9 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort " The result can be a Dictionary item, a List of the same, or null. let l:result = get(a:response, 'result', v:null) - if type(l:result) is type({}) + if type(l:result) is v:t_dict let l:result = [l:result] - elseif type(l:result) isnot type([]) + elseif type(l:result) isnot v:t_list let l:result = [] endif @@ -60,43 +60,50 @@ endfunction function! s:GoToLSPDefinition(linter, options) abort let l:buffer = bufnr('') let [l:line, l:column] = getcurpos()[1:2] + let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter) - let l:Callback = a:linter.lsp is# 'tsserver' - \ ? function('ale#definition#HandleTSServerResponse') - \ : function('ale#definition#HandleLSPResponse') - - let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) + if a:linter.lsp isnot# 'tsserver' + let l:column = min([l:column, len(getline(l:line))]) + endif if empty(l:lsp_details) return 0 endif let l:id = l:lsp_details.connection_id + let l:root = l:lsp_details.project_root - if a:linter.lsp is# 'tsserver' - let l:message = ale#lsp#tsserver_message#Definition( - \ l:buffer, - \ l:line, - \ l:column - \) - else - " Send a message saying the buffer has changed first, or the - " definition position probably won't make sense. - call ale#lsp#NotifyForChanges(l:lsp_details) + function! OnReady(...) abort closure + let l:Callback = a:linter.lsp is# 'tsserver' + \ ? function('ale#definition#HandleTSServerResponse') + \ : function('ale#definition#HandleLSPResponse') + call ale#lsp#RegisterCallback(l:id, l:Callback) - let l:column = min([l:column, len(getline(l:line))]) + if a:linter.lsp is# 'tsserver' + let l:message = ale#lsp#tsserver_message#Definition( + \ l:buffer, + \ l:line, + \ l:column + \) + else + " Send a message saying the buffer has changed first, or the + " definition position probably won't make sense. + call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer) - " For LSP completions, we need to clamp the column to the length of - " the line. python-language-server and perhaps others do not implement - " this correctly. - let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column) - endif + " For LSP completions, we need to clamp the column to the length of + " the line. python-language-server and perhaps others do not implement + " this correctly. + let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column) + endif - let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) + let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) - let s:go_to_definition_map[l:request_id] = { - \ 'open_in_tab': get(a:options, 'open_in_tab', 0), - \} + let s:go_to_definition_map[l:request_id] = { + \ 'open_in_tab': get(a:options, 'open_in_tab', 0), + \} + endfunction + + call ale#lsp#WaitForCapability(l:id, l:root, 'definition', function('OnReady')) endfunction function! ale#definition#GoTo(options) abort diff --git a/sources_non_forked/ale/autoload/ale/engine.vim b/sources_non_forked/ale/autoload/ale/engine.vim index ec5ccb6d..fdf883f5 100644 --- a/sources_non_forked/ale/autoload/ale/engine.vim +++ b/sources_non_forked/ale/autoload/ale/engine.vim @@ -557,7 +557,7 @@ function! s:RunJob(options) abort if get(g:, 'ale_run_synchronously') == 1 " Run a command synchronously if this test option is set. let s:job_info_map[l:job_id].output = systemlist( - \ type(l:command) == type([]) + \ type(l:command) is v:t_list \ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2]) \ : l:command \) diff --git a/sources_non_forked/ale/autoload/ale/engine/ignore.vim b/sources_non_forked/ale/autoload/ale/engine/ignore.vim index 65347e21..2db2c6c1 100644 --- a/sources_non_forked/ale/autoload/ale/engine/ignore.vim +++ b/sources_non_forked/ale/autoload/ale/engine/ignore.vim @@ -4,11 +4,11 @@ " Given a filetype and a configuration for ignoring linters, return a List of " Strings for linter names to ignore. function! ale#engine#ignore#GetList(filetype, config) abort - if type(a:config) is type([]) + if type(a:config) is v:t_list return a:config endif - if type(a:config) is type({}) + if type(a:config) is v:t_dict let l:names_to_remove = [] for l:part in split(a:filetype , '\.') diff --git a/sources_non_forked/ale/autoload/ale/fix.vim b/sources_non_forked/ale/autoload/ale/fix.vim index 62674b87..4d82b367 100644 --- a/sources_non_forked/ale/autoload/ale/fix.vim +++ b/sources_non_forked/ale/autoload/ale/fix.vim @@ -275,7 +275,7 @@ function! s:RunJob(options) abort if get(g:, 'ale_run_synchronously') == 1 " Run a command synchronously if this test option is set. let l:output = systemlist( - \ type(l:command) == type([]) + \ type(l:command) is v:t_list \ ? join(l:command[0:1]) . ' ' . ale#Escape(l:command[2]) \ : l:command \) @@ -313,10 +313,10 @@ function! s:RunFixer(options) abort \ : call(l:Function, [l:buffer, copy(l:input)]) endif - if type(l:result) == type(0) && l:result == 0 + if type(l:result) is v:t_number && l:result == 0 " When `0` is returned, skip this item. let l:index += 1 - elseif type(l:result) == type([]) + elseif type(l:result) is v:t_list let l:input = l:result let l:index += 1 else @@ -351,9 +351,9 @@ function! s:RunFixer(options) abort endfunction function! s:AddSubCallbacks(full_list, callbacks) abort - if type(a:callbacks) == type('') + if type(a:callbacks) is v:t_string call add(a:full_list, a:callbacks) - elseif type(a:callbacks) == type([]) + elseif type(a:callbacks) is v:t_list call extend(a:full_list, a:callbacks) else return 0 @@ -365,7 +365,7 @@ endfunction function! s:GetCallbacks(buffer, fixers) abort if len(a:fixers) let l:callback_list = a:fixers - elseif type(get(b:, 'ale_fixers')) is type([]) + elseif type(get(b:, 'ale_fixers')) is v:t_list " Lists can be used for buffer-local variables only let l:callback_list = b:ale_fixers else @@ -396,7 +396,7 @@ function! s:GetCallbacks(buffer, fixers) abort " Variables with capital characters are needed, or Vim will complain about " funcref variables. for l:Item in l:callback_list - if type(l:Item) == type('') + if type(l:Item) is v:t_string let l:Func = ale#fix#registry#GetFunc(l:Item) if !empty(l:Func) @@ -420,9 +420,7 @@ function! ale#fix#InitBufferData(buffer, fixing_flag) abort " The 'done' flag tells the function for applying changes when fixing " is complete. let g:ale_fix_buffer_data[a:buffer] = { - \ 'vars': getbufvar(a:buffer, ''), \ 'lines_before': getbufline(a:buffer, 1, '$'), - \ 'filename': expand('#' . a:buffer . ':p'), \ 'done': 0, \ 'should_save': a:fixing_flag is# 'save_file', \ 'temporary_directory_list': [], diff --git a/sources_non_forked/ale/autoload/ale/fix/registry.vim b/sources_non_forked/ale/autoload/ale/fix/registry.vim index e148ecd6..dc34e4c5 100644 --- a/sources_non_forked/ale/autoload/ale/fix/registry.vim +++ b/sources_non_forked/ale/autoload/ale/fix/registry.vim @@ -245,32 +245,32 @@ function! ale#fix#registry#Add(name, func, filetypes, desc, ...) abort " This command will throw from the sandbox. let &equalprg=&equalprg - if type(a:name) != type('') + if type(a:name) isnot v:t_string throw '''name'' must be a String' endif - if type(a:func) != type('') + if type(a:func) isnot v:t_string throw '''func'' must be a String' endif - if type(a:filetypes) != type([]) + if type(a:filetypes) isnot v:t_list throw '''filetypes'' must be a List' endif for l:type in a:filetypes - if type(l:type) != type('') + if type(l:type) isnot v:t_string throw 'Each entry of ''filetypes'' must be a String' endif endfor - if type(a:desc) != type('') + if type(a:desc) isnot v:t_string throw '''desc'' must be a String' endif let l:aliases = get(a:000, 0, []) - if type(l:aliases) != type([]) - \|| !empty(filter(copy(l:aliases), 'type(v:val) != type('''')')) + if type(l:aliases) isnot v:t_list + \|| !empty(filter(copy(l:aliases), 'type(v:val) isnot v:t_string')) throw '''aliases'' must be a List of String values' endif diff --git a/sources_non_forked/ale/autoload/ale/handlers/gcc.vim b/sources_non_forked/ale/autoload/ale/handlers/gcc.vim index 4b53652a..b8bac77f 100644 --- a/sources_non_forked/ale/autoload/ale/handlers/gcc.vim +++ b/sources_non_forked/ale/autoload/ale/handlers/gcc.vim @@ -5,6 +5,13 @@ scriptencoding utf-8 let s:pragma_error = '#pragma once in main file' +" Look for lines like the following. +" +" :8:5: warning: conversion lacks type at end of format [-Wformat=] +" :10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) +" -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] +let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' + function! s:IsHeaderFile(filename) abort return a:filename =~? '\v\.(h|hpp)$' endfunction @@ -18,16 +25,63 @@ function! s:RemoveUnicodeQuotes(text) abort return l:text endfunction +" Report problems inside of header files just for gcc and clang +function! s:ParseProblemsInHeaders(buffer, lines) abort + let l:output = [] + let l:include_item = {} + + for l:line in a:lines[: -2] + let l:include_match = matchlist(l:line, '\v^In file included from') + + if !empty(l:include_item) + let l:pattern_match = matchlist(l:line, s:pattern) + + if !empty(l:pattern_match) && l:pattern_match[1] is# '' + 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 \:(\d+):(\d*):?$') + + if !empty(l:stdin_match) + let l:include_item.lnum = str2nr(l:stdin_match[1]) + + if str2nr(l:stdin_match[2]) + let l:include_item.col = str2nr(l:stdin_match[2]) + endif + endif + endif + endfor + + if !empty(l:include_item) && has_key(l:include_item, 'lnum') + call add(l:output, l:include_item) + endif + + return l:output +endfunction + function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort - " Look for lines like the following. - " - " :8:5: warning: conversion lacks type at end of format [-Wformat=] - " :10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) - " -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] - let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' let l:output = [] - for l:match in ale#util#GetMatches(a:lines, l:pattern) + for l:match in ale#util#GetMatches(a:lines, s:pattern) " Filter out the pragma errors if s:IsHeaderFile(bufname(bufnr(''))) \&& l:match[5][:len(s:pragma_error) - 1] is# s:pragma_error @@ -67,3 +121,12 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort return l:output endfunction + +" Handle problems with the GCC format, but report problems inside of headers. +function! ale#handlers#gcc#HandleGCCFormatWithIncludes(buffer, lines) abort + let l:output = ale#handlers#gcc#HandleGCCFormat(a:buffer, a:lines) + + call extend(l:output, s:ParseProblemsInHeaders(a:buffer, a:lines)) + + return l:output +endfunction diff --git a/sources_non_forked/ale/autoload/ale/handlers/rust.vim b/sources_non_forked/ale/autoload/ale/handlers/rust.vim index 537bc731..f12c53be 100644 --- a/sources_non_forked/ale/autoload/ale/handlers/rust.vim +++ b/sources_non_forked/ale/autoload/ale/handlers/rust.vim @@ -32,7 +32,7 @@ function! ale#handlers#rust#HandleRustErrors(buffer, lines) abort let l:error = json_decode(l:errorline) - if has_key(l:error, 'message') && type(l:error.message) == type({}) + if has_key(l:error, 'message') && type(l:error.message) is v:t_dict let l:error = l:error.message endif diff --git a/sources_non_forked/ale/autoload/ale/handlers/writegood.vim b/sources_non_forked/ale/autoload/ale/handlers/writegood.vim index aee7c4de..aff66d5f 100644 --- a/sources_non_forked/ale/autoload/ale/handlers/writegood.vim +++ b/sources_non_forked/ale/autoload/ale/handlers/writegood.vim @@ -59,3 +59,14 @@ function! ale#handlers#writegood#Handle(buffer, lines) abort return l:output endfunction + +" Define the writegood linter for a given filetype. +function! ale#handlers#writegood#DefineLinter(filetype) abort + call ale#linter#Define(a:filetype, { + \ 'name': 'writegood', + \ 'aliases': ['write-good'], + \ 'executable_callback': 'ale#handlers#writegood#GetExecutable', + \ 'command_callback': 'ale#handlers#writegood#GetCommand', + \ 'callback': 'ale#handlers#writegood#Handle', + \}) +endfunction diff --git a/sources_non_forked/ale/autoload/ale/hover.vim b/sources_non_forked/ale/autoload/ale/hover.vim index 6d131adc..dc97ff41 100644 --- a/sources_non_forked/ale/autoload/ale/hover.vim +++ b/sources_non_forked/ale/autoload/ale/hover.vim @@ -63,19 +63,19 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort let l:result = l:result.contents - if type(l:result) is type('') + if type(l:result) is v:t_string " The result can be just a string. let l:result = [l:result] endif - if type(l:result) is type({}) + if type(l:result) is v:t_dict " If the result is an object, then it's markup content. let l:result = [l:result.value] endif - if type(l:result) is type([]) + if type(l:result) is v:t_list " Replace objects with text values. - call map(l:result, 'type(v:val) is type('''') ? v:val : v:val.value') + call map(l:result, 'type(v:val) is v:t_string ? v:val : v:val.value') let l:str = join(l:result, "\n") let l:str = substitute(l:str, '^\s*\(.\{-}\)\s*$', '\1', '') @@ -93,45 +93,51 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort endfunction function! s:ShowDetails(linter, buffer, line, column, opt) abort - let l:Callback = a:linter.lsp is# 'tsserver' - \ ? function('ale#hover#HandleTSServerResponse') - \ : function('ale#hover#HandleLSPResponse') - - let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter, l:Callback) + let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter) if empty(l:lsp_details) return 0 endif let l:id = l:lsp_details.connection_id + let l:root = l:lsp_details.project_root let l:language_id = l:lsp_details.language_id - if a:linter.lsp is# 'tsserver' - let l:column = a:column + function! OnReady(...) abort closure + let l:Callback = a:linter.lsp is# 'tsserver' + \ ? function('ale#hover#HandleTSServerResponse') + \ : function('ale#hover#HandleLSPResponse') + call ale#lsp#RegisterCallback(l:id, l:Callback) - let l:message = ale#lsp#tsserver_message#Quickinfo( - \ a:buffer, - \ a:line, - \ l:column - \) - else - " Send a message saying the buffer has changed first, or the - " hover position probably won't make sense. - call ale#lsp#NotifyForChanges(l:lsp_details) + if a:linter.lsp is# 'tsserver' + let l:column = a:column - let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])]) + let l:message = ale#lsp#tsserver_message#Quickinfo( + \ a:buffer, + \ a:line, + \ l:column + \) + else + " Send a message saying the buffer has changed first, or the + " hover position probably won't make sense. + call ale#lsp#NotifyForChanges(l:id, l:root, a:buffer) - let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column) - endif + let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])]) - let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) + let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column) + endif - let s:hover_map[l:request_id] = { - \ 'buffer': a:buffer, - \ 'line': a:line, - \ 'column': l:column, - \ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0), - \} + let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) + + let s:hover_map[l:request_id] = { + \ 'buffer': a:buffer, + \ 'line': a:line, + \ 'column': l:column, + \ 'hover_from_balloonexpr': get(a:opt, 'called_from_balloonexpr', 0), + \} + endfunction + + call ale#lsp#WaitForCapability(l:id, l:root, 'hover', function('OnReady')) endfunction " Obtain Hover information for the specified position diff --git a/sources_non_forked/ale/autoload/ale/linter.vim b/sources_non_forked/ale/autoload/ale/linter.vim index aa602f7e..683d506a 100644 --- a/sources_non_forked/ale/autoload/ale/linter.vim +++ b/sources_non_forked/ale/autoload/ale/linter.vim @@ -57,11 +57,11 @@ function! ale#linter#GetLintersLoaded() abort endfunction function! s:IsCallback(value) abort - return type(a:value) == type('') || type(a:value) == type(function('type')) + return type(a:value) is v:t_string || type(a:value) is v:t_func endfunction function! s:IsBoolean(value) abort - return type(a:value) == type(0) && (a:value == 0 || a:value == 1) + return type(a:value) is v:t_number && (a:value == 0 || a:value == 1) endfunction function! s:LanguageGetter(buffer) dict abort @@ -69,7 +69,7 @@ function! s:LanguageGetter(buffer) dict abort endfunction function! ale#linter#PreProcess(filetype, linter) abort - if type(a:linter) != type({}) + if type(a:linter) isnot v:t_dict throw 'The linter object must be a Dictionary' endif @@ -79,7 +79,7 @@ function! ale#linter#PreProcess(filetype, linter) abort \ 'lsp': get(a:linter, 'lsp', ''), \} - if type(l:obj.name) != type('') + if type(l:obj.name) isnot v:t_string throw '`name` must be defined to name the linter' endif @@ -114,7 +114,7 @@ function! ale#linter#PreProcess(filetype, linter) abort elseif has_key(a:linter, 'executable') let l:obj.executable = a:linter.executable - if type(l:obj.executable) != type('') + if type(l:obj.executable) isnot v:t_string throw '`executable` must be a string if defined' endif else @@ -130,7 +130,7 @@ function! ale#linter#PreProcess(filetype, linter) abort elseif has_key(a:linter, 'command_chain') let l:obj.command_chain = a:linter.command_chain - if type(l:obj.command_chain) != type([]) + if type(l:obj.command_chain) isnot v:t_list throw '`command_chain` must be a List' endif @@ -148,7 +148,7 @@ function! ale#linter#PreProcess(filetype, linter) abort endif if has_key(l:link, 'output_stream') - if type(l:link.output_stream) != type('') + if type(l:link.output_stream) isnot v:t_string \|| index(['stdout', 'stderr', 'both'], l:link.output_stream) < 0 throw l:err_prefix . '`output_stream` flag must be ' \ . "'stdout', 'stderr', or 'both'" @@ -170,7 +170,7 @@ function! ale#linter#PreProcess(filetype, linter) abort elseif has_key(a:linter, 'command') let l:obj.command = a:linter.command - if type(l:obj.command) != type('') + if type(l:obj.command) isnot v:t_string throw '`command` must be a string if defined' endif else @@ -217,7 +217,7 @@ function! ale#linter#PreProcess(filetype, linter) abort " Default to using the filetype as the language. let l:obj.language = get(a:linter, 'language', a:filetype) - if type(l:obj.language) != type('') + if type(l:obj.language) isnot v:t_string throw '`language` must be a string' endif @@ -257,7 +257,7 @@ function! ale#linter#PreProcess(filetype, linter) abort let l:obj.output_stream = get(a:linter, 'output_stream', 'stdout') - if type(l:obj.output_stream) != type('') + if type(l:obj.output_stream) isnot v:t_string \|| index(['stdout', 'stderr', 'both'], l:obj.output_stream) < 0 throw "`output_stream` must be 'stdout', 'stderr', or 'both'" endif @@ -283,8 +283,8 @@ function! ale#linter#PreProcess(filetype, linter) abort let l:obj.aliases = get(a:linter, 'aliases', []) - if type(l:obj.aliases) != type([]) - \|| len(filter(copy(l:obj.aliases), 'type(v:val) != type('''')')) > 0 + if type(l:obj.aliases) isnot v:t_list + \|| len(filter(copy(l:obj.aliases), 'type(v:val) isnot v:t_string')) > 0 throw '`aliases` must be a List of String values' endif @@ -336,7 +336,7 @@ function! s:GetAliasedFiletype(original_filetype) abort let l:buffer_aliases = get(b:, 'ale_linter_aliases', {}) " b:ale_linter_aliases can be set to a List. - if type(l:buffer_aliases) is type([]) + if type(l:buffer_aliases) is v:t_list return l:buffer_aliases endif @@ -360,7 +360,7 @@ endfunction function! ale#linter#ResolveFiletype(original_filetype) abort let l:filetype = s:GetAliasedFiletype(a:original_filetype) - if type(l:filetype) != type([]) + if type(l:filetype) isnot v:t_list return [l:filetype] endif @@ -376,7 +376,7 @@ function! s:GetLinterNames(original_filetype) abort endif " b:ale_linters can be set to a List. - if type(l:buffer_ale_linters) is type([]) + if type(l:buffer_ale_linters) is v:t_list return l:buffer_ale_linters endif @@ -414,9 +414,9 @@ function! ale#linter#Get(original_filetypes) abort let l:all_linters = ale#linter#GetAll(l:filetype) let l:filetype_linters = [] - if type(l:linter_names) == type('') && l:linter_names is# 'all' + if type(l:linter_names) is v:t_string && l:linter_names is# 'all' let l:filetype_linters = l:all_linters - elseif type(l:linter_names) == type([]) + elseif type(l:linter_names) is v:t_list " Select only the linters we or the user has specified. for l:linter in l:all_linters let l:name_list = [l:linter.name] + l:linter.aliases diff --git a/sources_non_forked/ale/autoload/ale/loclist_jumping.vim b/sources_non_forked/ale/autoload/ale/loclist_jumping.vim index 7ed9e6ba..fd5ff922 100644 --- a/sources_non_forked/ale/autoload/ale/loclist_jumping.vim +++ b/sources_non_forked/ale/autoload/ale/loclist_jumping.vim @@ -66,6 +66,7 @@ function! ale#loclist_jumping#Jump(direction, wrap) abort let l:nearest = ale#loclist_jumping#FindNearest(a:direction, a:wrap) if !empty(l:nearest) + normal! m` call cursor(l:nearest) endif endfunction @@ -82,6 +83,7 @@ function! ale#loclist_jumping#JumpToIndex(index) abort let l:item = l:loclist[a:index] if !empty(l:item) + normal! m` call cursor([l:item.lnum, l:item.col]) endif endfunction diff --git a/sources_non_forked/ale/autoload/ale/lsp.vim b/sources_non_forked/ale/autoload/ale/lsp.vim index e44b5bc3..5d900138 100644 --- a/sources_non_forked/ale/autoload/ale/lsp.vim +++ b/sources_non_forked/ale/autoload/ale/lsp.vim @@ -15,13 +15,23 @@ function! ale#lsp#NewConnection(initialization_options) abort " open_documents: A Dictionary mapping buffers to b:changedtick, keeping " track of when documents were opened, and when we last changed them. " callback_list: A list of callbacks for handling LSP responses. + " initialization_options: Options to send to the server. + " capabilities: Features the server supports. let l:conn = { + \ 'is_tsserver': 0, \ 'id': '', \ 'data': '', \ 'projects': {}, \ 'open_documents': {}, \ 'callback_list': [], \ 'initialization_options': a:initialization_options, + \ 'capabilities': { + \ 'hover': 0, + \ 'references': 0, + \ 'completion': 0, + \ 'completion_trigger_characters': [], + \ 'definition': 0, + \ }, \} call add(s:connections, l:conn) @@ -44,6 +54,11 @@ function! s:FindConnection(key, value) abort return {} endfunction +" Get the capabilities for a connection, or an empty Dictionary. +function! ale#lsp#GetConnectionCapabilities(id) abort + return get(s:FindConnection('id', a:id), 'capabilities', {}) +endfunction + function! ale#lsp#GetNextMessageID() abort " Use the current ID let l:id = g:ale_lsp_next_message_id @@ -174,6 +189,16 @@ function! s:MarkProjectAsInitialized(conn, project) abort " Remove the messages now. let a:conn.message_queue = [] + + " Call capabilities callbacks queued for the project. + for [l:capability, l:Callback] in a:project.capabilities_queue + if a:conn.is_tsserver || a:conn.capabilities[l:capability] + call call(l:Callback, [a:conn.id, a:project.root]) + endif + endfor + + " Clear the queued callbacks now. + let a:project.capabilities_queue = [] endfunction function! s:HandleInitializeResponse(conn, response) abort @@ -185,6 +210,38 @@ function! s:HandleInitializeResponse(conn, response) abort endif endfunction +" Update capabilities from the server, so we know which features the server +" supports. +function! s:UpdateCapabilities(conn, capabilities) abort + if type(a:capabilities) isnot v:t_dict + return + endif + + if get(a:capabilities, 'hoverProvider') is v:true + let a:conn.capabilities.hover = 1 + endif + + if get(a:capabilities, 'referencesProvider') is v:true + let a:conn.capabilities.references = 1 + endif + + if !empty(get(a:capabilities, 'completionProvider')) + let a:conn.capabilities.completion = 1 + endif + + if type(get(a:capabilities, 'completionProvider')) is v:t_dict + let l:chars = get(a:capabilities.completionProvider, 'triggerCharacters') + + if type(l:chars) is v:t_list + let a:conn.capabilities.completion_trigger_characters = l:chars + endif + endif + + if get(a:capabilities, 'definitionProvider') is v:true + let a:conn.capabilities.definition = 1 + endif +endfunction + function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort let l:uninitialized_projects = [] @@ -200,6 +257,8 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort if get(a:response, 'method', '') is# '' if has_key(get(a:response, 'result', {}), 'capabilities') + call s:UpdateCapabilities(a:conn, a:response.result.capabilities) + for [l:dir, l:project] in l:uninitialized_projects call s:MarkProjectAsInitialized(a:conn, l:project) endfor @@ -216,7 +275,7 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort endfunction function! ale#lsp#HandleMessage(conn, message) abort - if type(a:message) != type('') + if type(a:message) isnot v:t_string " Ignore messages that aren't strings. return endif @@ -254,22 +313,43 @@ function! s:HandleCommandMessage(job_id, message) abort call ale#lsp#HandleMessage(l:conn, a:message) endfunction -function! ale#lsp#RegisterProject(conn, project_root) abort +" Given a connection ID, mark it as a tsserver connection, so it will be +" handled that way. +function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort + let l:conn = s:FindConnection('id', a:conn_id) + + if !empty(l:conn) + let l:conn.is_tsserver = 1 + endif +endfunction + +" Register a project for an LSP connection. +" +" This function will throw if the connection doesn't exist. +function! ale#lsp#RegisterProject(conn_id, project_root) abort + let l:conn = s:FindConnection('id', a:conn_id) + " Empty strings can't be used for Dictionary keys in NeoVim, due to E713. " This appears to be a nonsensical bug in NeoVim. let l:key = empty(a:project_root) ? '<>' : a:project_root - if !has_key(a:conn.projects, l:key) + if !has_key(l:conn.projects, l:key) " Tools without project roots are ready right away, like tsserver. - let a:conn.projects[l:key] = { + let l:conn.projects[l:key] = { + \ 'root': a:project_root, \ 'initialized': empty(a:project_root), \ 'init_request_id': 0, \ 'message_queue': [], + \ 'capabilities_queue': [], \} endif endfunction function! ale#lsp#GetProject(conn, project_root) abort + if empty(a:conn) + return {} + endif + let l:key = empty(a:project_root) ? '<>' : a:project_root return get(a:conn.projects, l:key, {}) @@ -279,7 +359,7 @@ endfunction " " The job ID will be returned for for the program if it ran, otherwise " 0 will be returned. -function! ale#lsp#StartProgram(executable, command, project_root, callback, initialization_options) abort +function! ale#lsp#StartProgram(executable, command, init_options) abort if !executable(a:executable) return 0 endif @@ -287,7 +367,7 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init let l:conn = s:FindConnection('executable', a:executable) " Get the current connection or a new one. - let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options) + let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options) let l:conn.executable = a:executable if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id) @@ -305,18 +385,15 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init endif let l:conn.id = l:job_id - " Add the callback to the List if it's not there already. - call uniq(sort(add(l:conn.callback_list, a:callback))) - call ale#lsp#RegisterProject(l:conn, a:project_root) return l:job_id endfunction " Connect to an address and set up a callback for handling responses. -function! ale#lsp#ConnectToAddress(address, project_root, callback, initialization_options) abort +function! ale#lsp#ConnectToAddress(address, init_options) abort let l:conn = s:FindConnection('id', a:address) " Get the current connection or a new one. - let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options) + let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:init_options) if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id) let l:conn.channel_id = ale#socket#Open(a:address, { @@ -329,13 +406,21 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback, initializati endif let l:conn.id = a:address - " Add the callback to the List if it's not there already. - call uniq(sort(add(l:conn.callback_list, a:callback))) - call ale#lsp#RegisterProject(l:conn, a:project_root) return a:address endfunction +" Given a connection ID and a callback, register that callback for handling +" messages if the connection exists. +function! ale#lsp#RegisterCallback(conn_id, callback) abort + let l:conn = s:FindConnection('id', a:conn_id) + + if !empty(l:conn) + " Add the callback to the List if it's not there already. + call uniq(sort(add(l:conn.callback_list, a:callback))) + endif +endfunction + " Stop all LSP connections, closing all jobs and channels, and removing any " queued messages. function! ale#lsp#StopAll() abort @@ -373,11 +458,6 @@ function! ale#lsp#Send(conn_id, message, ...) abort let l:project_root = get(a:000, 0, '') let l:conn = s:FindConnection('id', a:conn_id) - - if empty(l:conn) - return 0 - endif - let l:project = ale#lsp#GetProject(l:conn, l:project_root) if empty(l:project) @@ -411,45 +491,22 @@ function! ale#lsp#Send(conn_id, message, ...) abort return l:id == 0 ? -1 : l:id endfunction -" The Document details Dictionary should contain the following keys. -" -" buffer - The buffer number for the document. -" connection_id - The connection ID for the LSP server. -" command - The command to run to start the LSP connection. -" project_root - The project root for the LSP project. -" language_id - The language ID for the project, like 'python', 'rust', etc. - -" Create a new Dictionary containing more connection details, with the -" following information added: -" -" conn - An existing LSP connection for the document. -" document_open - 1 if the document is currently open, 0 otherwise. -function! s:ExtendDocumentDetails(details) abort - let l:extended = copy(a:details) - let l:conn = s:FindConnection('id', a:details.connection_id) - - let l:extended.conn = l:conn - let l:extended.document_open = !empty(l:conn) - \ && has_key(l:conn.open_documents, a:details.buffer) - - return l:extended -endfunction - " Notify LSP servers or tsserver if a document is opened, if needed. " If a document is opened, 1 will be returned, otherwise 0 will be returned. -function! ale#lsp#OpenDocument(basic_details) abort - let l:d = s:ExtendDocumentDetails(a:basic_details) +function! ale#lsp#OpenDocument(conn_id, project_root, buffer, language_id) abort + let l:conn = s:FindConnection('id', a:conn_id) let l:opened = 0 - if !empty(l:d.conn) && !l:d.document_open - if empty(l:d.language_id) - let l:message = ale#lsp#tsserver_message#Open(l:d.buffer) + " FIXME: Return 1 if the document is already open? + if !empty(l:conn) && !has_key(l:conn.open_documents, a:buffer) + if l:conn.is_tsserver + let l:message = ale#lsp#tsserver_message#Open(a:buffer) else - let l:message = ale#lsp#message#DidOpen(l:d.buffer, l:d.language_id) + let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id) endif - call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root) - let l:d.conn.open_documents[l:d.buffer] = getbufvar(l:d.buffer, 'changedtick') + call ale#lsp#Send(a:conn_id, l:message, a:project_root) + let l:conn.open_documents[a:buffer] = getbufvar(a:buffer, 'changedtick') let l:opened = 1 endif @@ -458,25 +515,50 @@ endfunction " Notify LSP servers or tsserver that a document has changed, if needed. " If a notification is sent, 1 will be returned, otherwise 0 will be returned. -function! ale#lsp#NotifyForChanges(basic_details) abort - let l:d = s:ExtendDocumentDetails(a:basic_details) +function! ale#lsp#NotifyForChanges(conn_id, project_root, buffer) abort + let l:conn = s:FindConnection('id', a:conn_id) let l:notified = 0 - if l:d.document_open - let l:new_tick = getbufvar(l:d.buffer, 'changedtick') + if !empty(l:conn) && has_key(l:conn.open_documents, a:buffer) + let l:new_tick = getbufvar(a:buffer, 'changedtick') - if l:d.conn.open_documents[l:d.buffer] < l:new_tick - if empty(l:d.language_id) - let l:message = ale#lsp#tsserver_message#Change(l:d.buffer) + if l:conn.open_documents[a:buffer] < l:new_tick + if l:conn.is_tsserver + let l:message = ale#lsp#tsserver_message#Change(a:buffer) else - let l:message = ale#lsp#message#DidChange(l:d.buffer) + let l:message = ale#lsp#message#DidChange(a:buffer) endif - call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root) - let l:d.conn.open_documents[l:d.buffer] = l:new_tick + call ale#lsp#Send(a:conn_id, l:message, a:project_root) + let l:conn.open_documents[a:buffer] = l:new_tick let l:notified = 1 endif endif return l:notified endfunction + +" Given some LSP details that must contain at least `connection_id` and +" `project_root` keys, +function! ale#lsp#WaitForCapability(conn_id, project_root, capability, callback) abort + let l:conn = s:FindConnection('id', a:conn_id) + let l:project = ale#lsp#GetProject(l:conn, a:project_root) + + if empty(l:project) + return 0 + endif + + if type(get(l:conn.capabilities, a:capability, v:null)) isnot v:t_number + throw 'Invalid capability ' . a:capability + endif + + if l:project.initialized + if l:conn.is_tsserver || l:conn.capabilities[a:capability] + " The project has been initialized, so call the callback now. + call call(a:callback, [a:conn_id, a:project_root]) + endif + else + " Call the callback later, once we have the information we need. + call add(l:project.capabilities_queue, [a:capability, a:callback]) + endif +endfunction diff --git a/sources_non_forked/ale/autoload/ale/lsp/response.vim b/sources_non_forked/ale/autoload/ale/lsp/response.vim index 3ac65388..48740ad1 100644 --- a/sources_non_forked/ale/autoload/ale/lsp/response.vim +++ b/sources_non_forked/ale/autoload/ale/lsp/response.vim @@ -88,7 +88,7 @@ function! ale#lsp#response#ReadTSServerDiagnostics(response) abort endfunction function! ale#lsp#response#GetErrorMessage(response) abort - if type(get(a:response, 'error', 0)) isnot type({}) + if type(get(a:response, 'error', 0)) isnot v:t_dict return '' endif @@ -108,12 +108,12 @@ function! ale#lsp#response#GetErrorMessage(response) abort " Include the traceback or error data as details, if present. let l:error_data = get(a:response.error, 'data', {}) - if type(l:error_data) is type('') + if type(l:error_data) is v:t_string let l:message .= "\n" . l:error_data else let l:traceback = get(l:error_data, 'traceback', []) - if type(l:traceback) is type([]) && !empty(l:traceback) + if type(l:traceback) is v:t_list && !empty(l:traceback) let l:message .= "\n" . join(l:traceback, "\n") endif endif diff --git a/sources_non_forked/ale/autoload/ale/lsp_linter.vim b/sources_non_forked/ale/autoload/ale/lsp_linter.vim index 6dc78e4c..87aee759 100644 --- a/sources_non_forked/ale/autoload/ale/lsp_linter.vim +++ b/sources_non_forked/ale/autoload/ale/lsp_linter.vim @@ -126,10 +126,9 @@ function! ale#lsp_linter#GetOptions(buffer, linter) abort return l:initialization_options endfunction -" Given a buffer, an LSP linter, and a callback to register for handling -" messages, start up an LSP linter and get ready to receive errors or -" completions. -function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort +" Given a buffer, an LSP linter, start up an LSP linter and get ready to +" receive messages for the document. +function! ale#lsp_linter#StartLSP(buffer, linter) abort let l:command = '' let l:address = '' let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer) @@ -140,16 +139,11 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort return {} endif - let l:initialization_options = ale#lsp_linter#GetOptions(a:buffer, a:linter) + let l:init_options = ale#lsp_linter#GetOptions(a:buffer, a:linter) if a:linter.lsp is# 'socket' let l:address = ale#linter#GetAddress(a:buffer, a:linter) - let l:conn_id = ale#lsp#ConnectToAddress( - \ l:address, - \ l:root, - \ a:callback, - \ l:initialization_options, - \) + let l:conn_id = ale#lsp#ConnectToAddress(l:address, l:init_options) else let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) @@ -164,14 +158,10 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort let l:conn_id = ale#lsp#StartProgram( \ l:executable, \ l:command, - \ l:root, - \ a:callback, - \ l:initialization_options, + \ l:init_options, \) endif - let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer) - if empty(l:conn_id) if g:ale_history_enabled && !empty(l:command) call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command) @@ -180,6 +170,16 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort return {} endif + " tsserver behaves differently, so tell the LSP API that it is tsserver. + if a:linter.lsp is# 'tsserver' + call ale#lsp#MarkConnectionAsTsserver(l:conn_id) + endif + + " Register the project now the connection is ready. + call ale#lsp#RegisterProject(l:conn_id, l:root) + + let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer) + let l:details = { \ 'buffer': a:buffer, \ 'connection_id': l:conn_id, @@ -188,7 +188,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort \ 'language_id': l:language_id, \} - if ale#lsp#OpenDocument(l:details) + if ale#lsp#OpenDocument(l:conn_id, l:root, a:buffer, l:language_id) if g:ale_history_enabled && !empty(l:command) call ale#history#Add(a:buffer, 'started', l:conn_id, l:command) endif @@ -196,7 +196,7 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort " The change message needs to be sent for tsserver before doing anything. if a:linter.lsp is# 'tsserver' - call ale#lsp#NotifyForChanges(l:details) + call ale#lsp#NotifyForChanges(l:conn_id, l:root, a:buffer) endif return l:details @@ -204,11 +204,7 @@ endfunction function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort let l:info = g:ale_buffer_info[a:buffer] - let l:lsp_details = ale#lsp_linter#StartLSP( - \ a:buffer, - \ a:linter, - \ function('ale#lsp_linter#HandleLSPResponse'), - \) + let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter) if empty(l:lsp_details) return 0 @@ -217,25 +213,25 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort let l:id = l:lsp_details.connection_id let l:root = l:lsp_details.project_root + " Register a callback now for handling errors now. + let l:Callback = function('ale#lsp_linter#HandleLSPResponse') + call ale#lsp#RegisterCallback(l:id, l:Callback) + " Remember the linter this connection is for. let s:lsp_linter_map[l:id] = a:linter.name if a:linter.lsp is# 'tsserver' let l:message = ale#lsp#tsserver_message#Geterr(a:buffer) - let l:request_id = ale#lsp#Send(l:id, l:message, l:root) - - let l:notified = l:request_id != 0 + let l:notified = ale#lsp#Send(l:id, l:message, l:root) != 0 else - let l:notified = ale#lsp#NotifyForChanges(l:lsp_details) + let l:notified = ale#lsp#NotifyForChanges(l:id, l:root, a:buffer) endif " If this was a file save event, also notify the server of that. if a:linter.lsp isnot# 'tsserver' \&& getbufvar(a:buffer, 'ale_save_event_fired', 0) let l:save_message = ale#lsp#message#DidSave(a:buffer) - let l:request_id = ale#lsp#Send(l:id, l:save_message, l:root) - - let l:notified = l:request_id != 0 + let l:notified = ale#lsp#Send(l:id, l:save_message, l:root) != 0 endif if l:notified diff --git a/sources_non_forked/ale/autoload/ale/path.vim b/sources_non_forked/ale/autoload/ale/path.vim index 45da3709..2d8a6ac7 100644 --- a/sources_non_forked/ale/autoload/ale/path.vim +++ b/sources_non_forked/ale/autoload/ale/path.vim @@ -105,6 +105,21 @@ function! ale#path#GetAbsPath(base_directory, filename) abort return ale#path#Simplify(a:base_directory . l:sep . a:filename) endfunction +" Given a path, return the directory name for that path, with no trailing +" slashes. If the argument is empty(), return an empty string. +function! ale#path#Dirname(path) abort + if empty(a:path) + return '' + endif + + " For /foo/bar/ we need :h:h to get /foo + if a:path[-1:] is# '/' + return fnamemodify(a:path, ':h:h') + endif + + return fnamemodify(a:path, ':h') +endfunction + " Given a buffer number and a relative or absolute path, return 1 if the " two paths represent the same file on disk. function! ale#path#IsBufferPath(buffer, complex_filename) abort diff --git a/sources_non_forked/ale/autoload/ale/references.vim b/sources_non_forked/ale/autoload/ale/references.vim index 89df69eb..3a710b7b 100644 --- a/sources_non_forked/ale/autoload/ale/references.vim +++ b/sources_non_forked/ale/autoload/ale/references.vim @@ -68,37 +68,46 @@ function! s:FindReferences(linter) abort let l:buffer = bufnr('') let [l:line, l:column] = getcurpos()[1:2] - let l:Callback = a:linter.lsp is# 'tsserver' - \ ? function('ale#references#HandleTSServerResponse') - \ : function('ale#references#HandleLSPResponse') + if a:linter.lsp isnot# 'tsserver' + let l:column = min([l:column, len(getline(l:line))]) + endif - let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) + let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter) if empty(l:lsp_details) return 0 endif let l:id = l:lsp_details.connection_id + let l:root = l:lsp_details.project_root - if a:linter.lsp is# 'tsserver' - let l:message = ale#lsp#tsserver_message#References( - \ l:buffer, - \ l:line, - \ l:column - \) - else - " Send a message saying the buffer has changed first, or the - " references position probably won't make sense. - call ale#lsp#NotifyForChanges(l:lsp_details) + function! OnReady(...) abort closure + let l:Callback = a:linter.lsp is# 'tsserver' + \ ? function('ale#references#HandleTSServerResponse') + \ : function('ale#references#HandleLSPResponse') - let l:column = min([l:column, len(getline(l:line))]) + call ale#lsp#RegisterCallback(l:id, l:Callback) - let l:message = ale#lsp#message#References(l:buffer, l:line, l:column) - endif + if a:linter.lsp is# 'tsserver' + let l:message = ale#lsp#tsserver_message#References( + \ l:buffer, + \ l:line, + \ l:column + \) + else + " Send a message saying the buffer has changed first, or the + " references position probably won't make sense. + call ale#lsp#NotifyForChanges(l:id, l:root, l:buffer) - let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) + let l:message = ale#lsp#message#References(l:buffer, l:line, l:column) + endif - let s:references_map[l:request_id] = {} + let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root) + + let s:references_map[l:request_id] = {} + endfunction + + call ale#lsp#WaitForCapability(l:id, l:root, 'references', function('OnReady')) endfunction function! ale#references#Find() abort diff --git a/sources_non_forked/ale/autoload/ale/sign.vim b/sources_non_forked/ale/autoload/ale/sign.vim index a0dde359..af863682 100644 --- a/sources_non_forked/ale/autoload/ale/sign.vim +++ b/sources_non_forked/ale/autoload/ale/sign.vim @@ -211,7 +211,7 @@ function! s:BuildSignMap(buffer, current_sign_list, grouped_items) abort if l:max_signs is 0 let l:selected_grouped_items = [] - elseif type(l:max_signs) is type(0) && l:max_signs > 0 + elseif type(l:max_signs) is v:t_number && l:max_signs > 0 let l:selected_grouped_items = a:grouped_items[:l:max_signs - 1] else let l:selected_grouped_items = a:grouped_items diff --git a/sources_non_forked/ale/autoload/ale/test.vim b/sources_non_forked/ale/autoload/ale/test.vim index bea10c53..083b546f 100644 --- a/sources_non_forked/ale/autoload/ale/test.vim +++ b/sources_non_forked/ale/autoload/ale/test.vim @@ -52,3 +52,26 @@ function! ale#test#SetFilename(path) abort silent! noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path)) endfunction + +function! s:RemoveModule(results) abort + for l:item in a:results + if has_key(l:item, 'module') + call remove(l:item, 'module') + endif + endfor +endfunction + +" Return loclist data without the module string, only in newer Vim versions. +function! ale#test#GetLoclistWithoutModule() abort + let l:results = getloclist(0) + call s:RemoveModule(l:results) + + return l:results +endfunction + +function! ale#test#GetQflistWithoutModule() abort + let l:results = getqflist() + call s:RemoveModule(l:results) + + return l:results +endfunction diff --git a/sources_non_forked/ale/autoload/ale/toggle.vim b/sources_non_forked/ale/autoload/ale/toggle.vim index 6b1affc4..da108782 100644 --- a/sources_non_forked/ale/autoload/ale/toggle.vim +++ b/sources_non_forked/ale/autoload/ale/toggle.vim @@ -43,7 +43,7 @@ function! ale#toggle#Toggle() abort call s:CleanupEveryBuffer() call s:DisablePostamble() - if has('balloon_eval') + if exists('*ale#balloon#Disable') call ale#balloon#Disable() endif endif diff --git a/sources_non_forked/ale/autoload/ale/util.vim b/sources_non_forked/ale/autoload/ale/util.vim index fb6dc085..be37d1b5 100644 --- a/sources_non_forked/ale/autoload/ale/util.vim +++ b/sources_non_forked/ale/autoload/ale/util.vim @@ -79,7 +79,7 @@ function! ale#util#GetLineCount(buffer) abort endfunction function! ale#util#GetFunction(string_or_ref) abort - if type(a:string_or_ref) == type('') + if type(a:string_or_ref) is v:t_string return function(a:string_or_ref) endif @@ -89,11 +89,11 @@ endfunction function! ale#util#Open(filename, line, column, options) abort if get(a:options, 'open_in_tab', 0) call ale#util#Execute('tabedit ' . fnameescape(a:filename)) - else + elseif bufnr(a:filename) isnot bufnr('') " Open another file only if we need to. - if bufnr(a:filename) isnot bufnr('') - call ale#util#Execute('edit ' . fnameescape(a:filename)) - endif + call ale#util#Execute('edit ' . fnameescape(a:filename)) + else + normal! m` endif call cursor(a:line, a:column) @@ -303,8 +303,8 @@ endfunction " Only the first pattern which matches a line will be returned. function! ale#util#GetMatches(lines, patterns) abort let l:matches = [] - let l:lines = type(a:lines) == type([]) ? a:lines : [a:lines] - let l:patterns = type(a:patterns) == type([]) ? a:patterns : [a:patterns] + let l:lines = type(a:lines) is v:t_list ? a:lines : [a:lines] + let l:patterns = type(a:patterns) is v:t_list ? a:patterns : [a:patterns] for l:line in l:lines for l:pattern in l:patterns @@ -382,7 +382,7 @@ function! ale#util#FuzzyJSONDecode(data, default) abort return a:default endif - let l:str = type(a:data) == type('') ? a:data : join(a:data, '') + let l:str = type(a:data) is v:t_string ? a:data : join(a:data, '') try let l:result = json_decode(l:str) diff --git a/sources_non_forked/ale/doc/ale-c.txt b/sources_non_forked/ale/doc/ale-c.txt index c41f3bc8..282dcf94 100644 --- a/sources_non_forked/ale/doc/ale-c.txt +++ b/sources_non_forked/ale/doc/ale-c.txt @@ -25,13 +25,28 @@ g:ale_c_build_dir *g:ale_c_build_dir* Type: |String| Default: `''` - A path to the directory containing the `compile_commands.json` file to use - with c-family linters. Usually setting this option to a non-empty string - will override the |g:ale_c_build_dir_names| option to impose a compilation - database (it can be useful if multiple builds are in multiple build - subdirectories in the project tree). - This feature is also most useful for the clang tools linters, wrapped - around LibTooling (namely clang-tidy here) + For programs that can read `compile_commands.json` files, this option can be + set to the directory containing the file for the project. ALE will try to + determine the location of `compile_commands.json` automatically, but if your + file exists in some other directory, you can set this option so ALE will + know where it is. + + 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* @@ -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. +=============================================================================== +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* @@ -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. +=============================================================================== +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* diff --git a/sources_non_forked/ale/doc/ale-cpp.txt b/sources_non_forked/ale/doc/ale-cpp.txt index 8bd111e4..be7e840c 100644 --- a/sources_non_forked/ale/doc/ale-cpp.txt +++ b/sources_non_forked/ale/doc/ale-cpp.txt @@ -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| * |g:ale_c_parse_makefile| +* |g:ale_c_parse_compile_commands| =============================================================================== diff --git a/sources_non_forked/ale/doc/ale-development.txt b/sources_non_forked/ale/doc/ale-development.txt index 5688c1cc..d83c98f2 100644 --- a/sources_non_forked/ale/doc/ale-development.txt +++ b/sources_non_forked/ale/doc/ale-development.txt @@ -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 set. Use `ale#util#Tempname()` instead, which temporarily sets `$TMPDIR` 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. @@ -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. 1. Vim 8.0.0027 on Linux via Travis CI. -2. NeoVim 0.2.0 on Linux via Travis CI. -3. NeoVim 0.3.0 on Linux via Travis CI. -4. Vim 8 (stable builds) on Windows via AppVeyor. +2. Vim 8.1.0204 on Linux via Travis CI. +3. NeoVim 0.2.0 on Linux via Travis CI. +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 tests by installing Docker and running the `run-tests` script. Follow the diff --git a/sources_non_forked/ale/doc/ale-fortran.txt b/sources_non_forked/ale/doc/ale-fortran.txt index ed6bc724..c9b7e8e2 100644 --- a/sources_non_forked/ale/doc/ale-fortran.txt +++ b/sources_non_forked/ale/doc/ale-fortran.txt @@ -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. +=============================================================================== +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: diff --git a/sources_non_forked/ale/doc/ale-haskell.txt b/sources_non_forked/ale/doc/ale-haskell.txt index 09ecd92d..15d3ce48 100644 --- a/sources_non_forked/ale/doc/ale-haskell.txt +++ b/sources_non_forked/ale/doc/ale-haskell.txt @@ -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. +=============================================================================== +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* diff --git a/sources_non_forked/ale/doc/ale-python.txt b/sources_non_forked/ale/doc/ale-python.txt index e24ef1aa..093ea758 100644 --- a/sources_non_forked/ale/doc/ale-python.txt +++ b/sources_non_forked/ale/doc/ale-python.txt @@ -397,6 +397,36 @@ g:ale_python_pyre_use_global *g:ale_python_pyre_use_global* 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* diff --git a/sources_non_forked/ale/doc/ale.txt b/sources_non_forked/ale/doc/ale.txt index 2b25091e..15a20469 100644 --- a/sources_non_forked/ale/doc/ale.txt +++ b/sources_non_forked/ale/doc/ale.txt @@ -26,9 +26,11 @@ CONTENTS *ale-contents* gawk................................|ale-awk-gawk| c.....................................|ale-c-options| clang...............................|ale-c-clang| + clangd..............................|ale-c-clangd| clang-format........................|ale-c-clangformat| clangtidy...........................|ale-c-clangtidy| cppcheck............................|ale-c-cppcheck| + cquery..............................|ale-c-cquery| flawfinder..........................|ale-c-flawfinder| gcc.................................|ale-c-gcc| chef..................................|ale-chef-options| @@ -76,6 +78,7 @@ CONTENTS *ale-contents* fish..................................|ale-fish-options| fortran...............................|ale-fortran-options| gcc.................................|ale-fortran-gcc| + language_server.....................|ale-fortran-language-server| fountain..............................|ale-fountain-options| fusionscript..........................|ale-fuse-options| fusion-lint.........................|ale-fuse-fusionlint| @@ -98,6 +101,7 @@ CONTENTS *ale-contents* haskell...............................|ale-haskell-options| brittany............................|ale-haskell-brittany| ghc.................................|ale-haskell-ghc| + cabal-ghc...........................|ale-haskell-cabal-ghc| hdevtools...........................|ale-haskell-hdevtools| hfmt................................|ale-haskell-hfmt| stack-build.........................|ale-haskell-stack-build| @@ -203,6 +207,7 @@ CONTENTS *ale-contents* pylint..............................|ale-python-pylint| pyls................................|ale-python-pyls| pyre................................|ale-python-pyre| + vulture.............................|ale-python-vulture| yapf................................|ale-python-yapf| qml...................................|ale-qml-options| qmlfmt..............................|ale-qml-qmlfmt| @@ -333,7 +338,7 @@ Notes: * Awk: `gawk` * Bash: `language-server`, `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` * CUDA: `nvcc`!! * C#: `mcs`, `mcsc`!! @@ -355,7 +360,7 @@ Notes: * Erb: `erb`, `erubi`, `erubis` * Erlang: `erlc`, `SyntaxErl` * Fish: `fish` (-n flag) -* Fortran: `gcc` +* Fortran: `gcc`, `language_server` * Fountain: `proselint` * FusionScript: `fusion-lint` * Git Commit Messages: `gitlint` @@ -364,7 +369,7 @@ Notes: * GraphQL: `eslint`, `gqlint`, `prettier` * Haml: `haml-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` * Idris: `idris` * Java: `checkstyle`, `javac`, `google-java-format`, `PMD` @@ -395,7 +400,7 @@ Notes: * proto: `protoc-gen-lint` * Pug: `pug-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` * R: `lintr` * ReasonML: `merlin`, `ols`, `refmt` @@ -1413,8 +1418,7 @@ g:ale_set_balloons *g:ale_set_balloons* *b:ale_set_balloons* Type: |Number| - Default: `(has('balloon_eval') && has('gui_running'))` - `|| (has('balloon_eval_term') && !has('gui_running'))` + Default: `has('balloon_eval') && has('gui_running')` When this option is set to `1`, balloon messages will be displayed for 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 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. Balloons cannot be enabled for a specific buffer when not initially enabled globally. diff --git a/sources_non_forked/ale/plugin/ale.vim b/sources_non_forked/ale/plugin/ale.vim index ad2639bb..f0f90b6b 100644 --- a/sources_non_forked/ale/plugin/ale.vim +++ b/sources_non_forked/ale/plugin/ale.vim @@ -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) " This flag can be set to 0 to disable balloon support. -let g:ale_set_balloons = get(g:, 'ale_set_balloons', -\ (has('balloon_eval') && has('gui_running')) -\ || (has('balloon_eval_term') && !has('gui_running')) -\) +let g:ale_set_balloons = get(g:, 'ale_set_balloons', has('balloon_eval') && has('gui_running')) " 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) diff --git a/sources_non_forked/vim-commentary/plugin/commentary.vim b/sources_non_forked/vim-commentary/plugin/commentary.vim index 0290b7f5..862ca820 100644 --- a/sources_non_forked/vim-commentary/plugin/commentary.vim +++ b/sources_non_forked/vim-commentary/plugin/commentary.vim @@ -9,8 +9,8 @@ endif let g:loaded_commentary = 1 function! s:surroundings() abort - return split(get(b:, 'commentary_format', substitute(substitute( - \ &commentstring, '\S\zs%s',' %s','') ,'%s\ze\S', '%s ', '')), '%s', 1) + return split(get(b:, 'commentary_format', substitute(substitute(substitute( + \ &commentstring, '^$', '%s', ''), '\S\zs%s',' %s', '') ,'%s\ze\S', '%s ', '')), '%s', 1) endfunction function! s:strip_white_space(l,r,line) abort diff --git a/sources_non_forked/vim-fugitive/autoload/fugitive.vim b/sources_non_forked/vim-fugitive/autoload/fugitive.vim index 3925846e..6dcd5ee7 100644 --- a/sources_non_forked/vim-fugitive/autoload/fugitive.vim +++ b/sources_non_forked/vim-fugitive/autoload/fugitive.vim @@ -91,18 +91,24 @@ function! s:executable(binary) abort return s:executables[a:binary] endfunction -function! s:git_command() abort +function! s:UserCommand() abort return get(g:, 'fugitive_git_command', g:fugitive_git_executable) endfunction -function! fugitive#Prepare(...) abort - let args = copy(a:000) - if empty(args) - return g:fugitive_git_executable - elseif args[0] !~# '^-' && args[0] =~# '[\/.]' - let args[0] = '--git-dir=' . args[0] - endif - return g:fugitive_git_executable . ' ' . join(map(args, 's:shellesc(v:val)'), ' ') +function! s:System(cmd) abort + try + return system(a:cmd) + catch /^Vim\%((\a\+)\)\=:E484:/ + let opts = ['shell', 'shellcmdflag', 'shellredir', 'shellquote', 'shellxquote', 'shellxescape', 'shellslash'] + call filter(opts, 'exists("+".v:val) && !empty(eval("&".v:val))') + call map(opts, 'v:val."=".eval("&".v:val)') + call s:throw('failed to run `' . a:cmd . '` with ' . join(opts, ' ')) + endtry +endfunction + +function! s:Prepare(dir, ...) abort + let args = ['--git-dir=' . a:dir] + (a:000) + return g:fugitive_git_executable . ' ' . join(map(args, 's:shellesc(v:val)')) endfunction let s:git_versions = {} @@ -113,6 +119,65 @@ function! fugitive#GitVersion(...) abort return s:git_versions[g:fugitive_git_executable] endfunction +function! s:Tree(...) abort + return FugitiveTreeForGitDir(a:0 ? a:1 : get(b:, 'git_dir', '')) +endfunction + +function! s:TreeChomp(...) abort + let args = copy(type(a:1) == type([]) ? a:1 : a:000) + let dir = a:0 > 1 && type(a:1) == type([]) ? a:2 : b:git_dir + let tree = s:Tree(dir) + let pre = '' + if empty(tree) + let args = ['--git-dir=' . dir] . args + elseif s:cpath(tree) !=# s:cpath(getcwd()) + if fugitive#GitVersion() =~# '^[01]\.' + let pre = 'cd ' . s:shellesc(tree) . (s:winshell() ? ' & ' : '; ') + else + let args = ['-C', tree] + args + endif + endif + return s:sub(s:System(pre . g:fugitive_git_executable . ' ' . + \ join(map(args, 's:shellesc(v:val)'))), '\n$', '') +endfunction + +function! fugitive#RevParse(rev, ...) abort + let hash = system(s:Prepare(a:0 ? a:1 : b:git_dir, 'rev-parse', '--verify', a:rev))[0:-2] + if !v:shell_error && hash =~# '^\x\{40\}$' + return hash + endif + call s:throw('rev-parse '.a:rev.': '.hash) +endfunction + +function! fugitive#Config(name, ...) abort + let cmd = s:Prepare(a:0 ? a:1 : get(b:, 'git_dir', ''), 'config', '--get', a:name) + let out = matchstr(system(cmd), "[^\r\n]*") + return v:shell_error ? '' : out +endfunction + +function! s:Remote(dir) abort + let head = FugitiveHead(0, a:dir) + let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : '' + let i = 10 + while remote ==# '.' && i > 0 + let head = matchstr(fugitive#Config('branch.' . head . '.merge'), 'refs/heads/\zs.*') + let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : '' + let i -= 1 + endwhile + return remote =~# '\.\=$' ? 'origin' : remote +endfunction + +function! fugitive#RemoteUrl(...) abort + let dir = a:0 > 1 ? a:2 : get(b:, 'git_dir', '') + let remote = !a:0 || a:1 =~# '^\.\=$' ? s:Remote(dir) : a:1 + if fugitive#GitVersion() =~# '^[01]\.\|^2\.[0-6]\.' + return fugitive#Config('remote.' . remote . '.url') + endif + let cmd = s:Prepare(dir, 'remote', 'get-url', remote) + let out = substitute(system(cmd), "\n$", '', '') + return v:shell_error ? '' : out +endfunction + function! s:recall() abort let rev = s:sub(s:buffer().rev(), '^/', '') if rev ==# ':' @@ -152,6 +217,10 @@ function! s:map(mode, lhs, rhs, ...) abort endwhile if flags !~# '' || empty(mapcheck(head.tail, a:mode)) exe a:mode.'map ' flags head.tail a:rhs + if a:0 > 1 + let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . + \ '|sil! exe "' . a:mode . 'unmap ' . head.tail . '"' + endif endif endfunction @@ -174,60 +243,12 @@ endfunction let s:abstract_prototype = {} -" Section: Initialization - -function! fugitive#Init() abort - if exists('#User#FugitiveBoot') - try - let [save_mls, &modelines] = [&mls, 0] - doautocmd User FugitiveBoot - finally - let &mls = save_mls - endtry - endif - if !exists('g:fugitive_no_maps') - call s:map('c', '', 'fnameescape(recall())', '') - call s:map('n', 'y', ':call setreg(v:register, recall())', '') - endif - let buffer = fugitive#buffer() - if expand('%:p') =~# ':[\/][\/]' - call buffer.setvar('&path', s:sub(buffer.getvar('&path'), '^\.%(,|$)', '')) - endif - if stridx(buffer.getvar('&tags'), escape(b:git_dir, ', ')) == -1 - if filereadable(b:git_dir.'/tags') - call buffer.setvar('&tags', escape(b:git_dir.'/tags', ', ').','.buffer.getvar('&tags')) - endif - if &filetype !=# '' && filereadable(b:git_dir.'/'.&filetype.'.tags') - call buffer.setvar('&tags', escape(b:git_dir.'/'.&filetype.'.tags', ', ').','.buffer.getvar('&tags')) - endif - endif - try - let [save_mls, &modelines] = [&mls, 0] - call s:define_commands() - doautocmd User Fugitive - finally - let &mls = save_mls - endtry -endfunction - -function! fugitive#is_git_dir(path) abort - return FugitiveIsGitDir(a:path) -endfunction - -function! fugitive#extract_git_dir(path) abort - return FugitiveExtractGitDir(a:path) -endfunction - -function! fugitive#detect(path) abort - return FugitiveDetect(a:path) -endfunction - " Section: Repository let s:repo_prototype = {} let s:repos = {} -function! s:repo(...) abort +function! fugitive#repo(...) abort let dir = a:0 ? a:1 : (exists('b:git_dir') && b:git_dir !=# '' ? b:git_dir : FugitiveExtractGitDir(expand('%:p'))) if dir !=# '' if has_key(s:repos, dir) @@ -241,16 +262,12 @@ function! s:repo(...) abort call s:throw('not a git repository: '.expand('%:p')) endfunction -function! fugitive#repo(...) abort - return call('s:repo', a:000) -endfunction - function! s:repo_dir(...) dict abort return join([self.git_dir]+a:000,'/') endfunction function! s:repo_tree(...) dict abort - let dir = FugitiveTreeForGitDir(self.git_dir) + let dir = s:Tree(self.git_dir) if dir ==# '' call s:throw('no work tree') else @@ -262,61 +279,79 @@ function! s:repo_bare() dict abort if self.dir() =~# '/\.git$' return 0 else - return FugitiveTreeForGitDir(self.git_dir) ==# '' + return s:Tree(self.git_dir) ==# '' endif endfunction -function! s:repo_translate(spec) dict abort - if a:spec ==# '.' || a:spec ==# '/.' - return self.bare() ? self.dir() : self.tree() - elseif a:spec =~# '^/\=\.git$' && self.bare() - return self.dir() - elseif a:spec =~# '^/\=\.git/' - return self.dir(s:sub(a:spec, '^/=\.git/', '')) - elseif a:spec =~# '^/' - return self.tree().a:spec - elseif a:spec =~# '^:[0-3]:' - return 'fugitive://'.self.dir().'//'.a:spec[1].'/'.a:spec[3:-1] - elseif a:spec ==# ':' - if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(self.dir())] ==# self.dir('') && filereadable($GIT_INDEX_FILE) - return fnamemodify($GIT_INDEX_FILE,':p') +function! s:repo_translate(spec, ...) dict abort + let rev = a:spec + let dir = self.git_dir + let tree = s:Tree(dir) + if rev ==# '.' + let f = empty(tree) ? dir : tree + elseif rev =~# '^/\=\.git$' && empty(tree) + let f = dir + elseif rev =~# '^/\=\.git/' + let f = dir . s:sub(rev, '^/=\.git', '') + elseif empty(rev) || rev ==# '/.' + return self.tree() + elseif rev =~# '^\.\=/' + let f = self.tree(substitute(rev, '^\.', '', '')) + elseif rev =~# '^:[0-3]:/\@!' + let f = 'fugitive://' . dir . '//' . rev[1] . '/' . rev[3:-1] + elseif rev ==# ':' + if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && s:cpath(fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(dir)]) ==# s:cpath(dir . '/') && filereadable($GIT_INDEX_FILE) + let f = fnamemodify($GIT_INDEX_FILE, ':p') else - return self.dir('index') + let f = dir . '/index' endif - elseif a:spec =~# '^:/' - let ref = self.rev_parse(matchstr(a:spec,'.[^:]*')) - return 'fugitive://'.self.dir().'//'.ref - elseif a:spec =~# '^:' - return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1] + elseif rev =~# '^:/\@!' + let f = 'fugitive://' . dir . '//0/' . rev[1:-1] else - let refs = self.dir('refs/') - if filereadable(self.dir('commondir')) - let refs = simplify(self.dir(get(readfile(self.dir('commondir'), 1), 0, ''))) . '/refs/' + if rev =~# 'HEAD\|^refs/' && rev !~# ':' + let refs = dir . '/refs/' + if filereadable(dir . '/commondir') + let refs = simplify(dir . '/' . get(readfile(dir . '/commondir', 1), 0, '')) . '/refs/' + endif + if filereadable(refs . '../' . rev) + let f = simplify(refs . '../' . rev) + elseif filereadable(refs . rev) + let f = refs . rev + endif endif - if a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(refs . '../' . a:spec) - return simplify(refs . '../' . a:spec) - elseif filereadable(refs.a:spec) - return refs.a:spec + if !exists('f') + let commit = substitute(matchstr(rev,'^[^:]\+\|^:.*'), '^@\%($|[^~]\)\@=', 'HEAD', '') + let file = substitute(matchstr(rev, '^[^:]\+\zs:.*'), '^:', '/', '') + if commit !~# '^[0-9a-f]\{40\}$' + let commit = system(s:Prepare(dir, 'rev-parse', '--verify', commit))[0:-2] + let commit = v:shell_error ? '' : commit + endif + if len(commit) + let f = 'fugitive://' . dir . '//' . commit . file + else + let f = self.tree(rev) + endif endif - try - let ref = self.rev_parse(s:sub(matchstr(a:spec,'[^:]*'), '^\@%($|[^~])@=', 'HEAD')) - let path = s:sub(matchstr(a:spec,':.*'),'^:','/') - return 'fugitive://'.self.dir().'//'.ref.path - catch /^fugitive:/ - return self.tree(a:spec) - endtry endif + return a:0 && a:1 ? s:PlatformSlash(f) : f +endfunction + +function! s:Generate(rev) abort + return fugitive#repo().translate(a:rev, 1) endfunction function! s:repo_head(...) dict abort - let head = s:repo().head_ref() + if !filereadable(self.dir('HEAD')) + return '' + endif + let head = readfile(self.dir('HEAD'))[0] if head =~# '^ref: ' let branch = s:sub(head,'^ref: %(refs/%(heads/|remotes/|tags/)=)=','') elseif head =~# '^\x\{40\}$' " truncate hash to a:1 characters if we're in detached head mode let len = a:0 ? a:1 : 0 - let branch = len ? head[0:len-1] : '' + let branch = len < 0 ? head : len ? head[0:len-1] : '' else return '' endif @@ -327,119 +362,48 @@ endfunction call s:add_methods('repo',['dir','tree','bare','translate','head']) function! s:repo_git_command(...) dict abort - let git = s:git_command() . ' --git-dir='.s:shellesc(self.git_dir) + let git = s:UserCommand() . ' --git-dir='.s:shellesc(self.git_dir) return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'') endfunction function! s:repo_git_chomp(...) dict abort - return s:sub(system(call('fugitive#Prepare', [self.git_dir] + a:000)),'\n$','') + return s:sub(s:System(call('s:Prepare', [self.git_dir] + a:000)),'\n$','') endfunction function! s:repo_git_chomp_in_tree(...) dict abort let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' let dir = getcwd() try - execute cd s:fnameescape(s:repo().tree()) - return call(s:repo().git_chomp, a:000, s:repo()) + execute cd s:fnameescape(self.tree()) + return call(self.git_chomp, a:000, self) finally execute cd s:fnameescape(dir) endtry endfunction function! s:repo_rev_parse(rev) dict abort - let hash = self.git_chomp('rev-parse','--verify',a:rev) - if hash =~ '\<\x\{40\}$' - return matchstr(hash,'\<\x\{40\}$') - endif - call s:throw('rev-parse '.a:rev.': '.hash) + return fugitive#RevParse(a:rev, self.git_dir) endfunction call s:add_methods('repo',['git_command','git_chomp','git_chomp_in_tree','rev_parse']) -function! s:repo_dirglob(base) dict abort - let base = s:sub(a:base,'^/','') - let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*/')),"\n") - call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]') - return matches -endfunction - function! s:repo_superglob(base) dict abort - if a:base =~# '^/' || a:base !~# ':' - let results = [] - if a:base !~# '^/' - let heads = ["HEAD","ORIG_HEAD","FETCH_HEAD","MERGE_HEAD"] - let heads += sort(split(s:repo().git_chomp("rev-parse","--symbolic","--branches","--tags","--remotes"),"\n")) - " Add any stashes. - if filereadable(s:repo().dir('refs/stash')) - let heads += ["stash"] - let heads += sort(split(s:repo().git_chomp("stash","list","--pretty=format:%gd"),"\n")) - endif - call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') - let results += heads - endif - if !self.bare() - let base = s:sub(a:base,'^/','') - let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*')),"\n") - call map(matches,'s:shellslash(v:val)') - call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val') - call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]') - let results += matches - endif - return results - - elseif a:base =~# '^:' - let entries = split(self.git_chomp('ls-files','--stage'),"\n") - call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")') - if a:base !~# '^:[0-3]\%(:\|$\)' - call filter(entries,'v:val[1] == "0"') - call map(entries,'v:val[2:-1]') - endif - call filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') - return entries - - else - let tree = matchstr(a:base,'.*[:/]') - let entries = split(self.git_chomp('ls-tree',tree),"\n") - call map(entries,'s:sub(v:val,"^04.*\\zs$","/")') - call map(entries,'tree.s:sub(v:val,".*\t","")') - return filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') - endif + return map(fugitive#Complete(a:base, self.git_dir), 'substitute(v:val, ''\\\(.\)'', ''\1'', "g")') endfunction -call s:add_methods('repo',['dirglob','superglob']) +call s:add_methods('repo',['superglob']) -function! s:repo_config(conf) dict abort - return matchstr(s:repo().git_chomp('config',a:conf),"[^\r\n]*") -endfun +function! s:repo_config(name) dict abort + return fugitive#Config(a:name, self.git_dir) +endfunction function! s:repo_user() dict abort - let username = s:repo().config('user.name') - let useremail = s:repo().config('user.email') + let username = self.config('user.name') + let useremail = self.config('user.email') return username.' <'.useremail.'>' -endfun - -function! s:repo_aliases() dict abort - if !has_key(self,'_aliases') - let self._aliases = {} - for line in split(self.git_chomp('config','-z','--get-regexp','^alias[.]'),"\1") - let self._aliases[matchstr(line, '\.\zs.\{-}\ze\n')] = matchstr(line, '\n\zs.*') - endfor - endif - return self._aliases endfunction -call s:add_methods('repo',['config', 'user', 'aliases']) - -function! s:repo_keywordprg() dict abort - let args = ' --git-dir='.escape(self.dir(),"\\\"' ") - if has('gui_running') && !has('win32') - return s:git_command() . ' --no-pager' . args . ' log -1' - else - return s:git_command() . args . ' show' - endif -endfunction - -call s:add_methods('repo',['keywordprg']) +call s:add_methods('repo',['config', 'user']) " Section: Buffer @@ -456,25 +420,49 @@ function! s:DirRev(url) abort return [dir, (commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')] endfunction -function! fugitive#Path(url) abort +function! fugitive#Real(url) abort let [dir, commit, file] = s:DirCommitFile(a:url) if len(dir) - let tree = FugitiveTreeForGitDir(dir) + let tree = s:Tree(dir) return s:PlatformSlash((len(tree) ? tree : dir) . file) - elseif a:url =~# '^[\\/]\|^\a:[\\/]' - return s:PlatformSlash(a:url) + endif + let url = len(a:url) ? fnamemodify(a:url, ':p' . (a:url =~# '[\/]$' ? '' : ':s?[\/]$??')) : '' + if url =~# '^[\\/]\|^\a:[\\/]' + return s:PlatformSlash(url) endif return '' endfunction -function! fugitive#Real(url) abort - return fugitive#Path(a:url) +function! fugitive#Path(url, ...) abort + if !a:0 || empty(a:url) + return fugitive#Real(a:url) + endif + let url = s:shellslash(fnamemodify(a:url, ':p')) + if url =~# '/$' && s:shellslash(a:url) !~# '/$' + let url = url[0:-2] + endif + let dir = a:0 > 1 ? a:2 : get(b:, 'git_dir', '') + let tree = s:Tree(dir) + let [argdir, commit, file] = s:DirCommitFile(a:url) + if len(argdir) && s:cpath(argdir) !=# s:cpath(dir) + let file = '' + elseif len(dir) && s:cpath(url[0 : len(dir)]) ==# s:cpath(dir . '/') + let file = '/.git'.url[strlen(dir) : -1] + elseif len(tree) && s:cpath(url[0 : len(tree)]) ==# s:cpath(tree . '/') + let file = url[len(tree) : -1] + elseif s:cpath(url) ==# s:cpath(tree) + let file = '/' + endif + if empty(file) && a:1 =~# '^\%([.:]\=/\)\=$' + return s:shellslash(fugitive#Real(a:url)) + endif + return substitute(file, '^/', a:1, '') endfunction let s:trees = {} let s:indexes = {} function! s:TreeInfo(dir, commit) abort - let git = fugitive#Prepare(a:dir) + let git = s:Prepare(a:dir) if a:commit =~# '^:\=[0-3]$' let index = get(s:indexes, a:dir, []) let newftime = getftime(a:dir . '/index') @@ -539,7 +527,7 @@ function! fugitive#simplify(url) abort return '' endif if file =~# '/\.\.\%(/\|$\)' - let tree = FugitiveTreeForGitDir(dir) + let tree = s:Tree(dir) if len(tree) let path = simplify(tree . file) if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/' @@ -567,7 +555,7 @@ function! fugitive#getfsize(url) abort let entry = s:PathInfo(a:url) if entry[4] == -2 && entry[2] ==# 'blob' && len(entry[3]) let dir = s:DirCommitFile(a:url)[0] - let size = +system(fugitive#Prepare(dir, 'cat-file', '-s', entry[3])) + let size = +system(s:Prepare(dir, 'cat-file', '-s', entry[3])) let entry[4] = v:shell_error ? -1 : size endif return entry[4] @@ -581,39 +569,127 @@ function! fugitive#filereadable(url) abort return s:PathInfo(a:url)[2] ==# 'blob' endfunction +function! fugitive#filewritable(url) abort + let [dir, commit, file] = s:DirCommitFile(a:url) + if commit !~# '^\d$' || !filewritable(dir . '/index') + return 0 + endif + return s:PathInfo(a:url)[2] ==# 'blob' ? 1 : 2 +endfunction + function! fugitive#isdirectory(url) abort return s:PathInfo(a:url)[2] ==# 'tree' endfunction +function! fugitive#getfperm(url) abort + let [dir, commit, file] = s:DirCommitFile(a:url) + let perm = getfperm(dir) + let fperm = s:PathInfo(a:url)[1] + if fperm ==# '040000' + let fperm = '000755' + endif + if fperm !~# '[15]' + let perm = tr(perm, 'x', '-') + endif + if fperm !~# '[45]$' + let perm = tr(perm, 'rw', '--') + endif + if commit !~# '^\d$' + let perm = tr(perm, 'w', '-') + endif + return perm ==# '---------' ? '' : perm +endfunction + +function! fugitive#setfperm(url, perm) abort + let [dir, commit, file] = s:DirCommitFile(a:url) + let entry = s:PathInfo(a:url) + let perm = fugitive#getfperm(a:url) + if commit !~# '^\d$' || entry[2] !=# 'blob' || + \ substitute(perm, 'x', '-', 'g') !=# substitute(a:perm, 'x', '-', 'g') + return -2 + endif + call system(s:Prepare(dir, 'update-index', '--index-info'), + \ (a:perm =~# 'x' ? '000755 ' : '000644 ') . entry[3] . ' ' . commit . "\t" . file[1:-1]) + return v:shell_error ? -1 : 0 +endfunction + +function! s:TempCmd(out, cmd) abort + let prefix = '' + try + let cmd = (type(a:cmd) == type([]) ? call('s:Prepare', a:cmd) : a:cmd) + let redir = ' > ' . a:out + if s:winshell() + let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^' + return s:System('cmd /c "' . prefix . s:gsub(cmd, '[<>]', cmd_escape_char . '&') . redir . '"') + elseif &shell =~# 'fish' + return s:System(' begin;' . prefix . cmd . redir . ';end ') + else + return s:System(' (' . prefix . cmd . redir . ') ') + endif + endtry +endfunction + +if !exists('s:blobdirs') + let s:blobdirs = {} +endif +function! s:BlobTemp(url) abort + let [dir, commit, file] = s:DirCommitFile(a:url) + if empty(file) + return '' + endif + if !has_key(s:blobdirs, dir) + let s:blobdirs[dir] = s:tempname() + endif + let tempfile = s:PlatformSlash(s:blobdirs[dir] . '/' . commit . file) + let tempparent = fnamemodify(tempfile, ':h') + if !isdirectory(tempparent) + call mkdir(tempparent, 'p') + endif + if commit =~# '^\d$' || !filereadable(tempfile) + let rev = s:DirRev(a:url)[1] + let command = s:Prepare(dir, 'cat-file', 'blob', rev) + call s:TempCmd(tempfile, command) + if v:shell_error + call delete(tempfile) + return '' + endif + endif + return tempfile +endfunction + function! fugitive#readfile(url, ...) abort - let bin = a:0 && a:1 ==# 'b' - let max = a:0 > 1 ? a:2 : 'all' let entry = s:PathInfo(a:url) if entry[2] !=# 'blob' return [] endif - let [dir, rev] = s:DirRev(a:url) - let cmd = fugitive#Prepare(dir, 'cat-file', 'blob', rev) - if max > 0 && s:executable('head') - let cmd .= '|head -' . max + let temp = s:BlobTemp(a:url) + if empty(temp) + return [] endif - if exists('systemlist') && !bin - let lines = systemlist(cmd) - else - let lines = split(system(cmd), "\n", 1) - if !bin && empty(lines[-1]) - call remove(lines, -1) + return call('readfile', [temp] + a:000) +endfunction + +function! fugitive#writefile(lines, url, ...) abort + let url = type(a:url) ==# type('') ? a:url : '' + let [dir, commit, file] = s:DirCommitFile(url) + let entry = s:PathInfo(url) + if commit =~# '^\d$' && entry[2] !=# 'tree' + let temp = s:tempname() + if a:0 && a:1 =~# 'a' && entry[2] ==# 'blob' + call writefile(fugitive#readfile(url, 'b'), temp, 'b') + endif + call call('writefile', [a:lines, temp] + a:000) + let hash = system(s:Prepare(dir, 'hash-object', '-w', temp))[0:-2] + let mode = len(entry[1]) ? entry[1] : '100644' + if !v:shell_error && hash =~# '^\x\{40\}$' + call system(s:Prepare(dir, 'update-index', '--index-info'), + \ mode . ' ' . hash . ' ' . commit . "\t" . file[1:-1]) + if !v:shell_error + return 0 + endif endif endif - if v:shell_error || max is# 0 - return [] - elseif max > 0 && max < len(lines) - return lines[0 : max - 1] - elseif max < 0 - return lines[max : -1] - else - return lines - endif + return call('writefile', [a:lines, a:url] + a:000) endfunction let s:globsubs = {'*': '[^/]*', '**': '.*', '**/': '\%(.*/\)\=', '?': '[^/]'} @@ -645,9 +721,23 @@ function! fugitive#glob(url, ...) abort endif endfunction +function! fugitive#delete(url, ...) abort + let [dir, commit, file] = s:DirCommitFile(a:url) + if a:0 && len(a:1) || commit !~# '^\d$' + return -1 + endif + let entry = s:PathInfo(a:url) + if entry[2] !=# 'blob' + return -1 + endif + call system(s:Prepare(dir, 'update-index', '--index-info'), + \ '000000 0000000000000000000000000000000000000000 ' . commit . "\t" . file[1:-1]) + return v:shell_error ? -1 : 0 +endfunction + let s:buffer_prototype = {} -function! s:buffer(...) abort +function! fugitive#buffer(...) abort let buffer = {'#': bufnr(a:0 ? a:1 : '%')} call extend(extend(buffer,s:buffer_prototype,'keep'),s:abstract_prototype,'keep') if buffer.getvar('git_dir') !=# '' @@ -656,24 +746,16 @@ function! s:buffer(...) abort call s:throw('not a git repository: '.bufname(buffer['#'])) endfunction -function! fugitive#buffer(...) abort - return s:buffer(a:0 ? a:1 : '%') -endfunction - function! s:buffer_getvar(var) dict abort return getbufvar(self['#'],a:var) endfunction -function! s:buffer_setvar(var,value) dict abort - return setbufvar(self['#'],a:var,a:value) -endfunction - function! s:buffer_getline(lnum) dict abort return get(getbufline(self['#'], a:lnum), 0, '') endfunction function! s:buffer_repo() dict abort - return s:repo(self.getvar('git_dir')) + return fugitive#repo(self.getvar('git_dir')) endfunction function! s:buffer_type(...) dict abort @@ -731,9 +813,9 @@ endfunction function! s:cpath(path) abort if exists('+fileignorecase') && &fileignorecase - return tolower(a:path) + return s:PlatformSlash(tolower(a:path)) else - return a:path + return s:PlatformSlash(a:path) endif endfunction @@ -752,6 +834,10 @@ function! s:buffer_relative(...) dict abort return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '') endfunction +function! s:Relative(prefix) abort + return fugitive#Path(@%, a:prefix) +endfunction + function! s:buffer_path(...) dict abort if a:0 return self.relative(a:1) @@ -774,14 +860,6 @@ function! s:buffer_rev() dict abort endif endfunction -function! s:buffer_sha1() dict abort - if self.spec() =~? '^fugitive:' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$' - return self.repo().rev_parse(self.rev()) - else - return '' - endif -endfunction - function! s:buffer_expand(rev) dict abort if a:rev =~# '^:[0-3]$' let file = a:rev.self.relative(':') @@ -802,109 +880,467 @@ function! s:buffer_expand(rev) dict abort \ '\.\@<=/$','') endfunction -function! s:buffer_containing_commit() dict abort - if self.commit() =~# '^\d$' - return ':' - elseif self.commit() =~# '.' - return self.commit() +call s:add_methods('buffer',['getvar','getline','repo','type','spec','name','commit','path','relative','rev','expand']) + +" Section: Completion + +function! fugitive#PathComplete(base, ...) abort + let tree = FugitiveWorkTree(a:0 == 1 ? a:1 : get(b:, 'git_dir', '')) . '/' + let strip = '^:\=/' + let base = substitute(a:base, strip, '', '') + let matches = split(glob(tree . s:gsub(base, '/', '*&').'*'), "\n") + call map(matches, 's:shellslash(v:val)') + call map(matches, 'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val') + call map(matches, 'matchstr(a:base, strip) . v:val[ strlen(tree) : -1 ]') + call map(matches, 's:fnameescape(v:val)') + return matches +endfunction + +function! fugitive#Complete(base, ...) abort + let dir = a:0 == 1 ? a:1 : get(b:, 'git_dir', '') + let tree = FugitiveWorkTree(a:0 == 1 ? a:1 : get(b:, 'git_dir', '')) . '/' + if a:base =~# '^\.\=/' || a:base !~# ':' + let results = [] + if a:base !~# '^\.\=/' + let heads = ["HEAD","ORIG_HEAD","FETCH_HEAD","MERGE_HEAD"] + let heads += sort(split(s:TreeChomp(["rev-parse","--symbolic","--branches","--tags","--remotes"], dir),"\n")) + if filereadable(dir . '/refs/stash') + let heads += ["stash"] + let heads += sort(split(s:TreeChomp(["stash","list","--pretty=format:%gd"], dir),"\n")) + endif + call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') + let results += heads + endif + call map(results, 's:fnameescape(v:val)') + if !empty(s:Tree(dir)) + let results += fugitive#PathComplete(a:base, dir) + endif + return results + + elseif a:base =~# '^:' + let entries = split(s:TreeChomp(['ls-files','--stage'], dir),"\n") + call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")') + if a:base !~# '^:[0-3]\%(:\|$\)' + call filter(entries,'v:val[1] == "0"') + call map(entries,'v:val[2:-1]') + endif + call filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') + return map(entries, 's:fnameescape(v:val)') + else - return 'HEAD' + let tree = matchstr(a:base,'.*[:/]') + let entries = split(s:TreeChomp(['ls-tree',tree], dir),"\n") + call map(entries,'s:sub(v:val,"^04.*\\zs$","/")') + call map(entries,'tree.s:sub(v:val,".*\t","")') + call filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') + return map(entries, 's:fnameescape(v:val)') endif endfunction -function! s:buffer_up(...) dict abort - let rev = self.rev() - let c = a:0 ? a:1 : 1 - while c - if rev =~# '^[/:]$' - let rev = 'HEAD' - elseif rev =~# '^:' - let rev = ':' - elseif rev =~# '^refs/[^^~:]*$\|^[^^~:]*HEAD$' - let rev .= '^{}' - elseif rev =~# '^/\|:.*/' - let rev = s:sub(rev, '.*\zs/.*', '') - elseif rev =~# ':.' - let rev = matchstr(rev, '^[^:]*:') - elseif rev =~# ':$' - let rev = rev[0:-2] - else - return rev.'~'.c +" Section: File access + +function! s:ReplaceCmd(cmd) abort + let tmp = tempname() + let err = s:TempCmd(tmp, a:cmd) + if v:shell_error + call s:throw((len(err) ? err : filereadable(tmp) ? join(readfile(tmp), ' ') : 'unknown error running ' . a:cmd)) + endif + let fn = expand('%:p') + silent exe 'doau BufReadPre '.s:fnameescape(fn) + silent exe 'keepalt file '.tmp + try + silent noautocmd edit! + finally + try + silent exe 'keepalt file '.s:fnameescape(fn) + catch /^Vim\%((\a\+)\)\=:E302:/ + endtry + call delete(tmp) + if fnamemodify(bufname('$'), ':p') ==# tmp + silent execute 'bwipeout '.bufnr('$') endif - let c -= 1 - endwhile - return rev + silent exe 'doau BufReadPost '.s:fnameescape(fn) + endtry endfunction -call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','relative','rev','sha1','expand','containing_commit','up']) +function! fugitive#BufReadStatus() abort + let amatch = s:shellslash(expand('%:p')) + if !exists('b:fugitive_display_format') + let b:fugitive_display_format = filereadable(expand('%').'.lock') + endif + let b:fugitive_display_format = b:fugitive_display_format % 2 + let b:fugitive_type = 'index' + try + let dir = fnamemodify(amatch, ':h') + setlocal noro ma nomodeline + let prefix = '' + if s:cpath(fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : b:git_dir . '/index', ':p')) !=# s:cpath(amatch) + if s:winshell() + let old_index = $GIT_INDEX_FILE + else + let prefix = 'env GIT_INDEX_FILE='.s:shellesc(amatch).' ' + endif + endif + if b:fugitive_display_format + let cmd = ['ls-files', '--stage'] + elseif fugitive#GitVersion() =~# '^0\|^1\.[1-7]\.' + let cmd = ['status'] + else + let cmd = [ + \ '-c', 'status.displayCommentPrefix=true', + \ '-c', 'color.status=false', + \ '-c', 'status.short=false', + \ 'status'] + endif + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' + let cwd = getcwd() + let cmd_str = prefix . call('s:Prepare', [dir] + cmd) + try + if exists('old_index') + let $GIT_INDEX_FILE = amatch + endif + execute cd s:fnameescape(s:Tree(dir)) + call s:ReplaceCmd(cmd_str) + finally + if exists('old_index') + let $GIT_INDEX_FILE = old_index + endif + execute cd s:fnameescape(cwd) + endtry + if b:fugitive_display_format + if &filetype !=# 'git' + set filetype=git + endif + set nospell + else + if &filetype !=# 'gitcommit' + set filetype=gitcommit + endif + set foldtext=fugitive#Foldtext() + endif + setlocal readonly nomodifiable nomodified noswapfile + if &bufhidden ==# '' + setlocal bufhidden=delete + endif + call fugitive#MapJumps() + nunmap P + nunmap ~ + nnoremap :execute StageNext(v:count1) + nnoremap :execute StagePrevious(v:count1) + nnoremap - :silent execute StageToggle(line('.'),line('.')+v:count1-1) + xnoremap - :silent execute StageToggle(line("'<"),line("'>")) + nnoremap a :let b:fugitive_display_format += 1exe fugitive#BufReadStatus() + nnoremap i :let b:fugitive_display_format -= 1exe fugitive#BufReadStatus() + nnoremap C :Gcommit:echohl WarningMsgecho ':Gstatus C is deprecated in favor of cc'echohl NONE + nnoremap cA :Gcommit --amend --reuse-message=HEAD:echohl WarningMsgecho ':Gstatus cA is deprecated in favor of ce'echohl NONE + nnoremap ca :Gcommit --amend + nnoremap cc :Gcommit + nnoremap ce :Gcommit --amend --no-edit + nnoremap cw :Gcommit --amend --only + nnoremap cva :Gcommit -v --amend + nnoremap cvc :Gcommit -v + nnoremap D :execute StageDiff('Gdiff') + nnoremap dd :execute StageDiff('Gdiff') + nnoremap dh :execute StageDiff('Gsdiff') + nnoremap ds :execute StageDiff('Gsdiff') + nnoremap dp :execute StageDiffEdit() + nnoremap dv :execute StageDiff('Gvdiff') + nnoremap p :execute StagePatch(line('.'),line('.')+v:count1-1) + xnoremap p :execute StagePatch(line("'<"),line("'>")) + nnoremap P :execute StagePatch(line('.'),line('.')+v:count1-1) + xnoremap P :execute StagePatch(line("'<"),line("'>")) + nnoremap q :if bufnr('$') == 1quitelsebdeleteendif + nnoremap r :edit + nnoremap R :edit + nnoremap U :execute StageUndo() + nnoremap g? :help fugitive-:Gstatus + nnoremap :help fugitive-:Gstatus + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +function! fugitive#FileReadCmd(...) abort + let amatch = a:0 ? a:1 : expand('') + let [dir, rev] = s:DirRev(amatch) + let line = a:0 > 1 ? a:2 : line("'[") + if empty(dir) + return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch) + endif + if rev !~# ':' + let cmd = s:Prepare(dir, 'log', '--pretty=format:%B', '-1', rev) + else + let cmd = s:Prepare(dir, 'cat-file', '-p', rev) + endif + return line . 'read !' . escape(cmd, '!#%') +endfunction + +function! fugitive#FileWriteCmd(...) abort + let tmp = tempname() + let amatch = a:0 ? a:1 : expand('') + let autype = a:0 > 1 ? 'Buf' : 'File' + if exists('#' . autype . 'WritePre') + execute 'doautocmd ' . autype . 'WritePre ' . s:fnameescape(amatch) + endif + try + let [dir, commit, file] = s:DirCommitFile(amatch) + if commit !~# '^[0-3]$' || !v:cmdbang && (line("'[") != 1 || line("']") != line('$')) + return "noautocmd '[,']write" . (v:cmdbang ? '!' : '') . ' ' . s:fnameescape(amatch) + endif + silent execute "'[,']write !".s:Prepare(dir, 'hash-object', '-w', '--stdin').' > '.tmp + let sha1 = readfile(tmp)[0] + let old_mode = matchstr(system(s:Prepare(dir, 'ls-files', '--stage', file[1:-1])), '^\d\+') + if old_mode == '' + let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644' + endif + let info = old_mode.' '.sha1.' '.commit."\t".file[1:-1] + call writefile([info],tmp) + if s:winshell() + let error = s:System('type '.s:gsub(tmp,'/','\\').'|'.s:Prepare(dir, 'update-index', '--index-info')) + else + let error = s:System(s:Prepare(dir, 'update-index', '--index-info').' < '.tmp) + endif + if v:shell_error == 0 + setlocal nomodified + if exists('#' . autype . 'WritePost') + execute 'doautocmd ' . autype . 'WritePost ' . s:fnameescape(amatch) + endif + call fugitive#ReloadStatus() + return '' + else + return 'echoerr '.string('fugitive: '.error) + endif + finally + call delete(tmp) + endtry +endfunction + +function! fugitive#BufReadCmd(...) abort + let amatch = a:0 ? a:1 : expand('') + try + let [dir, rev] = s:DirRev(amatch) + if empty(dir) + return 'echo "Invalid Fugitive URL"' + endif + if rev =~# '^:\d$' + let b:fugitive_type = 'stage' + else + let b:fugitive_type = system(s:Prepare(dir, 'cat-file', '-t', rev))[0:-2] + if v:shell_error && rev =~# '^:0' + let sha = system(s:Prepare(dir, 'write-tree', '--prefix=' . rev[3:-1]))[0:-2] + let b:fugitive_type = 'tree' + endif + if v:shell_error + unlet b:fugitive_type + if rev =~# '^:\d:' + let &readonly = !filewritable(dir . '/index') + return 'silent doautocmd BufNewFile '.s:fnameescape(amatch) + else + setlocal readonly nomodifiable + return '' + endif + elseif b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$' + return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'") + endif + if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob' + let b:fugitive_display_format = +getbufvar('#','fugitive_display_format') + endif + endif + + if b:fugitive_type !=# 'blob' + setlocal nomodeline + endif + + setlocal noreadonly modifiable + let pos = getpos('.') + silent keepjumps %delete_ + setlocal endofline + + try + if b:fugitive_type ==# 'tree' + let b:fugitive_display_format = b:fugitive_display_format % 2 + if b:fugitive_display_format + call s:ReplaceCmd([dir, 'ls-tree', exists('sha') ? sha : rev]) + else + if !exists('sha') + let sha = system(s:Prepare(dir, 'rev-parse', '--verify', rev))[0:-2] + endif + call s:ReplaceCmd([dir, 'show', '--no-color', sha]) + endif + elseif b:fugitive_type ==# 'tag' + let b:fugitive_display_format = b:fugitive_display_format % 2 + if b:fugitive_display_format + call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev]) + else + call s:ReplaceCmd([dir, 'cat-file', '-p', rev]) + endif + elseif b:fugitive_type ==# 'commit' + let b:fugitive_display_format = b:fugitive_display_format % 2 + if b:fugitive_display_format + call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev]) + else + call s:ReplaceCmd([dir, 'show', '--no-color', '--pretty=format:tree%x20%T%nparent%x20%P%nauthor%x20%an%x20<%ae>%x20%ad%ncommitter%x20%cn%x20<%ce>%x20%cd%nencoding%x20%e%n%n%s%n%n%b', rev]) + keepjumps call search('^parent ') + if getline('.') ==# 'parent ' + silent keepjumps delete_ + else + silent exe 'keepjumps s/\m\C\%(^parent\)\@\)\=$','W',line('.')+3) + if lnum + silent keepjumps delete_ + end + silent keepjumps 1,/^diff --git\|\%$/g/\r$/s/// + keepjumps 1 + endif + elseif b:fugitive_type ==# 'stage' + call s:ReplaceCmd([dir, 'ls-files', '--stage']) + elseif b:fugitive_type ==# 'blob' + call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev]) + setlocal nomodeline + endif + finally + keepjumps call setpos('.',pos) + setlocal nomodified noswapfile + if rev !~# '^:.:' + setlocal nomodifiable + else + let &modifiable = b:fugitive_type !=# 'tree' + endif + let &readonly = !&modifiable || !filewritable(dir . '/index') + if &bufhidden ==# '' + setlocal bufhidden=delete + endif + if b:fugitive_type !=# 'blob' + setlocal filetype=git foldmethod=syntax + nnoremap a :let b:fugitive_display_format += v:count1exe fugitive#BufReadCmd(@%) + nnoremap i :let b:fugitive_display_format -= v:count1exe fugitive#BufReadCmd(@%) + else + call fugitive#MapJumps() + endif + endtry + + return '' + catch /^fugitive:/ + return 'echoerr v:errmsg' + endtry +endfunction + +function! fugitive#BufWriteCmd(...) abort + return fugitive#FileWriteCmd(a:0 ? a:1 : expand(''), 1) +endfunction + +function! fugitive#SourceCmd(...) abort + let amatch = a:0 ? a:1 : expand('') + let temp = s:BlobTemp(amatch) + if empty(temp) + return 'noautocmd source ' . s:fnameescape(amatch) + endif + if !exists('g:virtual_scriptnames') + let g:virtual_scriptnames = {} + endif + let g:virtual_scriptnames[temp] = amatch + return 'source ' . s:fnameescape(temp) +endfunction + +" Section: Temp files + +if !exists('s:temp_files') + let s:temp_files = {} +endif + +augroup fugitive_temp + autocmd! + autocmd BufNewFile,BufReadPost * + \ if has_key(s:temp_files,s:cpath(expand(':p'))) | + \ let b:git_dir = s:temp_files[s:cpath(expand(':p'))].dir | + \ call extend(b:, {'fugitive_type': 'temp'}, 'keep') | + \ call FugitiveDetect(expand(':p')) | + \ setlocal bufhidden=delete nobuflisted | + \ nnoremap q :bdelete| + \ endif +augroup END " Section: Git call s:command("-bang -nargs=? -complete=customlist,s:GitComplete Git :execute s:Git(0,'',)") -function! s:ExecuteInTree(cmd) abort - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' - let dir = getcwd() - try - execute cd s:fnameescape(s:repo().tree()) - execute a:cmd - finally - execute cd s:fnameescape(dir) - endtry -endfunction - function! s:Git(bang, mods, args) abort if a:bang return s:Edit('edit', 1, a:mods, a:args) endif - let git = s:git_command() + let git = s:UserCommand() if has('gui_running') && !has('win32') let git .= ' --no-pager' endif let args = matchstr(a:args,'\v\C.{-}%($|\\@' s:fnameescape(s:repo().bare() ? s:repo().dir() : s:repo().tree())") -call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :exe 'lcd' s:fnameescape(s:repo().bare() ? s:repo().dir() : s:repo().tree())") +call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Gcd :exe 'cd' s:fnameescape((empty(s:Tree()) ? b:git_dir : s:Tree()) . '/' . ") +call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :exe 'lcd' s:fnameescape((empty(s:Tree()) ? b:git_dir : s:Tree()) . '/' . ") " Section: Gstatus @@ -1001,7 +1437,7 @@ endfunction function! s:StagePrevious(count) abort if line('.') == 1 && exists(':CtrlP') && get(g:, 'ctrl_p_map') =~? '^$' - return 'CtrlP '.fnameescape(s:repo().tree()) + return 'CtrlP '.fnameescape(s:Tree()) else for i in range(a:count) call search('^.\=\t.*','Wbe') @@ -1027,17 +1463,16 @@ function! s:StageUndo() abort if empty(filename) return '' endif - let repo = s:repo() - let hash = repo.git_chomp('hash-object', '-w', filename) + let hash = s:TreeChomp('hash-object', '-w', filename) if !empty(hash) if section ==# 'untracked' - call repo.git_chomp_in_tree('clean', '-f', '--', filename) + call s:TreeChomp('clean', '-f', '--', filename) elseif section ==# 'unmerged' - call repo.git_chomp_in_tree('rm', '--', filename) + call s:TreeChomp('rm', '--', filename) elseif section ==# 'unstaged' - call repo.git_chomp_in_tree('checkout', '--', filename) + call s:TreeChomp('checkout', '--', filename) else - call repo.git_chomp_in_tree('checkout', 'HEAD', '--', filename) + call s:TreeChomp('checkout', 'HEAD', '--', filename) endif call s:StageReloadSeek(filename, line('.'), line('.')) let @" = hash @@ -1071,8 +1506,7 @@ function! s:StageDiffEdit() abort if section ==# 'staged' return 'Git! diff --no-ext-diff --cached '.s:shellesc(arg) elseif section ==# 'untracked' - let repo = s:repo() - call repo.git_chomp_in_tree('add','--intent-to-add',arg) + call s:TreeChomp('add','--intent-to-add',arg) if arg ==# '.' silent! edit! 1 @@ -1096,10 +1530,9 @@ function! s:StageToggle(lnum1,lnum2) abort let output = '' for lnum in range(a:lnum1,a:lnum2) let [filename, section] = s:stage_info(lnum) - let repo = s:repo() if getline('.') =~# ':$' if section ==# 'staged' - call repo.git_chomp_in_tree('reset','-q') + call s:TreeChomp('reset','-q') silent! edit! 1 if !search('^.*:\n.\= .*"git add .*\n#\n\|^\%(. \)\=Untracked files:$','W') @@ -1107,7 +1540,7 @@ function! s:StageToggle(lnum1,lnum2) abort endif return '' elseif section ==# 'unstaged' - call repo.git_chomp_in_tree('add','-u') + call s:TreeChomp('add','-u') silent! edit! 1 if !search('^.*:\n\.\= .*"git add .*\n#\n\|^\%( \)=Untracked files:$','W') @@ -1115,7 +1548,7 @@ function! s:StageToggle(lnum1,lnum2) abort endif return '' else - call repo.git_chomp_in_tree('add','.') + call s:TreeChomp('add','.') silent! edit! 1 call search(':$','W') @@ -1144,7 +1577,7 @@ function! s:StageToggle(lnum1,lnum2) abort if !exists('first_filename') let first_filename = filename endif - let output .= call(repo.git_chomp_in_tree,cmd,s:repo())."\n" + let output .= call('s:TreeChomp', cmd)."\n" endfor if exists('first_filename') call s:StageReloadSeek(first_filename,a:lnum1,a:lnum2) @@ -1208,10 +1641,11 @@ call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit :execute function! s:Commit(mods, args, ...) abort let mods = s:gsub(a:mods ==# '' ? '' : a:mods, '', '-tab') - let repo = a:0 ? a:1 : s:repo() + let dir = a:0 ? a:1 : b:git_dir + let tree = s:Tree(dir) let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' - let dir = getcwd() - let msgfile = repo.dir('COMMIT_EDITMSG') + let cwd = getcwd() + let msgfile = dir . '/COMMIT_EDITMSG' let outfile = tempname() let errorfile = tempname() try @@ -1220,7 +1654,7 @@ function! s:Commit(mods, args, ...) abort if &guioptions =~# '!' setglobal guioptions-=! endif - execute cd s:fnameescape(repo.tree()) + execute cd s:fnameescape(tree) if s:winshell() let command = '' let old_editor = $GIT_EDITOR @@ -1228,7 +1662,7 @@ function! s:Commit(mods, args, ...) abort else let command = 'env GIT_EDITOR=false ' endif - let command .= repo.git_command('commit').' '.a:args + let command .= s:UserCommand() . ' commit ' . a:args if &shell =~# 'csh' noautocmd silent execute '!('.command.' > '.outfile.') >& '.errorfile elseif a:args =~# '\%(^\| \)-\%(-interactive\|p\|-patch\)\>' @@ -1238,7 +1672,7 @@ function! s:Commit(mods, args, ...) abort endif let error = v:shell_error finally - execute cd s:fnameescape(dir) + execute cd s:fnameescape(cwd) let &guioptions = guioptions endtry if !has('gui_running') @@ -1261,7 +1695,7 @@ function! s:Commit(mods, args, ...) abort let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' let cwd = getcwd() try - exe cd s:fnameescape(repo.tree()) + exe cd s:fnameescape(tree) let args = s:gsub(args,'\\@' execute mods 'keepalt -tabedit' s:fnameescape(msgfile) - elseif s:buffer().type() ==# 'index' + elseif get(b:, 'fugitive_type', '') ==# 'index' execute mods 'keepalt edit' s:fnameescape(msgfile) execute (search('^#','n')+1).'wincmd+' setlocal nopreviewwindow @@ -1304,19 +1738,26 @@ function! s:Commit(mods, args, ...) abort endfunction function! s:CommitComplete(A,L,P) abort - if a:A =~ '^-' || type(a:A) == type(0) " a:A is 0 on :Gcommit - + if a:A =~# '^--fixup=\|^--squash=' + let commits = split(s:TreeChomp('log', '--pretty=format:%s', '@{upstream}..'), "\n") + if !v:shell_error + let pre = matchstr(a:A, '^--\w*=') . ':/^' + return map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")') + endif + elseif a:A =~ '^-' || type(a:A) == type(0) " a:A is 0 on :Gcommit - let args = ['-C', '-F', '-a', '-c', '-e', '-i', '-m', '-n', '-o', '-q', '-s', '-t', '-u', '-v', '--all', '--allow-empty', '--amend', '--author=', '--cleanup=', '--dry-run', '--edit', '--file=', '--fixup=', '--include', '--interactive', '--message=', '--no-verify', '--only', '--quiet', '--reedit-message=', '--reuse-message=', '--signoff', '--squash=', '--template=', '--untracked-files', '--verbose'] return filter(args,'v:val[0 : strlen(a:A)-1] ==# a:A') else - return s:repo().superglob(a:A) + return fugitive#PathComplete(a:A) endif + return [] endfunction function! s:FinishCommit() abort let args = getbufvar(+expand(''),'fugitive_commit_arguments') if !empty(args) call setbufvar(+expand(''),'fugitive_commit_arguments','') - return s:Commit('', args, s:repo(getbufvar(+expand(''),'git_dir'))) + return s:Commit('', args, getbufvar(+expand(''),'git_dir')) endif return '' endfunction @@ -1331,18 +1772,18 @@ call s:command("-nargs=? -bang -complete=custom,s:RemoteComplete Gpull " . \ "execute s:Merge('pull --progress', 0, )") function! s:RevisionComplete(A, L, P) abort - return s:repo().git_chomp('rev-parse', '--symbolic', '--branches', '--tags', '--remotes') + return s:TreeChomp('rev-parse', '--symbolic', '--branches', '--tags', '--remotes') \ . "\nHEAD\nFETCH_HEAD\nORIG_HEAD" endfunction function! s:RemoteComplete(A, L, P) abort let remote = matchstr(a:L, ' \zs\S\+\ze ') if !empty(remote) - let matches = split(s:repo().git_chomp('ls-remote', remote), "\n") + let matches = split(s:TreeChomp('ls-remote', remote), "\n") call filter(matches, 'v:val =~# "\t" && v:val !~# "{"') call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")') else - let matches = split(s:repo().git_chomp('remote'), "\n") + let matches = split(s:TreeChomp('remote'), "\n") endif return join(matches, "\n") endfunction @@ -1373,7 +1814,7 @@ function! s:Merge(cmd, bang, args) abort let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' let cwd = getcwd() let [mp, efm] = [&l:mp, &l:efm] - let had_merge_msg = filereadable(s:repo().dir('MERGE_MSG')) + let had_merge_msg = filereadable(b:git_dir . '/MERGE_MSG') try let &l:errorformat = '' \ . '%-Gerror:%.%#false''.,' @@ -1396,11 +1837,11 @@ function! s:Merge(cmd, bang, args) abort \ . "%+E\u51b2\u7a81 %.%#," \ . 'U%\t%f' if a:cmd =~# '^merge' && empty(a:args) && - \ (had_merge_msg || isdirectory(s:repo().dir('rebase-apply')) || - \ !empty(s:repo().git_chomp('diff-files', '--diff-filter=U'))) + \ (had_merge_msg || isdirectory(b:git_dir . '/rebase-apply') || + \ !empty(s:TreeChomp('diff-files', '--diff-filter=U'))) let &l:makeprg = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U' else - let &l:makeprg = s:sub(s:git_command() . ' ' . a:cmd . + let &l:makeprg = s:sub(s:UserCommand() . ' ' . a:cmd . \ (a:args =~# ' \%(--no-edit\|--abort\|-m\)\>' || a:cmd =~# '^rebase' ? '' : ' --edit') . \ ' ' . a:args, ' *$', '') endif @@ -1410,7 +1851,7 @@ function! s:Merge(cmd, bang, args) abort else let &l:makeprg = 'env GIT_EDITOR=false ' . &l:makeprg endif - execute cd fnameescape(s:repo().tree()) + execute cd fnameescape(s:Tree()) silent noautocmd make! catch /^Vim\%((\a\+)\)\=:E211/ let err = v:exception @@ -1424,9 +1865,9 @@ function! s:Merge(cmd, bang, args) abort endtry call fugitive#ReloadStatus() if empty(filter(getqflist(),'v:val.valid')) - if !had_merge_msg && filereadable(s:repo().dir('MERGE_MSG')) + if !had_merge_msg && filereadable(b:git_dir . '/MERGE_MSG') cclose - return 'Gcommit --no-status -n -t '.s:shellesc(s:repo().dir('MERGE_MSG')) + return 'Gcommit --no-status -n -t '.s:shellesc(b:git_dir . '/MERGE_MSG') endif endif let qflist = getqflist() @@ -1453,10 +1894,18 @@ if !exists('g:fugitive_summary_format') let g:fugitive_summary_format = '%s' endif -call s:command("-bang -nargs=? -complete=customlist,s:EditComplete Ggrep :execute s:Grep('grep',0,)") -call s:command("-bang -nargs=? -complete=customlist,s:EditComplete Glgrep :execute s:Grep('lgrep',0,)") -call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditComplete Glog :call s:Log('grep',,,)") -call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditComplete Gllog :call s:Log('lgrep',,,)") +function! s:GrepComplete(A, L, P) abort + if strpart(a:L, 0, a:P) =~# ' -- ' + return fugitive#PathComplete(a:A) + else + return fugitive#Complete(a:A) + endif +endfunction + +call s:command("-bang -nargs=? -complete=customlist,s:GrepComplete Ggrep :execute s:Grep('grep',0,)") +call s:command("-bang -nargs=? -complete=customlist,s:GrepComplete Glgrep :execute s:Grep('lgrep',0,)") +call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:GrepComplete Glog :call s:Log('grep',0,,,)") +call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:GrepComplete Gllog :call s:Log('lgrep',0,,,)") function! s:Grep(cmd,bang,arg) abort let grepprg = &grepprg @@ -1464,18 +1913,18 @@ function! s:Grep(cmd,bang,arg) abort let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' let dir = getcwd() try - execute cd s:fnameescape(s:repo().tree()) - let &grepprg = s:repo().git_command('--no-pager', 'grep', '-n', '--no-color') + execute cd s:fnameescape(s:Tree()) + let &grepprg = s:UserCommand() . ' --no-pager grep -n --no-color' let &grepformat = '%f:%l:%m,%m %f match%ts,%f' exe a:cmd.'! '.escape(matchstr(a:arg,'\v\C.{-}%($|[''" ]\@=\|)@='),'|') let list = a:cmd =~# '^l' ? getloclist(0) : getqflist() for entry in list if bufname(entry.bufnr) =~ ':' - let entry.filename = s:repo().translate(bufname(entry.bufnr)) + let entry.filename = s:Generate(bufname(entry.bufnr)) unlet! entry.bufnr let changed = 1 elseif a:arg =~# '\%(^\| \)--cached\>' - let entry.filename = s:repo().translate(':0:'.bufname(entry.bufnr)) + let entry.filename = s:Generate(':0:'.bufname(entry.bufnr)) unlet! entry.bufnr let changed = 1 endif @@ -1497,21 +1946,22 @@ function! s:Grep(cmd,bang,arg) abort endtry endfunction -function! s:Log(cmd, line1, line2, ...) abort - let path = s:buffer().relative('/') +function! s:Log(cmd, bang, line1, line2, ...) abort + let path = s:Relative('/') if path =~# '^/\.git\%(/\|$\)' || index(a:000,'--') != -1 let path = '' endif let cmd = ['--no-pager', 'log', '--no-color'] - let cmd += ['--pretty=format:fugitive://'.s:repo().dir().'//%H'.path.'::'.g:fugitive_summary_format] + let cmd += ['--pretty=format:fugitive://'.b:git_dir.'//%H'.path.'::'.g:fugitive_summary_format] if empty(filter(a:000[0 : index(a:000,'--')],'v:val !~# "^-"')) - if s:buffer().commit() =~# '\x\{40\}' - let cmd += [s:buffer().commit()] - elseif s:buffer().relative() =~# '^\.git/refs/\|^\.git/.*HEAD$' - let cmd += [s:buffer().relative()[5:-1]] + let commit = s:DirCommitFile(@%)[1] + if len(commit) > 2 + let cmd += [commit] + elseif s:Relative('') =~# '^\.git/refs/\|^\.git/.*HEAD$' + let cmd += [s:Relative('')[5:-1]] endif end - let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:buffer().relative(),submatch(1))")') + let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:Relative(''),submatch(1))")') if path =~# '/.' if a:line2 let cmd += ['-L', a:line1 . ',' . a:line2 . ':' . path[1:-1]] @@ -1524,10 +1974,10 @@ function! s:Log(cmd, line1, line2, ...) abort let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' let dir = getcwd() try - execute cd s:fnameescape(s:repo().tree()) - let &grepprg = escape(call(s:repo().git_command,cmd,s:repo()),'%#') + execute cd s:fnameescape(s:Tree()) + let &grepprg = escape(s:UserCommand() . join(map(cmd, '" ".s:shellesc(v:val)'), ''), '%#') let &grepformat = '%Cdiff %.%#,%C--- %.%#,%C+++ %.%#,%Z@@ -%\d%\+\,%\d%\+ +%l\,%\d%\+ @@,%-G-%.%#,%-G+%.%#,%-G %.%#,%A%f::%m,%-G%.%#' - exe a:cmd + exe a:cmd . (a:bang ? '!' : '') finally let &grepformat = grepformat let &grepprg = grepprg @@ -1542,138 +1992,159 @@ function! s:UsableWin(nr) abort \ index(['nofile','help','quickfix'], getbufvar(winbufnr(a:nr), '&buftype')) < 0 endfunction -function! s:Edit(cmd, bang, mods, ...) abort +function! s:Expand(rev) abort + return fugitive#buffer().expand(a:rev) +endfunction + +function! s:EditParse(args) abort + let pre = [] + let args = copy(a:args) + while !empty(args) && args[0] =~# '^+' + call add(pre, escape(remove(args, 0), ' |"') . ' ') + endwhile + if len(args) + let file = join(args) + elseif empty(expand('%')) + let file = ':' + elseif empty(s:DirCommitFile(@%)[1]) && s:Relative('./') !~# '^\./\.git\>' + let file = s:Relative(':0:') + else + let file = s:Relative('./') + endif + return [s:Expand(file), join(pre)] +endfunction + +function! s:Edit(cmd, bang, mods, args, ...) abort let mods = a:mods ==# '' ? '' : a:mods - let buffer = s:buffer() - if a:cmd !~# 'read' - if &previewwindow && getbufvar('','fugitive_type') ==# 'index' - let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)') - if len(winnrs) - exe winnrs[0].'wincmd w' - elseif winnr('$') == 1 - let tabs = (&go =~# 'e' || !has('gui_running')) && &stal && (tabpagenr('$') >= &stal) - execute 'rightbelow' (&lines - &previewheight - &cmdheight - tabs - 1 - !!&laststatus).'new' - else - rightbelow new - endif - if &diff - let mywinnr = winnr() - for winnr in range(winnr('$'),1,-1) - if winnr != mywinnr && getwinvar(winnr,'&diff') - execute winnr.'wincmd w' - close - if winnr('$') > 1 - wincmd p - endif + if &previewwindow && get(b:,'fugitive_type', '') ==# 'index' && a:cmd ==# 'edit' + let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)') + if len(winnrs) + exe winnrs[0].'wincmd w' + elseif winnr('$') == 1 + let tabs = (&go =~# 'e' || !has('gui_running')) && &stal && (tabpagenr('$') >= &stal) + execute 'rightbelow' (&lines - &previewheight - &cmdheight - tabs - 1 - !!&laststatus).'new' + else + rightbelow new + endif + if &diff + let mywinnr = winnr() + for winnr in range(winnr('$'),1,-1) + if winnr != mywinnr && getwinvar(winnr,'&diff') + execute winnr.'wincmd w' + close + if winnr('$') > 1 + wincmd p endif - endfor - diffoff! - endif + endif + endfor + diffoff! endif endif if a:bang - let arglist = map(copy(a:000), 's:gsub(v:val, ''\\@>>>>>> - if getline(1) !~# '^diff ' - setlocal readonly nomodifiable - endif - if a:cmd =~# 'pedit' - wincmd p - endif - return echo + let temp = s:tempname() + let s:temp_files[s:cpath(temp)] = { 'dir': b:git_dir } + silent execute mods a:cmd temp + if a:cmd =~# 'pedit' + wincmd P endif - return '' + let echo = call('s:Read', [-1, 0, 1, 1, 1, mods, a:args] + a:000) + silent write! + setlocal buftype=nowrite nomodified filetype=git foldmarker=<<<<<<<,>>>>>>> + if getline(1) !~# '^diff ' + setlocal readonly nomodifiable + endif + if a:cmd =~# 'pedit' + wincmd p + endif + return echo endif - if a:0 && a:1 == '' - return '' - elseif a:0 - let file = buffer.expand(join(a:000, ' ')) - elseif expand('%') ==# '' - let file = ':' - elseif buffer.commit() ==# '' && buffer.relative('/') !~# '^/.git\>' - let file = buffer.relative(':') - else - let file = buffer.relative('/') - endif + let [file, pre] = s:EditParse(a:000) try - let file = buffer.repo().translate(file) + let file = s:Generate(file) catch /^fugitive:/ return 'echoerr v:errmsg' endtry if file !~# '^fugitive:' let file = s:sub(file, '/$', '') endif - if a:cmd ==# 'read' - return 'silent %delete_|'.mods.' read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.') - else - return mods.' '.a:cmd.' '.s:fnameescape(file) - endif + return mods . ' ' . a:cmd . ' ' . pre . s:fnameescape(file) endfunction -function! s:EditComplete(A,L,P) abort - return map(s:repo().superglob(a:A), 'fnameescape(v:val)') +function! s:Read(count, line1, line2, range, bang, mods, args, ...) abort + let mods = a:mods ==# '' ? '' : a:mods + let after = a:line2 + if a:count < 0 + let delete = 'silent 1,' . line('$') . 'delete_|' + let after = line('$') + elseif a:range == 2 + let delete = 'silent ' . a:line1 . ',' . a:line2 . 'delete_|' + else + let delete = '' + endif + if a:bang + let args = s:gsub(a:args, '\\@',0,'',)") -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Gedit :execute s:Edit('edit',0,'',)") -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditRunComplete Gpedit :execute s:Edit('pedit',0,'',)") -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditRunComplete Gsplit :execute s:Edit('split',0,'',)") -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditRunComplete Gvsplit :execute s:Edit('vsplit',0,'',)") -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditRunComplete Gtabedit :execute s:Edit('tabedit',0,'',)") -call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:EditRunComplete Gread :execute s:Edit(( == -1 ? '' : ).'read',0,'',)") +call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#Complete Ge execute s:Edit('edit', 0, '', , )") +call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#Complete Gedit execute s:Edit('edit', 0, '', , )") +call s:command("-bar -bang -nargs=* -complete=customlist,s:EditRunComplete Gpedit execute s:Edit('pedit', 0, '', , )") +call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditRunComplete Gsplit execute s:Edit(( ? : '').'split', 0, '', , )") +call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditRunComplete Gvsplit execute s:Edit(( ? : '').'vsplit', 0, '', , )") +call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditRunComplete" . (has('patch-7.4.542') ? ' -addr=tabs' : '') . " Gtabedit execute s:Edit(( ? : '').'tabedit', 0, '', , )") +call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:EditRunComplete Gread execute s:Read(, , , +'', 0, '', , )") " Section: Gwrite, Gwq -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Gwrite :execute s:Write(0,)") -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Gw :execute s:Write(0,)") -call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Gwq :execute s:Wq(0,)") +call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#Complete Gwrite :execute s:Write(0,)") +call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#Complete Gw :execute s:Write(0,)") +call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#Complete Gwq :execute s:Wq(0,)") function! s:Write(force,...) abort if exists('b:fugitive_commit_arguments') return 'write|bdelete' elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != '' return 'wq' - elseif s:buffer().type() == 'index' + elseif get(b:, 'fugitive_type', '') ==# 'index' return 'Gcommit' - elseif s:buffer().relative() ==# '' && getline(4) =~# '^+++ ' + elseif s:Relative('') ==# '' && getline(4) =~# '^+++ ' let filename = getline(4)[6:-1] setlocal buftype= silent write setlocal buftype=nowrite - if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# s:repo().rev_parse(':0:'.filename)[0:6] - let err = s:repo().git_chomp('apply','--cached','--reverse',s:buffer().spec()) + if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# fugitive#RevParse(':0:'.filename)[0:6] + let err = s:TreeChomp('apply', '--cached', '--reverse', expand('%:p')) else - let err = s:repo().git_chomp('apply','--cached',s:buffer().spec()) + let err = s:TreeChomp('apply', '--cached', expand('%:p')) endif if err !=# '' let v:errmsg = split(err,"\n")[0] @@ -1686,19 +2157,19 @@ function! s:Write(force,...) abort endif let mytab = tabpagenr() let mybufnr = bufnr('') - let path = a:0 ? join(a:000, ' ') : s:buffer().relative() + let path = a:0 ? join(a:000, ' ') : s:Relative('') if empty(path) return 'echoerr '.string('fugitive: cannot determine file path') endif if path =~# '^:\d\>' - return 'write'.(a:force ? '! ' : ' ').s:fnameescape(s:repo().translate(s:buffer().expand(path))) + return 'write'.(a:force ? '! ' : ' ').s:fnameescape(s:Generate(s:Expand(path))) endif - let always_permitted = (s:buffer().relative() ==# path && s:buffer().commit() =~# '^0\=$') - if !always_permitted && !a:force && s:repo().git_chomp_in_tree('diff','--name-status','HEAD','--',path) . s:repo().git_chomp_in_tree('ls-files','--others','--',path) !=# '' + let always_permitted = (s:Relative('') ==# path && s:DirCommitFile(@%)[1] =~# '^0\=$') + if !always_permitted && !a:force && (len(s:TreeChomp('diff','--name-status','HEAD','--',path)) || len(s:TreeChomp('ls-files','--others','--',path))) let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)' return 'echoerr v:errmsg' endif - let file = s:repo().translate(path) + let file = s:Generate(path) let treebufnr = 0 for nr in range(1,bufnr('$')) if fnamemodify(bufname(nr),':p') ==# file @@ -1738,25 +2209,25 @@ function! s:Write(force,...) abort call writefile(readfile(temp,'b'),file,'b') endif else - execute 'write! '.s:fnameescape(s:repo().translate(path)) + execute 'write! '.s:fnameescape(s:Generate(path)) endif if a:force - let error = s:repo().git_chomp_in_tree('add', '--force', '--', path) + let error = s:TreeChomp('add', '--force', '--', path) else - let error = s:repo().git_chomp_in_tree('add', '--', path) + let error = s:TreeChomp('add', '--', path) endif if v:shell_error let v:errmsg = 'fugitive: '.error return 'echoerr v:errmsg' endif - if s:buffer().relative() ==# path && s:buffer().commit() =~# '^\d$' + if s:Relative('') ==# path && s:DirCommitFile(@%)[1] =~# '^\d$' set nomodified endif - let one = s:repo().translate(':1:'.path) - let two = s:repo().translate(':2:'.path) - let three = s:repo().translate(':3:'.path) + let one = s:Generate(':1:'.path) + let two = s:Generate(':2:'.path) + let three = s:Generate(':3:'.path) for nr in range(1,bufnr('$')) let name = fnamemodify(bufname(nr), ':p') if bufloaded(nr) && !getbufvar(nr,'&modified') && (name ==# one || name ==# two || name ==# three) @@ -1765,7 +2236,7 @@ function! s:Write(force,...) abort endfor unlet! restorewinnr - let zero = s:repo().translate(':0:'.path) + let zero = s:Generate(':0:'.path) silent execute 'doautocmd BufWritePost' s:fnameescape(zero) for tab in range(1,tabpagenr('$')) for winnr in range(1,tabpagewinnr(tab,'$')) @@ -1829,8 +2300,8 @@ function! s:Dispatch(bang, args) try let b:current_compiler = 'git' let &l:errorformat = s:common_efm - let &l:makeprg = substitute(s:git_command() . ' ' . a:args, '\s\+$', '', '') - execute cd fnameescape(s:repo().tree()) + let &l:makeprg = substitute(s:UserCommand() . ' ' . a:args, '\s\+$', '', '') + execute cd fnameescape(s:Tree()) if exists(':Make') == 2 noautocmd Make else @@ -1848,9 +2319,9 @@ endfunction " Section: Gdiff -call s:command("-bang -bar -nargs=* -complete=customlist,s:EditComplete Gdiff :execute s:Diff('',0,)") -call s:command("-bang -bar -nargs=* -complete=customlist,s:EditComplete Gvdiff :execute s:Diff('keepalt vert ',0,)") -call s:command("-bang -bar -nargs=* -complete=customlist,s:EditComplete Gsdiff :execute s:Diff('keepalt ',0,)") +call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#Complete Gdiff :execute s:Diff('',0,)") +call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#Complete Gvdiff :execute s:Diff('keepalt vert ',0,)") +call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#Complete Gsdiff :execute s:Diff('keepalt ',0,)") augroup fugitive_diff autocmd! @@ -1942,28 +2413,28 @@ function! s:diffoff_all(dir) abort execute curwin.'wincmd w' endfunction -function! s:buffer_compare_age(commit) dict abort +function! s:CompareAge(mine, theirs) abort let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5} - let my_score = get(scores, ':'.self.commit(), 0) - let their_score = get(scores, ':'.substitute(a:commit, '^:', '', ''), 0) + let mine = substitute(a:mine, '^:', '', '') + let theirs = substitute(a:theirs, '^:', '', '') + let my_score = get(scores, ':'.mine, 0) + let their_score = get(scores, ':'.theirs, 0) if my_score || their_score return my_score < their_score ? -1 : my_score != their_score - elseif self.commit() ==# a:commit + elseif mine ==# theirs return 0 endif - let base = self.repo().git_chomp('merge-base',self.commit(),a:commit) - if base ==# self.commit() + let base = s:TreeChomp('merge-base', mine, theirs) + if base ==# mine return -1 - elseif base ==# a:commit + elseif base ==# theirs return 1 endif - let my_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',self.commit()) - let their_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',a:commit) + let my_time = +s:TreeChomp('log','--max-count=1','--pretty=format:%at',a:mine) + let their_time = +s:TreeChomp('log','--max-count=1','--pretty=format:%at',a:theirs) return my_time < their_time ? -1 : my_time != their_time endfunction -call s:add_methods('buffer',['compare_age']) - function! s:Diff(vert,keepfocus,...) abort let args = copy(a:000) let post = '' @@ -1971,21 +2442,23 @@ function! s:Diff(vert,keepfocus,...) abort let post = remove(args, 0)[1:-1] endif let vert = empty(a:vert) ? s:diff_modifier(2) : a:vert + let commit = s:DirCommitFile(@%)[1] + let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p' if exists(':DiffGitCached') return 'DiffGitCached' - elseif (empty(args) || args[0] ==# ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().relative()) !=# '' + elseif (empty(args) || args[0] ==# ':') && commit =~# '^[0-1]\=$' && !empty(s:TreeChomp('ls-files', '--unmerged', '--', s:Relative(''))) let vert = empty(a:vert) ? s:diff_modifier(3) : a:vert let nr = bufnr('') - execute 'leftabove '.vert.'split' s:fnameescape(fugitive#repo().translate(s:buffer().expand(':2'))) + execute 'leftabove '.vert.'split' s:fnameescape(s:Generate(s:Relative(':2:'))) execute 'nnoremap dp :diffput '.nr.'diffupdate' let nr2 = bufnr('') call s:diffthis() - wincmd p - execute 'rightbelow '.vert.'split' s:fnameescape(fugitive#repo().translate(s:buffer().expand(':3'))) + exe back + execute 'rightbelow '.vert.'split' s:fnameescape(s:Generate(s:Relative(':3:'))) execute 'nnoremap dp :diffput '.nr.'diffupdate' let nr3 = bufnr('') call s:diffthis() - wincmd p + exe back call s:diffthis() execute 'nnoremap d2o :diffget '.nr2.'diffupdate' execute 'nnoremap d3o :diffget '.nr3.'diffupdate' @@ -1995,32 +2468,32 @@ function! s:Diff(vert,keepfocus,...) abort if arg ==# '' return post elseif arg ==# '/' - let file = s:buffer().relative('/') + let file = s:Relative('/') elseif arg ==# ':' - let file = s:buffer().relative(':0:') + let file = s:Relative(':0:') elseif arg =~# '^:/.' try - let file = s:repo().rev_parse(arg).s:buffer().relative(':') + let file = fugitive#RevParse(arg).s:Relative(':') catch /^fugitive:/ return 'echoerr v:errmsg' endtry else - let file = s:buffer().expand(arg) + let file = s:Expand(arg) endif - if file !~# ':' && file !~# '^/' && s:repo().git_chomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$' - let file = file.s:buffer().relative(':') + if file !~# ':' && file !~# '^/' && s:TreeChomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$' + let file = file.s:Relative(':') endif else - let file = s:buffer().relative(empty(s:buffer().commit()) ? ':0:' : '/') + let file = s:Relative(empty(commit) ? ':0:' : '/') endif try - let spec = s:repo().translate(file) + let spec = s:Generate(file) let restore = s:diff_restore() if exists('+cursorbind') setlocal cursorbind endif let w:fugitive_diff_restore = restore - if s:buffer().compare_age(s:DirCommitFile(spec)[1]) < 0 + if s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0 execute 'rightbelow '.vert.'diffsplit '.s:fnameescape(spec) else execute 'leftabove '.vert.'diffsplit '.s:fnameescape(spec) @@ -2030,7 +2503,7 @@ function! s:Diff(vert,keepfocus,...) abort let w:fugitive_diff_restore = restore let winnr = winnr() if getwinvar('#', '&diff') - wincmd p + exe back if !a:keepfocus call feedkeys(winnr."\w", 'n') endif @@ -2044,64 +2517,50 @@ endfunction " Section: Gmove, Gremove function! s:Move(force, rename, destination) abort - if a:destination =~# '^/' - let destination = a:destination[1:-1] + if a:destination =~# '^[.:]\=/' + let destination = substitute(a:destination[1:-1], '^[.:]\=/', '', '') elseif a:rename - let destination = fnamemodify(s:buffer().relative(), ':h') . '/' . a:destination + let destination = fnamemodify(s:Relative(''), ':h') . '/' . a:destination else - let destination = s:shellslash(fnamemodify(s:sub(a:destination,'[%#]%(:\w)*','\=expand(submatch(0))'),':p')) - if destination[0:strlen(s:repo().tree())] ==# s:repo().tree('') - let destination = destination[strlen(s:repo().tree('')):-1] - endif + let destination = a:destination endif - if isdirectory(s:buffer().spec()) - " Work around Vim parser idiosyncrasy - let discarded = s:buffer().setvar('&swapfile',0) + if isdirectory(@%) + setlocal noswapfile endif - let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().relative(), destination], s:repo()) + let message = call('s:TreeChomp', ['mv'] + (a:force ? ['-f'] : []) + ['--', s:Relative(''), destination]) if v:shell_error let v:errmsg = 'fugitive: '.message return 'echoerr v:errmsg' endif - let destination = s:repo().tree(destination) + let destination = s:Tree() . '/' . destination if isdirectory(destination) let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.') endif call fugitive#ReloadStatus() - if empty(s:buffer().commit()) + if empty(s:DirCommitFile(@%)[1]) if isdirectory(destination) return 'keepalt edit '.s:fnameescape(destination) else return 'keepalt saveas! '.s:fnameescape(destination) endif else - return 'file '.s:fnameescape(s:repo().translate(':0:'.destination)) - endif -endfunction - -function! s:MoveComplete(A,L,P) abort - if a:A =~# '^/' - return s:repo().superglob(a:A) - else - let matches = split(glob(a:A.'*'),"\n") - call map(matches,'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val') - return matches + return 'file '.s:fnameescape(s:Generate(':0:'.destination)) endif endfunction function! s:RenameComplete(A,L,P) abort - if a:A =~# '^/' - return s:repo().superglob(a:A) + if a:A =~# '^\.\=/' + return fugitive#PathComplete(a:A) else - let pre = '/'. fnamemodify(s:buffer().relative(), ':h') . '/' - return map(s:repo().superglob(pre.a:A), 'strpart(v:val, len(pre))') + let pre = '/' . fnamemodify(s:Relative(''), ':h') . '/' + return map(fugitive#PathComplete(pre.a:A), 'strpart(v:val, len(pre))') endif endfunction function! s:Remove(after, force) abort - if s:buffer().commit() ==# '' + if s:DirCommitFile(@%)[1] ==# '' let cmd = ['rm'] - elseif s:buffer().commit() ==# '0' + elseif s:DirCommitFile(@%)[1] ==# '0' let cmd = ['rm','--cached'] else let v:errmsg = 'fugitive: rm not supported here' @@ -2110,7 +2569,7 @@ function! s:Remove(after, force) abort if a:force let cmd += ['--force'] endif - let message = call(s:repo().git_chomp_in_tree,cmd+['--',s:buffer().relative()],s:repo()) + let message = call('s:TreeChomp', cmd+['--',s:Relative('')]) if v:shell_error let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)') return 'echoerr '.string(v:errmsg) @@ -2122,8 +2581,8 @@ endfunction augroup fugitive_remove autocmd! - autocmd User Fugitive if s:buffer().commit() =~# '^0\=$' | - \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,s:MoveComplete Gmove :execute s:Move(0,0,)" | + autocmd User Fugitive if s:DirCommitFile(@%)[1] =~# '^0\=$' | + \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,fugitive#PathComplete Gmove :execute s:Move(0,0,)" | \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,s:RenameComplete Grename :execute s:Move(0,1,)" | \ exe "command! -buffer -bar -bang Gremove :execute s:Remove('edit',0)" | \ exe "command! -buffer -bar -bang Gdelete :execute s:Remove('bdelete',0)" | @@ -2132,11 +2591,23 @@ augroup END " Section: Gblame +function! s:Keywordprg() abort + let args = ' --git-dir='.escape(b:git_dir,"\\\"' ") + if has('gui_running') && !has('win32') + return s:UserCommand() . ' --no-pager' . args . ' log -1' + else + return s:UserCommand() . args . ' show' + endif +endfunction + augroup fugitive_blame autocmd! - autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif + autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:Keywordprg() | endif autocmd Syntax fugitiveblame call s:BlameSyntax() - autocmd User Fugitive if s:buffer().type('file', 'blob', 'blame') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(0,,,,[])" | endif + autocmd User Fugitive + \ if get(b:, 'fugitive_type') =~# '^\%(file\|blob\|blame\)$' || filereadable(@%) | + \ exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(0,,,,'',[])" | + \ endif autocmd ColorScheme,GUIEnter * call s:RehighlightBlame() autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('')), 'fugitive_leave') augroup END @@ -2151,49 +2622,55 @@ function! s:linechars(pattern) abort return chars endfunction -function! s:Blame(bang,line1,line2,count,args) abort +function! s:Blame(bang, line1, line2, count, mods, args) abort if exists('b:fugitive_blamed_bufnr') return 'bdelete' endif try - if empty(s:buffer().relative()) + if empty(s:Relative('')) call s:throw('file or blob required') endif if filter(copy(a:args),'v:val !~# "^\\%(--root\|--show-name\\|-\\=\\%([ltfnsew]\\|[MC]\\d*\\)\\+\\)$"') != [] call s:throw('unsupported option') endif call map(a:args,'s:sub(v:val,"^\\ze[^-]","-")') - let cmd = ['--no-pager', 'blame', '--show-number'] + a:args - if s:buffer().commit() =~# '\D\|..' - let cmd += [s:buffer().commit()] + let cmd = ['--no-pager', 'blame', '--show-number'] + if a:count + let cmd += ['-L', a:line1 . ',' . a:line1] + endif + let cmd += a:args + if s:DirCommitFile(@%)[1] =~# '\D\|..' + let cmd += [s:DirCommitFile(@%)[1]] else let cmd += ['--contents', '-'] endif - let cmd += ['--', s:buffer().relative()] - let basecmd = escape(call(s:repo().git_command,cmd,s:repo()),'!%#') + let cmd += ['--', s:Relative('')] + let basecmd = escape(call('s:Prepare', [b:git_dir] + cmd), '!#%') try let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' - if !s:repo().bare() - let dir = getcwd() - execute cd s:fnameescape(s:repo().tree()) + let tree = s:Tree() + if len(tree) && s:cpath(tree) !=# s:cpath(getcwd()) + let cwd = getcwd() + execute cd s:fnameescape(tree) + endif + let error = s:tempname() + let temp = error.'.fugitiveblame' + if &shell =~# 'csh' + silent! execute '%write !('.basecmd.' > '.temp.') >& '.error + else + silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error + endif + if exists('l:cwd') + execute cd s:fnameescape(cwd) + unlet cwd + endif + if v:shell_error + call s:throw(join(readfile(error),"\n")) endif if a:count - execute 'write !'.substitute(basecmd,' blame ',' blame -L '.a:line1.','.a:line2.' ','g') + let edit = substitute(a:mods, '^$', '', '') . get(['edit', 'split', 'pedit'], a:line2 - a:line1, ' split') + return s:BlameCommit(edit, get(readfile(temp), 0, '')) else - let error = s:tempname() - let temp = error.'.fugitiveblame' - if &shell =~# 'csh' - silent! execute '%write !('.basecmd.' > '.temp.') >& '.error - else - silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error - endif - if exists('l:dir') - execute cd s:fnameescape(dir) - unlet dir - endif - if v:shell_error - call s:throw(join(readfile(error),"\n")) - endif for winnr in range(winnr('$'),1,-1) call setwinvar(winnr, '&scrollbind', 0) if exists('+cursorbind') @@ -2220,7 +2697,7 @@ function! s:Blame(bang,line1,line2,count,args) abort endif let top = line('w0') + &scrolloff let current = line('.') - let s:temp_files[s:cpath(temp)] = { 'dir': s:repo().dir(), 'args': cmd } + let s:temp_files[s:cpath(temp)] = { 'dir': b:git_dir, 'args': cmd } exe 'keepalt leftabove vsplit '.temp let b:fugitive_blamed_bufnr = bufnr let b:fugitive_type = 'blame' @@ -2251,6 +2728,7 @@ function! s:Blame(bang,line1,line2,count,args) abort nnoremap i :exe BlameCommit("exe 'norm q'edit") nnoremap o :exe BlameCommit((&splitbelow ? "botright" : "topleft")." split") nnoremap O :exe BlameCommit("tabedit") + nnoremap p :exe Edit((&splitbelow ? "botright" : "topleft").' pedit', 0, '', matchstr(getline('.'), '\x\+'), matchstr(getline('.'), '\x\+')) nnoremap A :exe "vertical resize ".(linechars('.\{-\}\ze [0-9:/+-][0-9:/+ -]* \d\+)')+1+v:count) nnoremap C :exe "vertical resize ".(linechars('^\S\+')+1+v:count) nnoremap D :exe "vertical resize ".(linechars('.\{-\}\ze\d\ze\s\+\d\+)')+1-v:count) @@ -2258,8 +2736,8 @@ function! s:Blame(bang,line1,line2,count,args) abort syncbind endif finally - if exists('l:dir') - execute cd s:fnameescape(dir) + if exists('l:cwd') + execute cd s:fnameescape(cwd) endif endtry return '' @@ -2268,17 +2746,24 @@ function! s:Blame(bang,line1,line2,count,args) abort endtry endfunction -function! s:BlameCommit(cmd) abort - let cmd = s:Edit(a:cmd, 0, '', matchstr(getline('.'),'\x\+')) +function! s:BlameCommit(cmd, ...) abort + let line = a:0 ? a:1 : getline('.') + if line =~# '^0\{4,40\} ' + return 'echoerr ' . string('Not Committed Yet') + endif + let cmd = s:Edit(a:cmd, 0, '', matchstr(line, '\x\+'), matchstr(line, '\x\+')) if cmd =~# '^echoerr' return cmd endif - let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]') - let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ') + let lnum = matchstr(line, ' \zs\d\+\ze\s\+[([:digit:]]') + let path = matchstr(line, '^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ') if path ==# '' - let path = s:buffer(b:fugitive_blamed_bufnr).relative() + let path = fugitive#Path(a:0 ? @% : bufname(b:fugitive_blamed_bufnr), '') endif execute cmd + if a:cmd ==# 'pedit' + return '' + endif if search('^diff .* b/\M'.escape(path,'\').'$','W') call search('^+++') let head = line('.') @@ -2317,7 +2802,7 @@ function! s:BlameJump(suffix) abort let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]') let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ') if path ==# '' - let path = s:buffer(b:fugitive_blamed_bufnr).relative() + let path = fugitive#Path(bufname(b:fugitive_blamed_bufnr), '') endif let args = b:fugitive_blame_arguments let offset = line('.') - line('w0') @@ -2326,7 +2811,7 @@ function! s:BlameJump(suffix) abort if winnr > 0 exe winnr.'wincmd w' endif - execute s:Edit('edit', 0, '', commit.a:suffix.':'.path) + execute 'Gedit' s:fnameescape(commit . a:suffix . ':' . path) execute lnum if winnr > 0 exe bufnr.'bdelete' @@ -2408,7 +2893,7 @@ endfunction " Section: Gbrowse -call s:command("-bar -bang -range=0 -nargs=* -complete=customlist,s:EditComplete Gbrowse :execute s:Browse(0,,,)") +call s:command("-bar -bang -range=0 -nargs=* -complete=customlist,fugitive#Complete Gbrowse :execute s:Browse(0,,,)") let s:redirects = {} @@ -2425,14 +2910,14 @@ function! s:Browse(bang,line1,count,...) abort if rev ==# '' let expanded = s:buffer().rev() elseif rev ==# ':' - let expanded = s:buffer().relative('/') + let expanded = s:Relative('/') else - let expanded = s:buffer().expand(rev) + let expanded = s:Expand(rev) endif - if filereadable(s:repo().tree('refs/tags/' . expanded)) - let expanded = 'refs/tags/' . expanded + if filereadable(b:git_dir . '/refs/tags/' . expanded) + let expanded = '.git/refs/tags/' . expanded endif - let full = s:repo().translate(expanded) + let full = s:Generate(expanded) let commit = '' if full =~? '^fugitive:' let [dir, commit, path] = s:DirCommitFile(full) @@ -2440,17 +2925,17 @@ function! s:Browse(bang,line1,count,...) abort let commit = '' endif if commit =~ '..' - let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':')) + let type = s:TreeChomp('cat-file','-t',commit.s:sub(path,'^/',':')) let branch = matchstr(expanded, '^[^:]*') else let type = 'blob' endif let path = path[1:-1] - elseif s:repo().bare() - let path = '.git/' . full[strlen(s:repo().dir())+1:-1] + elseif empty(s:Tree()) + let path = '.git/' . full[strlen(b:git_dir)+1:-1] let type = '' else - let path = full[strlen(s:repo().tree())+1:-1] + let path = full[strlen(s:Tree())+1:-1] if path =~# '^\.git/' let type = '' elseif isdirectory(full) @@ -2462,8 +2947,8 @@ function! s:Browse(bang,line1,count,...) abort if type ==# 'tree' && !empty(path) let path = s:sub(path, '/\=$', '/') endif - if path =~# '^\.git/.*HEAD' && filereadable(s:repo().dir(path[5:-1])) - let body = readfile(s:repo().dir(path[5:-1]))[0] + if path =~# '^\.git/.*HEAD' && filereadable(b:git_dir . '/' . path[5:-1]) + let body = readfile(b:git_dir . '/' . path[5:-1])[0] if body =~# '^\x\{40\}$' let commit = body let type = 'commit' @@ -2485,16 +2970,16 @@ function! s:Browse(bang,line1,count,...) abort elseif path =~# '^\.git/refs/heads/.' let branch = path[16:-1] elseif !exists('branch') - let branch = s:repo().head() + let branch = FugitiveHead() endif if !empty(branch) - let r = s:repo().git_chomp('config','branch.'.branch.'.remote') - let m = s:repo().git_chomp('config','branch.'.branch.'.merge')[11:-1] + let r = fugitive#Config('branch.'.branch.'.remote') + let m = fugitive#Config('branch.'.branch.'.merge')[11:-1] if r ==# '.' && !empty(m) - let r2 = s:repo().git_chomp('config','branch.'.m.'.remote') + let r2 = fugitive#Config('branch.'.m.'.remote') if r2 !~# '^\.\=$' let r = r2 - let m = s:repo().git_chomp('config','branch.'.m.'.merge')[11:-1] + let m = fugitive#Config('branch.'.m.'.merge')[11:-1] endif endif if empty(remote) @@ -2512,22 +2997,20 @@ function! s:Browse(bang,line1,count,...) abort if a:line1 && !a:count && !empty(merge) let commit = merge else - let commit = s:repo().rev_parse('HEAD') + let commit = readfile(b:git_dir . '/HEAD', '', 1)[0] + let i = 0 + while commit =~# '^ref: ' && i < 10 + let commit = readfile(b:git_dir . '/' . commit[5:-1], '', 1)[0] + let i -= 1 + endwhile endif endif if empty(remote) let remote = '.' - let remote_for_url = 'origin' - else - let remote_for_url = remote endif - if fugitive#GitVersion() =~# '^[01]\.\|^2\.[0-6]\.' - let raw = s:repo().git_chomp('config','remote.'.remote_for_url.'.url') - else - let raw = s:repo().git_chomp('remote','get-url',remote_for_url) - endif - if raw ==# '' + let raw = fugitive#RemoteUrl(remote) + if empty(raw) let raw = remote endif @@ -2542,25 +3025,26 @@ function! s:Browse(bang,line1,count,...) abort endif endif - for Handler in g:fugitive_browse_handlers - let url = call(Handler, [{ - \ 'repo': s:repo(), - \ 'remote': raw, - \ 'revision': 'No longer provided', - \ 'commit': commit, - \ 'path': path, - \ 'type': type, - \ 'line1': a:count > 0 ? a:line1 : 0, - \ 'line2': a:count > 0 ? a:count : 0}]) + let opts = { + \ 'dir': b:git_dir, + \ 'repo': fugitive#repo(), + \ 'remote': raw, + \ 'revision': 'No longer provided', + \ 'commit': commit, + \ 'path': path, + \ 'type': type, + \ 'line1': a:count > 0 ? a:line1 : 0, + \ 'line2': a:count > 0 ? a:count : 0} + + for Handler in get(g:, 'fugitive_browse_handlers', []) + let url = call(Handler, [copy(opts)]) if !empty(url) break endif endfor - if empty(url) && raw ==# '.' - call s:throw("Instaweb failed to start") - elseif empty(url) - call s:throw("'".remote."' is not a supported remote") + if empty(url) + call s:throw("No Gbrowse handler found for '".raw."'") endif let url = s:gsub(url, '[ <>]', '\="%".printf("%02X",char2nr(submatch(0)))') @@ -2586,433 +3070,130 @@ function! s:Browse(bang,line1,count,...) abort endtry endfunction -function! s:github_url(opts, ...) abort - if a:0 || type(a:opts) != type({}) - return '' - endif - let domain_pattern = 'github\.com' - let domains = exists('g:fugitive_github_domains') ? g:fugitive_github_domains : [] - for domain in domains - let domain_pattern .= '\|' . escape(split(domain, '://')[-1], '.') - endfor - let repo = matchstr(get(a:opts, 'remote'), '^\%(https\=://\|git://\|git@\)\=\zs\('.domain_pattern.'\)[/:].\{-\}\ze\%(\.git\)\=$') - if repo ==# '' - return '' - endif - call s:warn('Install rhubarb.vim for GitHub support') - return 'https://github.com/tpope/vim-rhubarb' -endfunction - -function! s:instaweb_url(opts) abort - if a:opts.remote !=# '.' - return '' - endif - let output = a:opts.repo.git_chomp('instaweb','-b','unknown') - if output =~# 'http://' - let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:opts.repo.dir(),':t') - else - return '' - endif - if a:opts.path =~# '^\.git/refs/.' - return root . ';a=shortlog;h=' . matchstr(a:opts.path,'^\.git/\zs.*') - elseif a:opts.path =~# '^\.git\>' - return root - endif - let url = root - if a:opts.commit =~# '^\x\{40\}$' - if a:opts.type ==# 'commit' - let url .= ';a=commit' - endif - let url .= ';h=' . a:opts.repo.rev_parse(a:opts.commit . (a:opts.path == '' ? '' : ':' . a:opts.path)) - else - if a:opts.type ==# 'blob' && empty(a:opts.commit) - let url .= ';h='.a:opts.repo.git_chomp('hash-object', '-w', a:opts.path) - else - try - let url .= ';h=' . a:opts.repo.rev_parse((a:opts.commit == '' ? 'HEAD' : ':' . a:opts.commit) . ':' . a:opts.path) - catch /^fugitive:/ - call s:throw('fugitive: cannot browse uncommitted file') - endtry - endif - let root .= ';hb=' . matchstr(a:opts.repo.head_ref(),'[^ ]\+$') - endif - if a:opts.path !=# '' - let url .= ';f=' . a:opts.path - endif - if get(a:opts, 'line1') - let url .= '#l' . a:opts.line1 - endif - return url -endfunction - -if !exists('g:fugitive_browse_handlers') - let g:fugitive_browse_handlers = [] -endif - -call extend(g:fugitive_browse_handlers, - \ [s:function('s:github_url'), s:function('s:instaweb_url')]) - -" Section: File access - -function! s:WriteCmd(out, cmd, ...) abort - let prefix = '' - try - if a:0 && len(a:1) - if s:winshell() - let old_index = $GIT_INDEX_FILE - let $GIT_INDEX_FILE = a:1 - else - let prefix = 'env GIT_INDEX_FILE='.s:shellesc(a:1).' ' - endif - endif - let redir = ' > '.a:out - if s:winshell() - let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^' - return system('cmd /c "'.prefix.s:gsub(a:cmd,'[<>]', cmd_escape_char.'&').redir.'"') - elseif &shell =~# 'fish' - return system(' begin;'.prefix.a:cmd.redir.';end ') - else - return system(' ('.prefix.a:cmd.redir.') ') - endif - finally - if exists('old_index') - let $GIT_INDEX_FILE = old_index - endif - endtry -endfunction - -function! s:ReplaceCmd(cmd, ...) abort - let tmp = tempname() - let err = s:WriteCmd(tmp, a:cmd, a:0 ? a:1 : '') - if v:shell_error - call s:throw((len(err) ? err : filereadable(tmp) ? join(readfile(tmp), ' ') : 'unknown error running ' . a:cmd)) - endif - let fn = expand('%:p') - silent exe 'doau BufReadPre '.s:fnameescape(fn) - silent exe 'keepalt file '.tmp - try - silent noautocmd edit! - finally - try - silent exe 'keepalt file '.s:fnameescape(fn) - catch /^Vim\%((\a\+)\)\=:E302/ - endtry - call delete(tmp) - if fnamemodify(bufname('$'), ':p') ==# tmp - silent execute 'bwipeout '.bufnr('$') - endif - silent exe 'doau BufReadPost '.s:fnameescape(fn) - endtry -endfunction - -function! fugitive#BufReadStatus() abort - if !exists('b:fugitive_display_format') - let b:fugitive_display_format = filereadable(expand('%').'.lock') - endif - let b:fugitive_display_format = b:fugitive_display_format % 2 - let b:fugitive_type = 'index' - try - let b:git_dir = s:repo().dir() - setlocal noro ma nomodeline - if fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : b:git_dir . '/index', ':p') ==# expand('%:p') - let index = '' - else - let index = expand('%:p') - endif - if b:fugitive_display_format - call s:ReplaceCmd(s:repo().git_command('ls-files','--stage'),index) - set ft=git nospell - else - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' - let dir = getcwd() - if fugitive#GitVersion() =~# '^0\|^1\.[1-7]\.' - let cmd = s:repo().git_command('status') - else - let cmd = s:repo().git_command( - \ '-c', 'status.displayCommentPrefix=true', - \ '-c', 'color.status=false', - \ '-c', 'status.short=false', - \ 'status') - endif - try - execute cd s:fnameescape(s:repo().tree()) - call s:ReplaceCmd(cmd, index) - finally - execute cd s:fnameescape(dir) - endtry - set ft=gitcommit - set foldtext=fugitive#Foldtext() - endif - setlocal ro noma nomod noswapfile - if &bufhidden ==# '' - setlocal bufhidden=delete - endif - call fugitive#MapJumps() - nunmap P - nunmap ~ - nnoremap :execute StageNext(v:count1) - nnoremap :execute StagePrevious(v:count1) - nnoremap - :silent execute StageToggle(line('.'),line('.')+v:count1-1) - xnoremap - :silent execute StageToggle(line("'<"),line("'>")) - nnoremap a :let b:fugitive_display_format += 1exe fugitive#BufReadStatus() - nnoremap i :let b:fugitive_display_format -= 1exe fugitive#BufReadStatus() - nnoremap C :Gcommit:echohl WarningMsgecho ':Gstatus C is deprecated in favor of cc'echohl NONE - nnoremap cA :Gcommit --amend --reuse-message=HEAD:echohl WarningMsgecho ':Gstatus cA is deprecated in favor of ce'echohl NONE - nnoremap ca :Gcommit --amend - nnoremap cc :Gcommit - nnoremap ce :Gcommit --amend --no-edit - nnoremap cw :Gcommit --amend --only - nnoremap cva :Gcommit -v --amend - nnoremap cvc :Gcommit -v - nnoremap D :execute StageDiff('Gdiff') - nnoremap dd :execute StageDiff('Gdiff') - nnoremap dh :execute StageDiff('Gsdiff') - nnoremap ds :execute StageDiff('Gsdiff') - nnoremap dp :execute StageDiffEdit() - nnoremap dv :execute StageDiff('Gvdiff') - nnoremap p :execute StagePatch(line('.'),line('.')+v:count1-1) - xnoremap p :execute StagePatch(line("'<"),line("'>")) - nnoremap P :execute StagePatch(line('.'),line('.')+v:count1-1) - xnoremap P :execute StagePatch(line("'<"),line("'>")) - nnoremap q :if bufnr('$') == 1quitelsebdeleteendif - nnoremap r :edit - nnoremap R :edit - nnoremap U :execute StageUndo() - nnoremap g? :help fugitive-:Gstatus - nnoremap :help fugitive-:Gstatus - catch /^fugitive:/ - return 'echoerr v:errmsg' - endtry -endfunction - -function! fugitive#FileRead() abort - let [dir, rev] = s:DirRev(expand('')) - if empty(dir) - return "noautocmd '[read " - endif - if rev !~# ':' - let cmd = fugitive#Prepare(dir, 'log', '--pretty=format:%B', '-1', rev) - else - let cmd = fugitive#Prepare(dir, 'cat-file', '-p', rev) - endif - return "'[read !" . escape(cmd, '!#%') -endfunction - -function! fugitive#BufReadIndex() abort - try - let b:fugitive_type = 'blob' - let b:git_dir = s:repo().dir() - try - call s:ReplaceCmd(s:repo().git_command('cat-file','blob',s:buffer().sha1())) - finally - if &bufhidden ==# '' - setlocal bufhidden=delete - endif - setlocal noswapfile - endtry - return '' - catch /^fugitive: rev-parse/ - silent exe 'doau BufNewFile '.s:fnameescape(expand('%:p')) - return '' - catch /^fugitive:/ - return 'echoerr v:errmsg' - endtry -endfunction - -function! fugitive#BufWriteIndex() abort - let tmp = tempname() - try - let [dir, commit, file] = s:DirCommitFile(expand('')) - let path = file[1:-1] - silent execute 'write !'.s:repo().git_command('hash-object','-w','--stdin').' > '.tmp - let sha1 = readfile(tmp)[0] - let old_mode = matchstr(s:repo().git_chomp('ls-files','--stage',path),'^\d\+') - if old_mode == '' - let old_mode = executable(s:repo().tree(path)) ? '100755' : '100644' - endif - let info = old_mode.' '.sha1.' '.commit[-1:-1]."\t".path - call writefile([info],tmp) - if s:winshell() - let error = system('type '.s:gsub(tmp,'/','\\').'|'.s:repo().git_command('update-index','--index-info')) - else - let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp) - endif - if v:shell_error == 0 - setlocal nomodified - if exists('#BufWritePost') - execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p')) - endif - call fugitive#ReloadStatus() - return '' - else - return 'echoerr '.string('fugitive: '.error) - endif - finally - call delete(tmp) - endtry -endfunction - -function! fugitive#BufReadObject() abort - try - setlocal noro ma - let b:git_dir = s:repo().dir() - let hash = s:buffer().sha1() - if !exists("b:fugitive_type") - let b:fugitive_type = s:repo().git_chomp('cat-file','-t',hash) - endif - if b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$' - return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'") - endif - if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob' - let b:fugitive_display_format = +getbufvar('#','fugitive_display_format') - endif - - if b:fugitive_type !=# 'blob' - setlocal nomodeline - endif - - let pos = getpos('.') - silent keepjumps %delete_ - setlocal endofline - - try - if b:fugitive_type ==# 'tree' - let b:fugitive_display_format = b:fugitive_display_format % 2 - if b:fugitive_display_format - call s:ReplaceCmd(s:repo().git_command('ls-tree',hash)) - else - call s:ReplaceCmd(s:repo().git_command('show','--no-color',hash)) - endif - elseif b:fugitive_type ==# 'tag' - let b:fugitive_display_format = b:fugitive_display_format % 2 - if b:fugitive_display_format - call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) - else - call s:ReplaceCmd(s:repo().git_command('cat-file','-p',hash)) - endif - elseif b:fugitive_type ==# 'commit' - let b:fugitive_display_format = b:fugitive_display_format % 2 - if b:fugitive_display_format - call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) - else - call s:ReplaceCmd(s:repo().git_command('show','--no-color','--pretty=format:tree%x20%T%nparent%x20%P%nauthor%x20%an%x20<%ae>%x20%ad%ncommitter%x20%cn%x20<%ce>%x20%cd%nencoding%x20%e%n%n%s%n%n%b',hash)) - keepjumps call search('^parent ') - if getline('.') ==# 'parent ' - silent keepjumps delete_ - else - silent exe 'keepjumps s/\m\C\%(^parent\)\@\)\=$','W',line('.')+3) - if lnum - silent keepjumps delete_ - end - silent keepjumps 1,/^diff --git\|\%$/g/\r$/s/// - keepjumps 1 - endif - elseif b:fugitive_type ==# 'blob' - call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) - setlocal nomodeline - endif - finally - keepjumps call setpos('.',pos) - setlocal ro noma nomod noswapfile - if &bufhidden ==# '' - setlocal bufhidden=delete - endif - if b:fugitive_type !=# 'blob' - setlocal filetype=git foldmethod=syntax - nnoremap a :let b:fugitive_display_format += v:count1exe fugitive#BufReadObject() - nnoremap i :let b:fugitive_display_format -= v:count1exe fugitive#BufReadObject() - else - call fugitive#MapJumps() - endif - endtry - - return '' - catch /^fugitive: rev-parse/ - return '' - catch /^fugitive:/ - return 'echoerr v:errmsg' - endtry -endfunction - -" Section: Temp files - -if !exists('s:temp_files') - let s:temp_files = {} -endif - -augroup fugitive_temp - autocmd! - autocmd BufNewFile,BufReadPost * - \ if has_key(s:temp_files,s:cpath(expand(':p'))) | - \ let b:git_dir = s:temp_files[s:cpath(expand(':p'))].dir | - \ call extend(b:, {'fugitive_type': 'temp'}, 'keep') | - \ let b:git_args = s:temp_files[s:cpath(expand(':p'))].args | - \ call FugitiveDetect(expand(':p')) | - \ setlocal bufhidden=delete nobuflisted | - \ nnoremap q :bdelete| - \ endif -augroup END - " Section: Go to file nnoremap : :=v:count ? v:count : '' function! fugitive#MapCfile(...) abort - cnoremap fugitive#Cfile() + exe 'cnoremap ' (a:0 ? a:1 : 'fugitive#Cfile()') + let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|sil! exe "cunmap "' if !exists('g:fugitive_no_maps') - call s:map('n', 'gf', ':find ', '') - call s:map('n', 'f', ':sfind ', '') - call s:map('n', '', ':sfind ', '') - call s:map('n', 'gf', ':tabfind ', '') - call s:map('c', '', '', '') + call s:map('n', 'gf', ':find ', '', 1) + call s:map('n', 'f', ':sfind ', '', 1) + call s:map('n', '', ':sfind ', '', 1) + call s:map('n', 'gf', ':tabfind ', '', 1) + call s:map('c', '', '', '', 1) endif endfunction +function! s:ContainingCommit() abort + let commit = s:DirCommitFile(@%)[1] + if commit =~# '^\d\=$' + return 'HEAD' + else + return commit + endif +endfunction + +function! s:NavigateUp(count) abort + let rev = s:buffer().rev() + let c = a:count + while c + if rev =~# ':.*/.' + let rev = matchstr(rev, '.*\ze/.\+', '') + elseif rev =~# '.:.' + let rev = matchstr(rev, '^.[^:]*:') + elseif rev =~# '^:' + let rev = 'HEAD^{}' + elseif rev =~# ':$' + let rev = rev[0:-2] + else + return rev.'~'.c + endif + let c -= 1 + endwhile + return rev +endfunction + function! fugitive#MapJumps(...) abort - nnoremap :exe GF("edit") + if get(b:, 'fugitive_type', '') ==# 'blob' + nnoremap :.Gblame + else + nnoremap :exe GF("edit") + endif if !&modifiable - nnoremap o :exe GF("split") - nnoremap S :exe GF("vsplit") - nnoremap O :exe GF("tabedit") - nnoremap - :exe Edit('edit',0,'',buffer().up(v:count1)) if fugitive#buffer().type('tree')call search('^'.escape(expand('#:t'),'.*[]~\').'/\=$','wc')endif - nnoremap P :exe Edit('edit',0,'',buffer().commit().'^'.v:count1.buffer().relative(':')) - nnoremap ~ :exe Edit('edit',0,'',buffer().commit().'~'.v:count1.buffer().relative(':')) - nnoremap C :exe Edit('edit',0,'',buffer().containing_commit()) - nnoremap cc :exe Edit('edit',0,'',buffer().containing_commit()) - nnoremap co :exe Edit('split',0,'',buffer().containing_commit()) - nnoremap cS :exe Edit('vsplit',0,'',buffer().containing_commit()) - nnoremap cO :exe Edit('tabedit',0,'',buffer().containing_commit()) - nnoremap cP :exe Edit('pedit',0,'',buffer().containing_commit()) + if get(b:, 'fugitive_type', '') ==# 'blob' + nnoremap o :.,.+1Gblame + nnoremap S :vertical .,.+1Gblame + nnoremap O :tab .,.+1Gblame + nnoremap p :.,.+2Gblame + else + nnoremap o :exe GF("split") + nnoremap S :exe GF("vsplit") + nnoremap O :exe GF("tabedit") + nnoremap p :exe GF("pedit") + endif + nnoremap - :exe 'Gedit ' . fnameescape(NavigateUp(v:count1)) if getline(1) =~# '^tree \x\{40\}$' && empty(getline(2))call search('^'.escape(expand('#:t'),'.*[]~\').'/\=$','wc')endif + nnoremap P :exe 'Gedit ' . fnameescape(ContainingCommit().'^'.v:count1.Relative(':')) + nnoremap ~ :exe 'Gedit ' . fnameescape(ContainingCommit().'~'.v:count1.Relative(':')) + nnoremap C :exe 'Gedit ' . fnameescape(ContainingCommit()) + nnoremap cc :exe 'Gedit ' . fnameescape(ContainingCommit()) + nnoremap co :exe 'Gsplit ' . fnameescape(ContainingCommit()) + nnoremap cS :exe 'Gvsplit ' . fnameescape(ContainingCommit()) + nnoremap cO :exe 'Gtabedit ' . fnameescape(ContainingCommit()) + nnoremap cp :exe 'Gpedit ' . fnameescape(ContainingCommit()) nnoremap . : =fnameescape(recall()) endif endfunction +function! s:StatusCfile(...) abort + let pre = '' + if getline('.') =~# '^.\=\trenamed:.* -> ' + return '/'.matchstr(getline('.'),' -> \zs.*') + elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.' + return '/'.matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$') + elseif getline('.') =~# '^.\=\t.' + return '/'.matchstr(getline('.'),'\t\zs.*') + elseif getline('.') =~# ': needs merge$' + return '/'.matchstr(getline('.'),'.*\ze: needs merge$') + elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$' + return 'HEAD' + elseif getline('.') =~# '^\%(. \)\=On branch ' + return 'refs/heads/'.getline('.')[12:] + elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'" + return matchstr(getline('.'),"'\\zs\\S\\+\\ze'") + else + return '' + endif +endfunction + +function! fugitive#StatusCfile() abort + let file = s:StatusCfile() + return empty(file) ? "\\" : s:fnameescape(s:Generate(file)) +endfunction + function! s:cfile() abort try - let buffer = s:buffer() - let myhash = buffer.sha1() - if myhash ==# '' && getline(1) =~# '^\%(commit\|tag\) \w' + let myhash = s:DirRev(@%)[1] + if len(myhash) + try + let myhash = fugitive#RevParse(myhash) + catch /^fugitive:/ + let myhash = '' + endtry + endif + if empty(myhash) && getline(1) =~# '^\%(commit\|tag\) \w' let myhash = matchstr(getline(1),'^\w\+ \zs\S\+') endif - if buffer.type('tree') - let showtree = (getline(1) =~# '^tree ' && getline(2) == "") - if showtree && line('.') > 2 - return [buffer.commit().':'.s:buffer().relative().(buffer.relative() =~# '^$\|/$' ? '' : '/').s:sub(getline('.'),'/$','')] - elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t' - return [buffer.commit().':'.s:buffer().relative().(buffer.relative() =~# '^$\|/$' ? '' : '/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')] - endif + let showtree = (getline(1) =~# '^tree ' && getline(2) == "") - elseif buffer.type('blob') - let ref = expand("") - try - let sha1 = buffer.repo().rev_parse(ref) - catch /^fugitive:/ - endtry - if exists('sha1') - return [ref] - endif + let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' . + \ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/') + + if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t' + return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')] + elseif showtree + return [treebase . s:sub(getline('.'),'/$','')] else @@ -3023,32 +3204,8 @@ function! s:cfile() abort let ref = matchstr(getline('.'),'\x\{40\}') let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':') return [file] - - elseif getline('.') =~# '^.\=\trenamed:.* -> ' - let file = '/'.matchstr(getline('.'),' -> \zs.*') - return [file] - elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.' - let file = '/'.matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$') - return [file] - elseif getline('.') =~# '^.\=\t.' - let file = '/'.matchstr(getline('.'),'\t\zs.*') - return [file] - elseif getline('.') =~# ': needs merge$' - let file = '/'.matchstr(getline('.'),'.*\ze: needs merge$') - return [file, 'Gdiff!'] - - elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$' - return ['HEAD'] - elseif getline('.') =~# '^\%(. \)\=On branch ' - let file = 'refs/heads/'.getline('.')[12:] - return [file] - elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'" - let file = matchstr(getline('.'),"'\\zs\\S\\+\\ze'") - return [file] endif - let showtree = (getline(1) =~# '^tree ' && getline(2) == "") - if getline('.') =~# '^ref: ' let ref = strpart(getline('.'),5) @@ -3066,9 +3223,9 @@ function! s:cfile() abort endwhile return [ref] - elseif getline('.') =~ '^tree \x\{40\}$' + elseif getline('.') =~# '^tree \x\{40\}$' let ref = matchstr(getline('.'),'\x\{40\}') - if s:repo().rev_parse(myhash.':') == ref + if len(myhash) && fugitive#RevParse(myhash.':') ==# ref let ref = myhash.':' endif return [ref] @@ -3078,7 +3235,7 @@ function! s:cfile() abort let type = matchstr(getline(line('.')+1),'type \zs.*') elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$' - let ref = buffer.rev() + let ref = s:buffer().rev() elseif getline('.') =~# '^\l\{3,8\} \x\{40\}\>' let ref = matchstr(getline('.'),'\x\{40\}') @@ -3170,12 +3327,16 @@ endfunction function! s:GF(mode) abort try - let results = s:cfile() + let results = &filetype ==# 'gitcommit' ? [s:StatusCfile()] : s:cfile() catch /^fugitive:/ return 'echoerr v:errmsg' endtry - if len(results) - return s:Edit(a:mode, 0, '', results[0]).join(map(results[1:-1], '"|".v:val'), '') + if len(results) > 1 + return 'G' . a:mode . + \ ' +' . escape(join(results[1:-1], '|'), '| ') . ' ' . + \ s:fnameescape(results[0]) + elseif len(results) + return 'G' . a:mode . ' ' . s:fnameescape(results[0]) else return '' endif @@ -3193,38 +3354,22 @@ function! fugitive#Cfile() abort elseif len(results) > 1 let pre = '+' . join(map(results[1:-1], 'escape(v:val, " ")'), '\|') . ' ' endif - return pre . s:fnameescape(fugitive#repo().translate(results[0])) -endfunction - -function! fugitive#cfile() abort - return fugitive#Cfile() + return pre . s:fnameescape(s:Generate(results[0])) endfunction " Section: Statusline -function! s:repo_head_ref() dict abort - if !filereadable(self.dir('HEAD')) - return '' - endif - return readfile(self.dir('HEAD'))[0] -endfunction - -call s:add_methods('repo',['head_ref']) - function! fugitive#Statusline(...) abort if !exists('b:git_dir') return '' endif let status = '' - if s:buffer().commit() != '' - let status .= ':' . s:buffer().commit()[0:7] + let commit = s:DirCommitFile(@%)[1] + if len(commit) + let status .= ':' . commit[0:7] endif let status .= '('.FugitiveHead(7).')' - if &statusline =~# '%[MRHWY]' && &statusline !~# '%[mrhwy]' - return ',GIT'.status - else - return '[Git'.status.']' - endif + return '[Git'.status.']' endfunction function! fugitive#statusline(...) abort @@ -3236,7 +3381,7 @@ function! fugitive#head(...) abort return '' endif - return s:repo().head(a:0 ? a:1 : 0) + return fugitive#repo().head(a:0 ? a:1 : 0) endfunction " Section: Folding @@ -3295,3 +3440,50 @@ augroup fugitive_folding \ set foldtext=fugitive#Foldtext() | \ endif augroup END + +" Section: Initialization + +function! fugitive#Init() abort + if exists('#User#FugitiveBoot') + try + let [save_mls, &modelines] = [&mls, 0] + doautocmd User FugitiveBoot + finally + let &mls = save_mls + endtry + endif + if !exists('g:fugitive_no_maps') + call s:map('c', '', 'fnameescape(recall())', '') + call s:map('n', 'y', ':call setreg(v:register, recall())', '') + endif + if expand('%:p') =~# ':[\/][\/]' + let &l:path = s:sub(&path, '^\.%(,|$)', '') + endif + if stridx(&tags, escape(b:git_dir, ', ')) == -1 + if filereadable(b:git_dir.'/tags') + let &l:tags = escape(b:git_dir.'/tags', ', ').','.&tags + endif + if &filetype !=# '' && filereadable(b:git_dir.'/'.&filetype.'.tags') + let &l:tags = escape(b:git_dir.'/'.&filetype.'.tags', ', ').','.&tags + endif + endif + try + let [save_mls, &modelines] = [&mls, 0] + call s:define_commands() + doautocmd User Fugitive + finally + let &mls = save_mls + endtry +endfunction + +function! fugitive#is_git_dir(path) abort + return FugitiveIsGitDir(a:path) +endfunction + +function! fugitive#extract_git_dir(path) abort + return FugitiveExtractGitDir(a:path) +endfunction + +function! fugitive#detect(path) abort + return FugitiveDetect(a:path) +endfunction diff --git a/sources_non_forked/vim-fugitive/doc/fugitive.txt b/sources_non_forked/vim-fugitive/doc/fugitive.txt index 2e06d374..8ab30b60 100644 --- a/sources_non_forked/vim-fugitive/doc/fugitive.txt +++ b/sources_non_forked/vim-fugitive/doc/fugitive.txt @@ -199,11 +199,7 @@ that are part of Git repositories). *fugitive-:Gmove* :Gmove {destination} Wrapper around git-mv that renames the buffer - afterward. The destination is relative to the current - 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. + afterward. Add a ! to pass -f. *fugitive-:Grename* :Grename {destination} Like |:Gmove| but operates relative to the parent @@ -234,12 +230,11 @@ that are part of Git repositories). q, then open commit o open commit in horizontal split O open commit in new tab + p open commit in preview window - reblame at commit ~ reblame at [count]th first grandparent P reblame at [count]th parent (like HEAD^[count]) -:[range]Gblame [flags] Run git-blame on the given range. - *fugitive-:Gbrowse* :Gbrowse Open the current file, blob, tree, commit, or tag 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 . - 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 useful value here is -, which ties the URL to the latest commit rather than a volatile branch. @@ -334,19 +323,19 @@ refs/heads/x .git/refs/heads/x @ The commit referenced by @ aka HEAD master^ The parent of the commit 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 parent of HEAD :Makefile The file named Makefile in the index (writable) +@:% The current file in HEAD - The current file in HEAD ^ The current file in the previous commit ~3 The current file 3 commits ago : .git/index (Same as |:Gstatus|) -:0 The current file in the index -:1 The current file's common ancestor 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 -:/foo The most recent commit with "foo" in the message +:% The current file in the index +:1:% The current file's common ancestor 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 STATUSLINE *fugitive-statusline* diff --git a/sources_non_forked/vim-fugitive/plugin/fugitive.vim b/sources_non_forked/vim-fugitive/plugin/fugitive.vim index a916c9e3..b0e1f7e6 100644 --- a/sources_non_forked/vim-fugitive/plugin/fugitive.vim +++ b/sources_non_forked/vim-fugitive/plugin/fugitive.vim @@ -16,6 +16,18 @@ function! s:shellslash(path) abort endif 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 let path = substitute(a:path, '[\/]$', '', '') . '/' return getfsize(path.'HEAD') > 10 && ( @@ -25,8 +37,8 @@ endfunction let s:worktree_for_dir = {} let s:dir_for_worktree = {} -function! FugitiveTreeForGitDir(git_dir) abort - let dir = substitute(s:shellslash(a:git_dir), '/$', '', '') +function! FugitiveWorkTree(...) abort + let dir = substitute(s:shellslash(a:0 ? a:1 : get(b:, 'git_dir', '')), '/$', '', '') if dir =~# '/\.git$' return len(dir) ==# 5 ? '/' : dir[0:-6] endif @@ -57,6 +69,73 @@ function! FugitiveTreeForGitDir(git_dir) abort endif 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 let path = s:shellslash(a:path) if path =~# '^fugitive:' @@ -66,6 +145,10 @@ function! FugitiveExtractGitDir(path) abort else let path = fnamemodify(path, ':p:h:s?/$??') 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) if root !=# path silent! exe haslocaldir() ? 'lcd .' : 'cd .' @@ -82,7 +165,7 @@ function! FugitiveExtractGitDir(path) abort return simplify(fnamemodify($GIT_DIR, ':p:s?[\/]$??')) endif 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) return s:dir_for_worktree[root] endif @@ -124,66 +207,50 @@ function! FugitiveDetect(path) abort endif 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 autocmd! - autocmd BufNewFile,BufReadPost * call FugitiveDetect(expand('%:p')) - autocmd FileType netrw call FugitiveDetect(fnamemodify(get(b:, 'netrw_curdir', @%), ':p')) + autocmd BufNewFile,BufReadPost * call FugitiveDetect(expand(':p')) + autocmd FileType netrw call FugitiveDetect(fnamemodify(get(b:, 'netrw_curdir', expand('')), ':p')) autocmd User NERDTreeInit,NERDTreeNewRoot \ if exists('b:NERDTree.root.path.str') | \ call FugitiveDetect(b:NERDTree.root.path.str()) | \ endif - autocmd VimEnter * if expand('')==''|call FugitiveDetect(getcwd())|endif + autocmd VimEnter * if empty(expand(''))|call FugitiveDetect(getcwd())|endif autocmd CmdWinEnter * call FugitiveDetect(expand('#:p')) autocmd FileType git \ if exists('b:git_dir') | - \ call fugitive#MapJumps() | - \ endif - autocmd FileType git,gitcommit,gitrebase - \ if exists('b:git_dir') | + \ call fugitive#MapJumps() | \ call fugitive#MapCfile() | \ 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(':p:h')) | + \ let b:git_dir = s:shellslash(expand(':p:h')) | \ exe fugitive#BufReadStatus() | \ elseif filereadable(expand('')) | \ read | - \ 1delete | + \ 1delete_ | \ endif - autocmd FileReadCmd fugitive://**//[0-3]/** exe fugitive#FileRead() - autocmd BufReadCmd fugitive://**//[0-3]/** exe fugitive#BufReadIndex() - autocmd BufWriteCmd fugitive://**//[0-3]/** exe fugitive#BufWriteIndex() - autocmd BufReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe fugitive#BufReadObject() - autocmd FileReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe fugitive#FileRead() + autocmd BufReadCmd fugitive://*//* exe fugitive#BufReadCmd() + autocmd BufWriteCmd fugitive://*//[0-3]/* exe fugitive#BufWriteCmd() + autocmd FileReadCmd fugitive://*//* exe fugitive#FileReadCmd() + autocmd FileWriteCmd fugitive://*//[0-3]/* exe fugitive#FileWriteCmd() + if exists('##SourceCmd') + autocmd SourceCmd fugitive://*//* nested exe fugitive#SourceCmd() + endif autocmd User Flags call Hoist('buffer', function('FugitiveStatusline')) augroup END diff --git a/sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim b/sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim index 9441f7a4..b2841326 100644 --- a/sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim +++ b/sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim @@ -100,7 +100,7 @@ function! gitgutter#diff#run_diff(bufnr, preserve_full_diff) abort call s:write_buffer(a:bufnr, buff_file) " 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 let cmd .= ' -c "diff.autorefreshindex=0"' let cmd .= ' -c "diff.noprefix=false"' diff --git a/sources_non_forked/vim-go/.coveragerc b/sources_non_forked/vim-go/.coveragerc index 9f46ed7f..8cddf91c 100644 --- a/sources_non_forked/vim-go/.coveragerc +++ b/sources_non_forked/vim-go/.coveragerc @@ -1,3 +1,3 @@ [run] plugins = covimerage -data_file = .coverage.covimerage +data_file = .coverage_covimerage diff --git a/sources_non_forked/vim-go/CHANGELOG.md b/sources_non_forked/vim-go/CHANGELOG.md index b0284d11..fde19880 100644 --- a/sources_non_forked/vim-go/CHANGELOG.md +++ b/sources_non_forked/vim-go/CHANGELOG.md @@ -3,6 +3,12 @@ IMPROVEMENTS: * Unify async job handling for Vim8 and Neovim. [[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) diff --git a/sources_non_forked/vim-go/README.md b/sources_non_forked/vim-go/README.md index 8bd9e4f0..0e74a031 100644 --- a/sources_non_forked/vim-go/README.md +++ b/sources_non_forked/vim-go/README.md @@ -33,10 +33,13 @@ This plugin adds Go language support for Vim, with the following main features: ## 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 recommended version to use. If you choose to use the master branch instead, please do so with caution; it is a _development_ branch. + vim-go follows the standard runtime path structure. Below are some helper lines 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` * [vim-plug](https://github.com/junegunn/vim-plug) * `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 to install all of them by providing a command, `:GoInstallBinaries`, which will diff --git a/sources_non_forked/vim-go/autoload/go/cmd.vim b/sources_non_forked/vim-go/autoload/go/cmd.vim index cccc001f..05a39cd8 100644 --- a/sources_non_forked/vim-go/autoload/go/cmd.vim +++ b/sources_non_forked/vim-go/autoload/go/cmd.vim @@ -143,7 +143,12 @@ function! go#cmd#Run(bang, ...) abort endif 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 redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None else diff --git a/sources_non_forked/vim-go/autoload/go/job.vim b/sources_non_forked/vim-go/autoload/go/job.vim index 9d88292c..af139d39 100644 --- a/sources_non_forked/vim-go/autoload/go/job.vim +++ b/sources_non_forked/vim-go/autoload/go/job.vim @@ -7,15 +7,6 @@ function! go#job#Spawn(cmd, args) return go#job#Start(a:cmd, l:options) endfunction -" Spawn starts an asynchronous job. See the description of go#job#Options to -" understand the args parameter. -" -" Spawn returns a job. -function! go#job#Spawn(cmd, args) - let l:options = go#job#Options(a:args) - return go#job#Start(a:cmd, l:options) -endfunction - " Options returns callbacks to be used with job_start. It is abstracted to be " used with various go commands, such as build, test, install, etc.. This " allows us to avoid writing the same callback over and over for some diff --git a/sources_non_forked/vim-go/doc/vim-go.txt b/sources_non_forked/vim-go/doc/vim-go.txt index 4d7f897d..aadaed36 100644 --- a/sources_non_forked/vim-go/doc/vim-go.txt +++ b/sources_non_forked/vim-go/doc/vim-go.txt @@ -76,6 +76,12 @@ tools developed by the Go community to provide a seamless Vim experience. ============================================================================== 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 recommended version to use. If you choose to use the master branch instead, please do so with caution; it is a _development_ branch. diff --git a/sources_non_forked/vim-markdown/.travis.yml b/sources_non_forked/vim-markdown/.travis.yml index 310e5034..338161ca 100644 --- a/sources_non_forked/vim-markdown/.travis.yml +++ b/sources_non_forked/vim-markdown/.travis.yml @@ -26,7 +26,6 @@ before_script: | fi if [ "$TRAVIS_OS_NAME" = "osx" ]; then curl https://bootstrap.pypa.io/get-pip.py | sudo python - sudo -H easy_install pip fi sudo -H pip install virtualenv diff --git a/sources_non_forked/vim-markdown/README.md b/sources_non_forked/vim-markdown/README.md index 8c056ce0..982248e2 100644 --- a/sources_non_forked/vim-markdown/README.md +++ b/sources_non_forked/vim-markdown/README.md @@ -5,6 +5,7 @@ Syntax highlighting, matching rules and mappings for [the original Markdown](http://daringfireball.net/projects/markdown/) and extensions. 1. [Installation](#installation) +1. [Basic usage](#basic-usage) 1. [Options](#options) 1. [Mappings](#mappings) 1. [Commands](#commands) @@ -52,6 +53,37 @@ cd ~/.vim 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 ### 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' ``` +### 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 The following work on normal and visual modes: diff --git a/sources_non_forked/vim-markdown/doc/vim-markdown.txt b/sources_non_forked/vim-markdown/doc/vim-markdown.txt index 714d3a95..29ed66dd 100644 --- a/sources_non_forked/vim-markdown/doc/vim-markdown.txt +++ b/sources_non_forked/vim-markdown/doc/vim-markdown.txt @@ -5,7 +5,10 @@ Contents ~ 1. Introduction |vim-markdown-introduction| 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| 2. Change fold style |vim-markdown-change-fold-style| 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| 13. Auto-write when following link |vim-markdown-auto-write-when-following-link| - 14. Change default file extension |vim-markdown-auto-extension-ext| - 4. Mappings |vim-markdown-mappings| - 5. Commands |vim-markdown-commands| - 6. Credits |vim-markdown-credits| - 7. License |vim-markdown-license| - 8. References |vim-markdown-references| + 14. Change default file extension + |vim-markdown-change-default-file-extension| + 15. Do not automatically insert bulletpoints + |vim-markdown-do-not-automatically-insert-bulletpoints| + 16. Change how to open new files |vim-markdown-change-how-to-open-new-files| + 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* @@ -73,6 +80,52 @@ If you are not using any package manager, download the tarball [5] and do this: cd ~/.vim 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* Options ~ @@ -144,7 +197,7 @@ never increases its default size (half screen), it only shrinks. Text emphasis restriction to single-lines ~ 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: > 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* Follow named anchors ~ -This feature allows ge to follow named anchors in links of the form -'file#anchor' or just '#anchor', where file may omit the '.md' extension as -usual. Two variables control its operation: +This feature allows the 'ge' command to follow named anchors in links of the +form 'file#anchor' or just '#anchor', where file may omit the '.md' extension +as usual. Two variables control its operation: > 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 < ------------------------------------------------------------------------------- - *vim-markdown-auto-extension-ext* + *vim-markdown-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 @@ -319,6 +372,32 @@ the 'vim_markdown_auto_extension_ext' variable: > 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* Mappings ~ diff --git a/sources_non_forked/vim-markdown/ftplugin/markdown.vim b/sources_non_forked/vim-markdown/ftplugin/markdown.vim index 1aa3494f..68d1d3d9 100644 --- a/sources_non_forked/vim-markdown/ftplugin/markdown.vim +++ b/sources_non_forked/vim-markdown/ftplugin/markdown.vim @@ -609,7 +609,23 @@ if !exists('*s:EditUrlUnderCursor') endif endif 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 if l:anchor != '' silent! execute '/'.l:anchor diff --git a/sources_non_forked/vim-markdown/indent/markdown.vim b/sources_non_forked/vim-markdown/indent/markdown.vim index 103e160f..2fa4cda8 100644 --- a/sources_non_forked/vim-markdown/indent/markdown.vim +++ b/sources_non_forked/vim-markdown/indent/markdown.vim @@ -5,15 +5,15 @@ setlocal indentexpr=GetMarkdownIndent() setlocal nolisp 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 -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 if exists("*GetMarkdownIndent") | finish | endif diff --git a/sources_non_forked/vim-markdown/syntax/markdown.vim b/sources_non_forked/vim-markdown/syntax/markdown.vim index 008a6d83..0bffaa2b 100644 --- a/sources_non_forked/vim-markdown/syntax/markdown.vim +++ b/sources_non_forked/vim-markdown/syntax/markdown.vim @@ -82,25 +82,25 @@ syn region mkdLinkTitle matchgroup=mkdDelimiter start=+'+ end=+'+ contained syn region mkdLinkTitle matchgroup=mkdDelimiter start=+(+ end=+)+ contained "HTML headings -syn region htmlH1 start="^\s*#" end="$" contains=mkdLink,mkdInlineURL,@Spell -syn region htmlH2 start="^\s*##" end="$" contains=mkdLink,mkdInlineURL,@Spell -syn region htmlH3 start="^\s*###" end="$" contains=mkdLink,mkdInlineURL,@Spell -syn region htmlH4 start="^\s*####" end="$" contains=mkdLink,mkdInlineURL,@Spell -syn region htmlH5 start="^\s*#####" end="$" contains=mkdLink,mkdInlineURL,@Spell -syn region htmlH6 start="^\s*######" end="$" contains=mkdLink,mkdInlineURL,@Spell +syn region htmlH1 matchgroup=mkdHeading start="^\s*#" end="$" contains=mkdLink,mkdInlineURL,@Spell +syn region htmlH2 matchgroup=mkdHeading start="^\s*##" end="$" contains=mkdLink,mkdInlineURL,@Spell +syn region htmlH3 matchgroup=mkdHeading start="^\s*###" end="$" contains=mkdLink,mkdInlineURL,@Spell +syn region htmlH4 matchgroup=mkdHeading start="^\s*####" end="$" contains=mkdLink,mkdInlineURL,@Spell +syn region htmlH5 matchgroup=mkdHeading 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 htmlH2 /^.\+\n-\+$/ contains=mkdLink,mkdInlineURL,@Spell "define Markdown groups syn match mkdLineBreak / \+$/ syn region mkdBlockquote start=/^\s*>/ end=/$/ contains=mkdLink,mkdInlineURL,mkdLineBreak,@Spell -syn region mkdCode start=/\(\([^\\]\|^\)\\\)\@]*\\\@" end="" -syn region mkdCode start="]*\\\@" end="" +execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/\(\([^\\]\|^\)\\\)\@]*\\\@" end=""' . s:concealends +execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start="]*\\\@" end=""' . s:concealends syn region mkdFootnote start="\[^" end="\]" syn match mkdCode /^\s*\n\(\(\s\{8,}[^ ]\|\t\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 if get(g:, 'vim_markdown_frontmatter', 0) 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 endif @@ -150,6 +150,7 @@ syn cluster mkdNonListItem contains=@htmlTop,htmlItalic,htmlBold,htmlBoldItalic, "highlighting for Markdown groups HtmlHiLink mkdString String HtmlHiLink mkdCode String +HtmlHiLink mkdCodeDelimiter String HtmlHiLink mkdCodeStart String HtmlHiLink mkdCodeEnd String HtmlHiLink mkdFootnote Comment diff --git a/sources_non_forked/vim-markdown/test/syntax.vader b/sources_non_forked/vim-markdown/test/syntax.vader index 0adae58a..fdf9c0be 100644 --- a/sources_non_forked/vim-markdown/test/syntax.vader +++ b/sources_non_forked/vim-markdown/test/syntax.vader @@ -1139,19 +1139,19 @@ Given markdown; ###### h6 Execute (atx headers): - AssertEqual SyntaxOf('# h1 space'), 'htmlH1' - AssertEqual SyntaxOf('#h1 nospace'), 'htmlH1' - AssertEqual SyntaxOf('# h1 2 spaces'), 'htmlH1' - AssertEqual SyntaxOf('# h1 trailing hash #'), 'htmlH1' - AssertEqual SyntaxOf('## h2 space'), 'htmlH2' - AssertEqual SyntaxOf('##h2 nospace'), 'htmlH2' - AssertEqual SyntaxOf('## h2 trailing hash ##'), 'htmlH2' - AssertEqual SyntaxOf('### h3 space'), 'htmlH3' - AssertEqual SyntaxOf('###h3 nospace'), 'htmlH3' - AssertEqual SyntaxOf('### h3 trailing hash ###'), 'htmlH3' - AssertEqual SyntaxOf('#### h4'), 'htmlH4' - AssertEqual SyntaxOf('##### h5'), 'htmlH5' - AssertEqual SyntaxOf('###### h6'), 'htmlH6' + AssertEqual SyntaxOf(' h1 space'), 'htmlH1' + AssertEqual SyntaxOf('h1 nospace'), 'htmlH1' + AssertEqual SyntaxOf(' h1 2 spaces'), 'htmlH1' + AssertEqual SyntaxOf(' h1 trailing hash '), 'htmlH1' + AssertEqual SyntaxOf(' h2 space'), 'htmlH2' + AssertEqual SyntaxOf('h2 nospace'), 'htmlH2' + AssertEqual SyntaxOf(' h2 trailing hash '), 'htmlH2' + AssertEqual SyntaxOf(' h3 space'), 'htmlH3' + AssertEqual SyntaxOf('h3 nospace'), 'htmlH3' + AssertEqual SyntaxOf(' h3 trailing hash '), 'htmlH3' + AssertEqual SyntaxOf(' h4'), 'htmlH4' + AssertEqual SyntaxOf(' h5'), 'htmlH5' + AssertEqual SyntaxOf(' h6'), 'htmlH6' Given markdown; # h1 before h2 @@ -1161,9 +1161,9 @@ Given markdown; # h1 after h2 Execute (atx headers relative positions): - AssertEqual SyntaxOf('# h1 before h2'), 'htmlH1' - AssertEqual SyntaxOf('## h2 between h1s'), 'htmlH2' - AssertEqual SyntaxOf('# h1 after h2'), 'htmlH1' + AssertEqual SyntaxOf(' h1 before h2'), 'htmlH1' + AssertEqual SyntaxOf(' h2 between h1s'), 'htmlH2' + AssertEqual SyntaxOf(' h1 after h2'), 'htmlH1' Given markdown; setex h1 @@ -1214,9 +1214,9 @@ setex h2 Execute (mixed atx and setex headers): AssertEqual SyntaxOf('setex h1 before atx'), 'htmlH1' AssertEqual SyntaxOf('==================='), 'htmlH1' - AssertEqual SyntaxOf('## atx h2'), 'htmlH2' - AssertEqual SyntaxOf('### atx h3'), 'htmlH3' - AssertEqual SyntaxOf('# atx h1'), 'htmlH1' + AssertEqual SyntaxOf(' atx h2'), 'htmlH2' + AssertEqual SyntaxOf(' atx h3'), 'htmlH3' + AssertEqual SyntaxOf(' atx h1'), 'htmlH1' AssertEqual SyntaxOf('setex h2'), 'htmlH2' AssertEqual SyntaxOf('------------------'), 'htmlH2' diff --git a/sources_non_forked/vim-surround/plugin/surround.vim b/sources_non_forked/vim-surround/plugin/surround.vim index 72a14424..5626f22f 100644 --- a/sources_non_forked/vim-surround/plugin/surround.vim +++ b/sources_non_forked/vim-surround/plugin/surround.vim @@ -263,11 +263,16 @@ function! s:wrap(string,char,type,removed,special) elseif keeper =~ '\n$' && after =~ '^\n' let after = strpart(after,1) endif - if before !~ '\n\s*$' + if keeper !~ '^\n' && before !~ '\n\s*$' let before .= "\n" if a:special let before .= "\t" 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 if type ==# 'V'