1
0
Fork 0
mirror of synced 2024-11-22 16:55:34 -05:00

Updated plugins

This commit is contained in:
Amir Salihefendic 2018-07-04 12:53:25 +02:00
parent 993ed55304
commit 2f164fee9b
70 changed files with 2044 additions and 761 deletions

View file

@ -0,0 +1,35 @@
" Author: Yasuhiro Kiyota <yasuhiroki.duck@gmail.com>
" Description: Support cfn-python-lint for AWS Cloudformation template file
function! ale_linters#cloudformation#cfn_python_lint#Handle(buffer, lines) abort
" Matches patterns line the following:
"
" sample.template.yaml:96:7:96:15:E3012:Property Resources/Sample/Properties/FromPort should be of type Integer
let l:pattern = '\v^(.*):(\d+):(\d+):(\d+):(\d+):([[:alnum:]]+):(.*)$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:code = l:match[6]
if ale#path#IsBufferPath(a:buffer, l:match[1])
call add(l:output, {
\ 'lnum': l:match[2],
\ 'col': l:match[3],
\ 'end_lnum': l:match[4],
\ 'end_col': l:match[5],
\ 'code': l:code,
\ 'type': l:code[:0] is# 'E' ? 'E' : 'W',
\ 'text': l:match[7]
\})
endif
endfor
return l:output
endfunction
call ale#linter#Define('cloudformation', {
\ 'name': 'cloudformation',
\ 'executable': 'cfn-lint',
\ 'command': 'cfn-lint --template %t --format parseable',
\ 'callback': 'ale_linters#cloudformation#cfn_python_lint#Handle',
\})

View file

@ -0,0 +1,61 @@
" Author: evnu - https://github.com/evnu
" Author: colbydehart - https://github.com/colbydehart
" Description: Mix compile checking for Elixir files
function! ale_linters#elixir#mix#Handle(buffer, lines) abort
" Matches patterns like the following:
"
" Error format
" ** (CompileError) apps/sim/lib/sim/server.ex:87: undefined function update_in/4
"
" TODO: Warning format
" warning: variable "foobar" does not exist and is being expanded to "foobar()", please use parentheses to remove the ambiguity or change the variable name
let l:pattern = '\v\(([^\)]+Error)\) ([^:]+):([^:]+): (.+)$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:type = 'E'
let l:text = l:match[4]
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': l:match[3] + 0,
\ 'col': 0,
\ 'type': l:type,
\ 'text': l:text,
\})
endfor
return l:output
endfunction
function! ale_linters#elixir#mix#FindProjectRoot(buffer) abort
let l:mix_file = ale#path#FindNearestFile(a:buffer, 'mix.exs')
if !empty(l:mix_file)
return fnamemodify(l:mix_file, ':p:h')
endif
return '.'
endfunction
function! ale_linters#elixir#mix#GetCommand(buffer) abort
let l:project_root = ale_linters#elixir#mix#FindProjectRoot(a:buffer)
let l:temp_dir = ale#engine#CreateDirectory(a:buffer)
let l:mix_build_path = has('win32')
\ ? 'set MIX_BUILD_PATH=' . ale#Escape(l:temp_dir) . ' &&'
\ : 'MIX_BUILD_PATH=' . ale#Escape(l:temp_dir)
return ale#path#CdString(l:project_root)
\ . l:mix_build_path
\ . ' mix compile %s'
endfunction
call ale#linter#Define('elixir', {
\ 'name': 'mix',
\ 'executable': 'mix',
\ 'command_callback': 'ale_linters#elixir#mix#GetCommand',
\ 'callback': 'ale_linters#elixir#mix#Handle',
\ 'lint_file': 1,
\})

View file

@ -1,21 +1,10 @@
" Author: KabbAmine <amine.kabb@gmail.com> " Author: KabbAmine <amine.kabb@gmail.com>
" Description: This file adds support for checking HTML code with tidy. " Description: This file adds support for checking HTML code with tidy.
" CLI options
let g:ale_html_tidy_executable = get(g:, 'ale_html_tidy_executable', 'tidy') let g:ale_html_tidy_executable = get(g:, 'ale_html_tidy_executable', 'tidy')
" remove in 2.0 let g:ale_html_tidy_options = get(g:, 'ale_html_tidy_options', '-q -e -language en')
" Look for the old _args variable first.
let s:deprecation_warning_echoed = 0
let s:default_options = get(g:, 'ale_html_tidy_args', '-q -e -language en')
let g:ale_html_tidy_options = get(g:, 'ale_html_tidy_options', s:default_options)
function! ale_linters#html#tidy#GetCommand(buffer) abort function! ale_linters#html#tidy#GetCommand(buffer) abort
" remove in 2.0
if exists('g:ale_html_tidy_args') && !s:deprecation_warning_echoed
execute 'echom ''Rename your g:ale_html_tidy_args setting to g:ale_html_tidy_options instead. Support for this will removed in ALE 2.0.'''
let s:deprecation_warning_echoed = 1
endif
" Specify file encoding in options " Specify file encoding in options
" (Idea taken from https://github.com/scrooloose/syntastic/blob/master/syntax_checkers/html/tidy.vim) " (Idea taken from https://github.com/scrooloose/syntastic/blob/master/syntax_checkers/html/tidy.vim)
let l:file_encoding = get({ let l:file_encoding = get({

View file

@ -0,0 +1,26 @@
" Author: Chaucerbao, w0rp <devw0rp@gmail.com>
" Description: tsserver integration for ALE
call ale#Set('javascript_tsserver_executable', 'tsserver')
call ale#Set('javascript_tsserver_config_path', '')
call ale#Set('javascript_tsserver_use_global', get(g:, 'ale_use_global_executables', 0))
" These functions need to be defined just to comply with the API for LSP.
function! ale_linters#javascript#tsserver#GetProjectRoot(buffer) abort
return ''
endfunction
function! ale_linters#javascript#tsserver#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_tsserver', [
\ 'node_modules/.bin/tsserver',
\])
endfunction
call ale#linter#Define('javascript', {
\ 'name': 'tsserver',
\ 'lsp': 'tsserver',
\ 'executable_callback': 'ale_linters#javascript#tsserver#GetExecutable',
\ 'command_callback': 'ale_linters#javascript#tsserver#GetExecutable',
\ 'project_root_callback': 'ale_linters#javascript#tsserver#GetProjectRoot',
\ 'language': '',
\})

View file

@ -1,10 +1,43 @@
" Author: w0rp <devw0rp@gmail.com> " Author: w0rp <devw0rp@gmail.com>,
" Nicolas Pauss <https://github.com/nicopauss>
" Description: cython syntax checking for cython files. " Description: cython syntax checking for cython files.
call ale#Set('pyrex_cython_executable', 'cython')
call ale#Set('pyrex_cython_options', '--warning-extra')
function! ale_linters#pyrex#cython#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'pyrex_cython_executable')
endfunction
function! ale_linters#pyrex#cython#GetCommand(buffer) abort
let l:local_dir = ale#Escape(fnamemodify(bufname(a:buffer), ':p:h'))
return ale#Escape(ale_linters#pyrex#cython#GetExecutable(a:buffer))
\ . ' --working ' . l:local_dir . ' --include-dir ' . l:local_dir
\ . ' ' . ale#Var(a:buffer, 'pyrex_cython_options')
\ . ' --output-file ' . g:ale#util#nul_file . ' %t'
endfunction
function! ale_linters#pyrex#cython#Handle(buffer, lines) abort
let l:pattern = '\v^(\w+: )?[^:]+:(\d+):?(\d+)?:? ?(.+)$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'lnum': l:match[2] + 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[4],
\ 'type': l:match[1][0] is# 'w' ? 'W' : 'E',
\})
endfor
return l:output
endfunction
call ale#linter#Define('pyrex', { call ale#linter#Define('pyrex', {
\ 'name': 'cython', \ 'name': 'cython',
\ 'output_stream': 'stderr', \ 'output_stream': 'stderr',
\ 'executable': 'cython', \ 'executable_callback': 'ale_linters#pyrex#cython#GetExecutable',
\ 'command': 'cython --warning-extra -o ' . g:ale#util#nul_file . ' %t', \ 'command_callback': 'ale_linters#pyrex#cython#GetCommand',
\ 'callback': 'ale#handlers#unix#HandleAsError', \ 'callback': 'ale_linters#pyrex#cython#Handle',
\}) \})

View file

@ -1,13 +1,8 @@
" Author: w0rp <devw0rp@gmail.com> " Author: w0rp <devw0rp@gmail.com>
" Description: flake8 for python files " Description: flake8 for python files
" remove in 2.0
" Support an old setting as a fallback.
let s:deprecation_warning_echoed = 0
let s:default_options = get(g:, 'ale_python_flake8_args', '')
call ale#Set('python_flake8_executable', 'flake8') call ale#Set('python_flake8_executable', 'flake8')
call ale#Set('python_flake8_options', s:default_options) call ale#Set('python_flake8_options', '')
call ale#Set('python_flake8_use_global', get(g:, 'ale_use_global_executables', 0)) call ale#Set('python_flake8_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_flake8_change_directory', 1) call ale#Set('python_flake8_change_directory', 1)
@ -40,12 +35,6 @@ function! ale_linters#python#flake8#VersionCheck(buffer) abort
endfunction endfunction
function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort
" remove in 2.0
if exists('g:ale_python_flake8_args') && !s:deprecation_warning_echoed
execute 'echom ''Rename your g:ale_python_flake8_args setting to g:ale_python_flake8_options instead. Support for this will removed in ALE 2.0.'''
let s:deprecation_warning_echoed = 1
endif
let l:cd_string = ale#Var(a:buffer, 'python_flake8_change_directory') let l:cd_string = ale#Var(a:buffer, 'python_flake8_change_directory')
\ ? ale#path#BufferCdString(a:buffer) \ ? ale#path#BufferCdString(a:buffer)
\ : '' \ : ''

View file

@ -30,6 +30,10 @@ endfunction
function! ale_linters#python#prospector#Handle(buffer, lines) abort function! ale_linters#python#prospector#Handle(buffer, lines) abort
let l:output = [] let l:output = []
if empty(a:lines)
return []
endif
let l:prospector_error = json_decode(join(a:lines, '')) let l:prospector_error = json_decode(join(a:lines, ''))
for l:error in l:prospector_error.messages for l:error in l:prospector_error.messages

View file

@ -0,0 +1,29 @@
" Author: dsifford <dereksifford@gmail.com>
" Description: A performant type-checker supporting LSP for Python 3 created by Facebook
call ale#Set('python_pyre_executable', 'pyre')
call ale#Set('python_pyre_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#python#pyre#GetExecutable(buffer) abort
return ale#python#FindExecutable(a:buffer, 'python_pyre', ['pyre'])
endfunction
function! ale_linters#python#pyre#GetCommand(buffer) abort
let l:executable = ale_linters#python#pyre#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run pyre persistent'
\ : ' persistent'
return ale#Escape(l:executable) . l:exec_args
endfunction
call ale#linter#Define('python', {
\ 'name': 'pyre',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#python#pyre#GetExecutable',
\ 'command_callback': 'ale_linters#python#pyre#GetCommand',
\ 'language': 'python',
\ 'project_root_callback': 'ale#python#FindProjectRoot',
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\})

View file

@ -4,21 +4,27 @@
call ale#Set('ruby_reek_show_context', 0) call ale#Set('ruby_reek_show_context', 0)
call ale#Set('ruby_reek_show_wiki_link', 0) call ale#Set('ruby_reek_show_wiki_link', 0)
function! ale_linters#ruby#reek#Handle(buffer, lines) abort function! ale_linters#ruby#reek#VersionCheck(buffer) abort
let l:output = [] " If we have previously stored the version number in a cache, then
" don't look it up again.
if ale#semver#HasVersion('reek')
" Returning an empty string skips this command.
return ''
endif
for l:error in ale#util#FuzzyJSONDecode(a:lines, []) return 'reek --version'
for l:location in l:error.lines endfunction
call add(l:output, {
\ 'lnum': l:location,
\ 'type': 'W',
\ 'text': s:BuildText(a:buffer, l:error),
\ 'code': l:error.smell_type,
\})
endfor
endfor
return l:output function! ale_linters#ruby#reek#GetCommand(buffer, version_output) abort
let l:version = ale#semver#GetVersion('reek', a:version_output)
" Tell reek what the filename is if the version of reek is new enough.
let l:display_name_args = ale#semver#GTE(l:version, [5, 0, 0])
\ ? ' --stdin-filename %s'
\ : ''
return 'reek -f json --no-progress --no-color'
\ . l:display_name_args
endfunction endfunction
function! s:BuildText(buffer, error) abort function! s:BuildText(buffer, error) abort
@ -37,9 +43,29 @@ function! s:BuildText(buffer, error) abort
return join(l:parts, ' ') return join(l:parts, ' ')
endfunction endfunction
function! ale_linters#ruby#reek#Handle(buffer, lines) abort
let l:output = []
for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
for l:location in l:error.lines
call add(l:output, {
\ 'lnum': l:location,
\ 'type': 'W',
\ 'text': s:BuildText(a:buffer, l:error),
\ 'code': l:error.smell_type,
\})
endfor
endfor
return l:output
endfunction
call ale#linter#Define('ruby', { call ale#linter#Define('ruby', {
\ 'name': 'reek', \ 'name': 'reek',
\ 'executable': 'reek', \ 'executable': 'reek',
\ 'command': 'reek -f json --no-progress --no-color', \ 'command_chain': [
\ {'callback': 'ale_linters#ruby#reek#VersionCheck'},
\ {'callback': 'ale_linters#ruby#reek#GetCommand'},
\ ],
\ 'callback': 'ale_linters#ruby#reek#Handle', \ 'callback': 'ale_linters#ruby#reek#Handle',
\}) \})

View file

@ -6,6 +6,7 @@ call ale#Set('rust_cargo_use_check', 1)
call ale#Set('rust_cargo_check_all_targets', 0) call ale#Set('rust_cargo_check_all_targets', 0)
call ale#Set('rust_cargo_check_examples', 0) call ale#Set('rust_cargo_check_examples', 0)
call ale#Set('rust_cargo_check_tests', 0) call ale#Set('rust_cargo_check_tests', 0)
call ale#Set('rust_cargo_avoid_whole_workspace', 1)
call ale#Set('rust_cargo_default_feature_behavior', 'default') call ale#Set('rust_cargo_default_feature_behavior', 'default')
call ale#Set('rust_cargo_include_features', '') call ale#Set('rust_cargo_include_features', '')
@ -45,6 +46,18 @@ function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort
let l:include_features = ' --features ' . ale#Escape(l:include_features) let l:include_features = ' --features ' . ale#Escape(l:include_features)
endif endif
let l:avoid_whole_workspace = ale#Var(a:buffer, 'rust_cargo_avoid_whole_workspace')
let l:nearest_cargo_prefix = ''
if l:avoid_whole_workspace
let l:nearest_cargo = ale#path#FindNearestFile(a:buffer, 'Cargo.toml')
let l:nearest_cargo_dir = fnamemodify(l:nearest_cargo, ':h')
if l:nearest_cargo_dir isnot# '.'
let l:nearest_cargo_prefix = 'cd '. ale#Escape(l:nearest_cargo_dir) .' && '
endif
endif
let l:default_feature_behavior = ale#Var(a:buffer, 'rust_cargo_default_feature_behavior') let l:default_feature_behavior = ale#Var(a:buffer, 'rust_cargo_default_feature_behavior')
if l:default_feature_behavior is# 'all' if l:default_feature_behavior is# 'all'
let l:include_features = '' let l:include_features = ''
@ -55,7 +68,7 @@ function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort
let l:default_feature = '' let l:default_feature = ''
endif endif
return 'cargo ' return l:nearest_cargo_prefix . 'cargo '
\ . (l:use_check ? 'check' : 'build') \ . (l:use_check ? 'check' : 'build')
\ . (l:use_all_targets ? ' --all-targets' : '') \ . (l:use_all_targets ? ' --all-targets' : '')
\ . (l:use_examples ? ' --examples' : '') \ . (l:use_examples ? ' --examples' : '')

View file

@ -0,0 +1,33 @@
" Author: Christian Höltje (https://docwhat.org/)
" Description: BASH Language server integration for ALE
scriptencoding utf-8
call ale#Set('sh_language_server_executable', 'bash-language-server')
call ale#Set('sh_language_server_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#sh#language_server#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'sh_language_server', [
\ 'node_modules/.bin/bash-language-server',
\])
endfunction
function! ale_linters#sh#language_server#GetCommand(buffer) abort
let l:exe = ale#Escape(ale_linters#sh#language_server#GetExecutable(a:buffer))
return l:exe . ' start'
endfunction
function! ale_linters#sh#language_server#GetProjectRoot(buffer) abort
let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git')
return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : ''
endfunction
call ale#linter#Define('sh', {
\ 'name': 'language_server',
\ 'lsp': 'stdio',
\ 'executable_callback': 'ale_linters#sh#language_server#GetExecutable',
\ 'command_callback': 'ale_linters#sh#language_server#GetCommand',
\ 'language': 'sh',
\ 'project_root_callback': 'ale_linters#sh#language_server#GetProjectRoot',
\})

View file

@ -20,21 +20,31 @@ function! ale_linters#yaml#yamllint#Handle(buffer, lines) abort
" Matches patterns line the following: " Matches patterns line the following:
" something.yaml:1:1: [warning] missing document start "---" (document-start) " something.yaml:1:1: [warning] missing document start "---" (document-start)
" something.yml:2:1: [error] syntax error: expected the node content, but found '<stream end>' " something.yml:2:1: [error] syntax error: expected the node content, but found '<stream end>'
let l:pattern = '^.*:\(\d\+\):\(\d\+\): \[\(error\|warning\)\] \(.\+\)$' let l:pattern = '\v^.*:(\d+):(\d+): \[(error|warning)\] (.+)$'
let l:output = [] let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern) for l:match in ale#util#GetMatches(a:lines, l:pattern)
let l:line = l:match[1] + 0 let l:item = {
let l:col = l:match[2] + 0 \ 'lnum': l:match[1] + 0,
let l:type = l:match[3] \ 'col': l:match[2] + 0,
let l:text = l:match[4] \ 'text': l:match[4],
\ 'type': l:match[3] is# 'error' ? 'E' : 'W',
\}
call add(l:output, { let l:code_match = matchlist(l:item.text, '\v^(.+) \(([^)]+)\)$')
\ 'lnum': l:line,
\ 'col': l:col, if !empty(l:code_match)
\ 'text': l:text, if l:code_match[2] is# 'trailing-spaces'
\ 'type': l:type is# 'error' ? 'E' : 'W', \&& !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
\}) " Skip warnings for trailing whitespace if the option is off.
continue
endif
let l:item.text = l:code_match[1]
let l:item.code = l:code_match[2]
endif
call add(l:output, l:item)
endfor endfor
return l:output return l:output

View file

@ -6,35 +6,12 @@
let g:ale_echo_msg_error_str = get(g:, 'ale_echo_msg_error_str', 'Error') let g:ale_echo_msg_error_str = get(g:, 'ale_echo_msg_error_str', 'Error')
let g:ale_echo_msg_info_str = get(g:, 'ale_echo_msg_info_str', 'Info') let g:ale_echo_msg_info_str = get(g:, 'ale_echo_msg_info_str', 'Info')
let g:ale_echo_msg_warning_str = get(g:, 'ale_echo_msg_warning_str', 'Warning') let g:ale_echo_msg_warning_str = get(g:, 'ale_echo_msg_warning_str', 'Warning')
" Ignoring linters, for disabling some, or ignoring LSP diagnostics.
let g:ale_linters_ignore = get(g:, 'ale_linters_ignore', {})
let s:lint_timer = -1 let s:lint_timer = -1
let s:queued_buffer_number = -1 let s:queued_buffer_number = -1
let s:should_lint_file_for_buffer = {} let s:should_lint_file_for_buffer = {}
let s:error_delay_ms = 1000 * 60 * 2
let s:timestamp_map = {}
" Given a key for a script variable for tracking the time to wait until
" a given function should be called, a funcref for a function to call, and
" a List of arguments, call the function and return whatever value it returns.
"
" If the function throws an exception, then the function will not be called
" for a while, and 0 will be returned instead.
function! ale#CallWithCooldown(timestamp_key, func, arglist) abort
let l:now = ale#util#ClockMilliseconds()
if l:now < get(s:timestamp_map, a:timestamp_key, -1)
return 0
endif
let s:timestamp_map[a:timestamp_key] = l:now + s:error_delay_ms
let l:return_value = call(a:func, a:arglist)
let s:timestamp_map[a:timestamp_key] = -1
return l:return_value
endfunction
" Return 1 if a file is too large for ALE to handle. " Return 1 if a file is too large for ALE to handle.
function! ale#FileTooLarge(buffer) abort function! ale#FileTooLarge(buffer) abort
@ -114,30 +91,22 @@ function! ale#Queue(delay, ...) abort
let l:linting_flag = get(a:000, 0, '') let l:linting_flag = get(a:000, 0, '')
let l:buffer = get(a:000, 1, bufnr('')) let l:buffer = get(a:000, 1, bufnr(''))
return ale#CallWithCooldown( if l:linting_flag isnot# '' && l:linting_flag isnot# 'lint_file'
\ 'dont_queue_until',
\ function('s:ALEQueueImpl'),
\ [a:delay, l:linting_flag, l:buffer],
\)
endfunction
function! s:ALEQueueImpl(delay, linting_flag, buffer) abort
if a:linting_flag isnot# '' && a:linting_flag isnot# 'lint_file'
throw "linting_flag must be either '' or 'lint_file'" throw "linting_flag must be either '' or 'lint_file'"
endif endif
if type(a:buffer) != type(0) if type(l:buffer) != type(0)
throw 'buffer_number must be a Number' throw 'buffer_number must be a Number'
endif endif
if ale#ShouldDoNothing(a:buffer) if ale#ShouldDoNothing(l:buffer)
return return
endif endif
" Remember that we want to check files for this buffer. " Remember that we want to check files for this buffer.
" We will remember this until we finally run the linters, via any event. " We will remember this until we finally run the linters, via any event.
if a:linting_flag is# 'lint_file' if l:linting_flag is# 'lint_file'
let s:should_lint_file_for_buffer[a:buffer] = 1 let s:should_lint_file_for_buffer[l:buffer] = 1
endif endif
if s:lint_timer != -1 if s:lint_timer != -1
@ -145,24 +114,24 @@ function! s:ALEQueueImpl(delay, linting_flag, buffer) abort
let s:lint_timer = -1 let s:lint_timer = -1
endif endif
let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype')) let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype'))
" Don't set up buffer data and so on if there are no linters to run. " Don't set up buffer data and so on if there are no linters to run.
if empty(l:linters) if empty(l:linters)
" If we have some previous buffer data, then stop any jobs currently " If we have some previous buffer data, then stop any jobs currently
" running and clear everything. " running and clear everything.
if has_key(g:ale_buffer_info, a:buffer) if has_key(g:ale_buffer_info, l:buffer)
call ale#engine#RunLinters(a:buffer, [], 1) call ale#engine#RunLinters(l:buffer, [], 1)
endif endif
return return
endif endif
if a:delay > 0 if a:delay > 0
let s:queued_buffer_number = a:buffer let s:queued_buffer_number = l:buffer
let s:lint_timer = timer_start(a:delay, function('ale#Lint')) let s:lint_timer = timer_start(a:delay, function('ale#Lint'))
else else
call ale#Lint(-1, a:buffer) call ale#Lint(-1, l:buffer)
endif endif
endfunction endfunction
@ -178,30 +147,29 @@ function! ale#Lint(...) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
endif endif
return ale#CallWithCooldown( if ale#ShouldDoNothing(l:buffer)
\ 'dont_lint_until',
\ function('s:ALELintImpl'),
\ [l:buffer],
\)
endfunction
function! s:ALELintImpl(buffer) abort
if ale#ShouldDoNothing(a:buffer)
return return
endif endif
" Use the filetype from the buffer " Use the filetype from the buffer
let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype')) let l:filetype = getbufvar(l:buffer, '&filetype')
let l:linters = ale#linter#Get(l:filetype)
let l:should_lint_file = 0 let l:should_lint_file = 0
" Check if we previously requested checking the file. " Check if we previously requested checking the file.
if has_key(s:should_lint_file_for_buffer, a:buffer) if has_key(s:should_lint_file_for_buffer, l:buffer)
unlet s:should_lint_file_for_buffer[a:buffer] unlet s:should_lint_file_for_buffer[l:buffer]
" Lint files if they exist. " Lint files if they exist.
let l:should_lint_file = filereadable(expand('#' . a:buffer . ':p')) let l:should_lint_file = filereadable(expand('#' . l:buffer . ':p'))
endif endif
call ale#engine#RunLinters(a:buffer, l:linters, l:should_lint_file) " Apply ignore lists for linters only if needed.
let l:ignore_config = ale#Var(l:buffer, 'linters_ignore')
let l:linters = !empty(l:ignore_config)
\ ? ale#engine#ignore#Exclude(l:filetype, l:linters, l:ignore_config)
\ : l:linters
call ale#engine#RunLinters(l:buffer, l:linters, l:should_lint_file)
endfunction endfunction
" Reset flags indicating that files should be checked for all buffers. " Reset flags indicating that files should be checked for all buffers.
@ -209,10 +177,6 @@ function! ale#ResetLintFileMarkers() abort
let s:should_lint_file_for_buffer = {} let s:should_lint_file_for_buffer = {}
endfunction endfunction
function! ale#ResetErrorDelays() abort
let s:timestamp_map = {}
endfunction
let g:ale_has_override = get(g:, 'ale_has_override', {}) let g:ale_has_override = get(g:, 'ale_has_override', {})
" Call has(), but check a global Dictionary so we can force flags on or off " Call has(), but check a global Dictionary so we can force flags on or off

View file

@ -1,77 +0,0 @@
function! ale#autocmd#InitAuGroups() abort
" This value used to be a Boolean as a Number, and is now a String.
let l:text_changed = '' . g:ale_lint_on_text_changed
augroup ALEPatternOptionsGroup
autocmd!
autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions(str2nr(expand('<abuf>')))
augroup END
augroup ALERunOnTextChangedGroup
autocmd!
if g:ale_enabled
if l:text_changed is? 'always' || l:text_changed is# '1'
autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay)
elseif l:text_changed is? 'normal'
autocmd TextChanged * call ale#Queue(g:ale_lint_delay)
elseif l:text_changed is? 'insert'
autocmd TextChangedI * call ale#Queue(g:ale_lint_delay)
endif
endif
augroup END
augroup ALERunOnEnterGroup
autocmd!
if g:ale_enabled
" Handle everything that needs to happen when buffers are entered.
autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand('<abuf>')))
endif
if g:ale_enabled && g:ale_lint_on_enter
autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand('<abuf>')))
" Track when the file is changed outside of Vim.
autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>')))
endif
augroup END
augroup ALERunOnFiletypeChangeGroup
autocmd!
if g:ale_enabled && g:ale_lint_on_filetype_changed
" Only start linting if the FileType actually changes after
" opening a buffer. The FileType will fire when buffers are opened.
autocmd FileType * call ale#events#FileTypeEvent(
\ str2nr(expand('<abuf>')),
\ expand('<amatch>')
\)
endif
augroup END
augroup ALERunOnSaveGroup
autocmd!
autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand('<abuf>')))
augroup END
augroup ALERunOnInsertLeave
autocmd!
if g:ale_enabled && g:ale_lint_on_insert_leave
autocmd InsertLeave * call ale#Queue(0)
endif
augroup END
augroup ALECursorGroup
autocmd!
if g:ale_enabled && g:ale_echo_cursor
autocmd CursorMoved,CursorHold * call ale#cursor#EchoCursorWarningWithDelay()
" Look for a warning to echo as soon as we leave Insert mode.
" The script's position variable used when moving the cursor will
" not be changed here.
autocmd InsertLeave * call ale#cursor#EchoCursorWarning()
endif
augroup END
if !g:ale_enabled
augroup! ALERunOnTextChangedGroup
augroup! ALERunOnEnterGroup
augroup! ALERunOnInsertLeave
augroup! ALECursorGroup
endif
endfunction

View file

@ -34,7 +34,11 @@ function! ale#balloon#Expr() abort
endfunction endfunction
function! ale#balloon#Disable() abort function! ale#balloon#Disable() abort
set noballooneval noballoonevalterm if has('balloon_eval_term')
set noballoonevalterm
endif
set noballooneval
set balloonexpr= set balloonexpr=
endfunction endfunction

View file

@ -134,7 +134,11 @@ function! s:ReplaceCompleteopt() abort
let b:ale_old_completopt = &l:completeopt let b:ale_old_completopt = &l:completeopt
endif endif
if &l:completeopt =~# 'preview'
let &l:completeopt = 'menu,menuone,preview,noselect,noinsert' let &l:completeopt = 'menu,menuone,preview,noselect,noinsert'
else
let &l:completeopt = 'menu,menuone,noselect,noinsert'
endif
endfunction endfunction
function! ale#completion#OmniFunc(findstart, base) abort function! ale#completion#OmniFunc(findstart, base) abort
@ -389,14 +393,13 @@ function! s:GetLSPCompletions(linter) abort
\ ? function('ale#completion#HandleTSServerResponse') \ ? function('ale#completion#HandleTSServerResponse')
\ : function('ale#completion#HandleLSPResponse') \ : function('ale#completion#HandleLSPResponse')
let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback) let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver' if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Completions( let l:message = ale#lsp#tsserver_message#Completions(
@ -408,7 +411,7 @@ function! s:GetLSPCompletions(linter) abort
else else
" Send a message saying the buffer has changed first, otherwise " Send a message saying the buffer has changed first, otherwise
" completions won't know what text is nearby. " completions won't know what text is nearby.
call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root) call ale#lsp#NotifyForChanges(l:lsp_details)
" For LSP completions, we need to clamp the column to the length of " For LSP completions, we need to clamp the column to the length of
" the line. python-language-server and perhaps others do not implement " the line. python-language-server and perhaps others do not implement
@ -424,7 +427,7 @@ function! s:GetLSPCompletions(linter) abort
\) \)
endif endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:root) let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
if l:request_id if l:request_id
let b:ale_completion_info.conn_id = l:id let b:ale_completion_info.conn_id = l:id

View file

@ -56,10 +56,6 @@ function! s:StopCursorTimer() abort
endfunction endfunction
function! ale#cursor#EchoCursorWarning(...) abort function! ale#cursor#EchoCursorWarning(...) abort
return ale#CallWithCooldown('dont_echo_until', function('s:EchoImpl'), [])
endfunction
function! s:EchoImpl() abort
if !g:ale_echo_cursor if !g:ale_echo_cursor
return return
endif endif

View file

@ -214,10 +214,15 @@ function! ale#debugging#Info() abort
" This must be done after linters are loaded. " This must be done after linters are loaded.
let l:variable_list = s:GetLinterVariables(l:filetype, l:enabled_names) let l:variable_list = s:GetLinterVariables(l:filetype, l:enabled_names)
let l:fixers = ale#fix#registry#SuggestedFixers(l:filetype)
let l:fixers = uniq(sort(l:fixers[0] + l:fixers[1]))
let l:fixers_string = join(map(copy(l:fixers), '"\n " . v:val'), '')
call s:Echo(' Current Filetype: ' . l:filetype) call s:Echo(' Current Filetype: ' . l:filetype)
call s:Echo('Available Linters: ' . string(l:all_names)) call s:Echo('Available Linters: ' . string(l:all_names))
call s:EchoLinterAliases(l:all_linters) call s:EchoLinterAliases(l:all_linters)
call s:Echo(' Enabled Linters: ' . string(l:enabled_names)) call s:Echo(' Enabled Linters: ' . string(l:enabled_names))
call s:Echo(' Suggested Fixers: ' . l:fixers_string)
call s:Echo(' Linter Variables:') call s:Echo(' Linter Variables:')
call s:Echo('') call s:Echo('')
call s:EchoLinterVariables(l:variable_list) call s:EchoLinterVariables(l:variable_list)

View file

@ -65,14 +65,13 @@ function! s:GoToLSPDefinition(linter, options) abort
\ ? function('ale#definition#HandleTSServerResponse') \ ? function('ale#definition#HandleTSServerResponse')
\ : function('ale#definition#HandleLSPResponse') \ : function('ale#definition#HandleLSPResponse')
let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback) let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver' if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Definition( let l:message = ale#lsp#tsserver_message#Definition(
@ -83,7 +82,7 @@ function! s:GoToLSPDefinition(linter, options) abort
else else
" Send a message saying the buffer has changed first, or the " Send a message saying the buffer has changed first, or the
" definition position probably won't make sense. " definition position probably won't make sense.
call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root) call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([l:column, len(getline(l:line))]) let l:column = min([l:column, len(getline(l:line))])
@ -93,7 +92,7 @@ function! s:GoToLSPDefinition(linter, options) abort
let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column) let l:message = ale#lsp#message#Definition(l:buffer, l:line, l:column)
endif endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:root) let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:go_to_definition_map[l:request_id] = { let s:go_to_definition_map[l:request_id] = {
\ 'open_in_tab': get(a:options, 'open_in_tab', 0), \ 'open_in_tab': get(a:options, 'open_in_tab', 0),

View file

@ -14,11 +14,6 @@ if !has_key(s:, 'job_info_map')
let s:job_info_map = {} let s:job_info_map = {}
endif endif
" Associates LSP connection IDs with linter names.
if !has_key(s:, 'lsp_linter_map')
let s:lsp_linter_map = {}
endif
if !has_key(s:, 'executable_cache_map') if !has_key(s:, 'executable_cache_map')
let s:executable_cache_map = {} let s:executable_cache_map = {}
endif endif
@ -79,16 +74,6 @@ function! ale#engine#InitBufferInfo(buffer) abort
return 0 return 0
endfunction endfunction
" Clear LSP linter data for the linting engine.
function! ale#engine#ClearLSPData() abort
let s:lsp_linter_map = {}
endfunction
" Just for tests.
function! ale#engine#SetLSPLinterMap(replacement_map) abort
let s:lsp_linter_map = a:replacement_map
endfunction
" This function is documented and part of the public API. " This function is documented and part of the public API.
" "
" Return 1 if ALE is busy checking a given buffer " Return 1 if ALE is busy checking a given buffer
@ -241,88 +226,6 @@ function! s:HandleExit(job_id, exit_code) abort
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist) call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist)
endfunction endfunction
function! s:HandleLSPDiagnostics(conn_id, response) abort
let l:linter_name = s:lsp_linter_map[a:conn_id]
let l:filename = ale#path#FromURI(a:response.params.uri)
let l:buffer = bufnr(l:filename)
if l:buffer <= 0
return
endif
let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist)
endfunction
function! s:HandleTSServerDiagnostics(response, error_type) abort
let l:buffer = bufnr(a:response.body.file)
let l:info = get(g:ale_buffer_info, l:buffer, {})
if empty(l:info)
return
endif
let l:thislist = ale#lsp#response#ReadTSServerDiagnostics(a:response)
" tsserver sends syntax and semantic errors in separate messages, so we
" have to collect the messages separately for each buffer and join them
" back together again.
if a:error_type is# 'syntax'
let l:info.syntax_loclist = l:thislist
else
let l:info.semantic_loclist = l:thislist
endif
let l:loclist = get(l:info, 'semantic_loclist', [])
\ + get(l:info, 'syntax_loclist', [])
call ale#engine#HandleLoclist('tsserver', l:buffer, l:loclist)
endfunction
function! s:HandleLSPErrorMessage(linter_name, response) abort
if !g:ale_history_enabled || !g:ale_history_log_output
return
endif
if empty(a:linter_name)
return
endif
let l:message = ale#lsp#response#GetErrorMessage(a:response)
if empty(l:message)
return
endif
" This global variable is set here so we don't load the debugging.vim file
" until someone uses :ALEInfo.
let g:ale_lsp_error_messages = get(g:, 'ale_lsp_error_messages', {})
if !has_key(g:ale_lsp_error_messages, a:linter_name)
let g:ale_lsp_error_messages[a:linter_name] = []
endif
call add(g:ale_lsp_error_messages[a:linter_name], l:message)
endfunction
function! ale#engine#HandleLSPResponse(conn_id, response) abort
let l:method = get(a:response, 'method', '')
let l:linter_name = get(s:lsp_linter_map, a:conn_id, '')
if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
call s:HandleLSPErrorMessage(l:linter_name, a:response)
elseif l:method is# 'textDocument/publishDiagnostics'
call s:HandleLSPDiagnostics(a:conn_id, a:response)
elseif get(a:response, 'type', '') is# 'event'
\&& get(a:response, 'event', '') is# 'semanticDiag'
call s:HandleTSServerDiagnostics(a:response, 'semantic')
elseif get(a:response, 'type', '') is# 'event'
\&& get(a:response, 'event', '') is# 'syntaxDiag'
call s:HandleTSServerDiagnostics(a:response, 'syntax')
endif
endfunction
function! ale#engine#SetResults(buffer, loclist) abort function! ale#engine#SetResults(buffer, loclist) abort
let l:linting_is_done = !ale#engine#IsCheckingBuffer(a:buffer) let l:linting_is_done = !ale#engine#IsCheckingBuffer(a:buffer)
@ -367,9 +270,6 @@ function! ale#engine#SetResults(buffer, loclist) abort
" Call user autocommands. This allows users to hook into ALE's lint cycle. " Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd <nomodeline> User ALELintPost silent doautocmd <nomodeline> User ALELintPost
" remove in 2.0
" Old DEPRECATED name; call it for backwards compatibility.
silent doautocmd <nomodeline> User ALELint
endif endif
endfunction endfunction
@ -739,44 +639,6 @@ function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
let l:info.active_linter_list = l:new_active_linter_list let l:info.active_linter_list = l:new_active_linter_list
endfunction endfunction
function! s:CheckWithLSP(buffer, linter) abort
let l:info = g:ale_buffer_info[a:buffer]
let l:lsp_details = ale#linter#StartLSP(
\ a:buffer,
\ a:linter,
\ function('ale#engine#HandleLSPResponse'),
\)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
" Remember the linter this connection is for.
let s:lsp_linter_map[l:id] = a:linter.name
let l:change_message = a:linter.lsp is# 'tsserver'
\ ? ale#lsp#tsserver_message#Geterr(a:buffer)
\ : ale#lsp#message#DidChange(a:buffer)
let l:request_id = ale#lsp#Send(l:id, l:change_message, l:root)
" 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)
endif
if l:request_id != 0
if index(l:info.active_linter_list, a:linter.name) < 0
call add(l:info.active_linter_list, a:linter.name)
endif
endif
return l:request_id != 0
endfunction
function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
" Figure out which linters are still enabled, and remove " Figure out which linters are still enabled, and remove
@ -832,7 +694,7 @@ endfunction
" Returns 1 if the linter was successfully run. " Returns 1 if the linter was successfully run.
function! s:RunLinter(buffer, linter) abort function! s:RunLinter(buffer, linter) abort
if !empty(a:linter.lsp) if !empty(a:linter.lsp)
return s:CheckWithLSP(a:buffer, a:linter) return ale#lsp_linter#CheckWithLSP(a:buffer, a:linter)
else else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
@ -914,7 +776,7 @@ endfunction
" The time taken will be a very rough approximation, and more time may be " The time taken will be a very rough approximation, and more time may be
" permitted than is specified. " permitted than is specified.
function! ale#engine#WaitForJobs(deadline) abort function! ale#engine#WaitForJobs(deadline) abort
let l:start_time = ale#util#ClockMilliseconds() let l:start_time = ale#events#ClockMilliseconds()
if l:start_time == 0 if l:start_time == 0
throw 'Failed to read milliseconds from the clock!' throw 'Failed to read milliseconds from the clock!'
@ -945,7 +807,7 @@ function! ale#engine#WaitForJobs(deadline) abort
for l:job_id in l:job_list for l:job_id in l:job_list
if ale#job#IsRunning(l:job_id) if ale#job#IsRunning(l:job_id)
let l:now = ale#util#ClockMilliseconds() let l:now = ale#events#ClockMilliseconds()
if l:now - l:start_time > a:deadline if l:now - l:start_time > a:deadline
" Stop waiting after a timeout, so we don't wait forever. " Stop waiting after a timeout, so we don't wait forever.
@ -982,7 +844,7 @@ function! ale#engine#WaitForJobs(deadline) abort
if l:has_new_jobs if l:has_new_jobs
" We have to wait more. Offset the timeout by the time taken so far. " We have to wait more. Offset the timeout by the time taken so far.
let l:now = ale#util#ClockMilliseconds() let l:now = ale#events#ClockMilliseconds()
let l:new_deadline = a:deadline - (l:now - l:start_time) let l:new_deadline = a:deadline - (l:now - l:start_time)
if l:new_deadline <= 0 if l:new_deadline <= 0

View file

@ -0,0 +1,46 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Code for ignoring linters. Only loaded and if configured.
" 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([])
return a:config
endif
if type(a:config) is type({})
let l:names_to_remove = []
for l:part in split(a:filetype , '\.')
call extend(l:names_to_remove, get(a:config, l:part, []))
endfor
return l:names_to_remove
endif
return []
endfunction
" Given a List of linter descriptions, exclude the linters to be ignored.
function! ale#engine#ignore#Exclude(filetype, all_linters, config) abort
let l:names_to_remove = ale#engine#ignore#GetList(a:filetype, a:config)
let l:filtered_linters = []
for l:linter in a:all_linters
let l:name_list = [l:linter.name] + l:linter.aliases
let l:should_include = 1
for l:name in l:name_list
if index(l:names_to_remove, l:name) >= 0
let l:should_include = 0
break
endif
endfor
if l:should_include
call add(l:filtered_linters, l:linter)
endif
endfor
return l:filtered_linters
endfunction

View file

@ -1,14 +1,25 @@
" Author: w0rp <devw0rp@gmail.com> " Author: w0rp <devw0rp@gmail.com>
" Description: ALE functions for autocmd events.
" Get the number of milliseconds since some vague, but consistent, point in
" the past.
"
" This function can be used for timing execution, etc.
"
" The time will be returned as a Number.
function! ale#events#ClockMilliseconds() abort
return float2nr(reltimefloat(reltime()) * 1000)
endfunction
function! ale#events#QuitEvent(buffer) abort function! ale#events#QuitEvent(buffer) abort
" Remember when ALE is quitting for BufWrite, etc. " Remember when ALE is quitting for BufWrite, etc.
call setbufvar(a:buffer, 'ale_quitting', ale#util#ClockMilliseconds()) call setbufvar(a:buffer, 'ale_quitting', ale#events#ClockMilliseconds())
endfunction endfunction
function! ale#events#QuitRecently(buffer) abort function! ale#events#QuitRecently(buffer) abort
let l:time = getbufvar(a:buffer, 'ale_quitting', 0) let l:time = getbufvar(a:buffer, 'ale_quitting', 0)
return l:time && ale#util#ClockMilliseconds() - l:time < 1000 return l:time && ale#events#ClockMilliseconds() - l:time < 1000
endfunction endfunction
function! ale#events#SaveEvent(buffer) abort function! ale#events#SaveEvent(buffer) abort
@ -67,3 +78,56 @@ function! ale#events#FileChangedEvent(buffer) abort
call s:LintOnEnter(a:buffer) call s:LintOnEnter(a:buffer)
endif endif
endfunction endfunction
function! ale#events#Init() abort
" This value used to be a Boolean as a Number, and is now a String.
let l:text_changed = '' . g:ale_lint_on_text_changed
augroup ALEEvents
autocmd!
" These events always need to be set up.
autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions(str2nr(expand('<abuf>')))
autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand('<abuf>')))
if g:ale_enabled
if l:text_changed is? 'always' || l:text_changed is# '1'
autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay)
elseif l:text_changed is? 'normal'
autocmd TextChanged * call ale#Queue(g:ale_lint_delay)
elseif l:text_changed is? 'insert'
autocmd TextChangedI * call ale#Queue(g:ale_lint_delay)
endif
" Handle everything that needs to happen when buffers are entered.
autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand('<abuf>')))
if g:ale_lint_on_enter
autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand('<abuf>')))
" Track when the file is changed outside of Vim.
autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>')))
endif
if g:ale_lint_on_filetype_changed
" Only start linting if the FileType actually changes after
" opening a buffer. The FileType will fire when buffers are opened.
autocmd FileType * call ale#events#FileTypeEvent(
\ str2nr(expand('<abuf>')),
\ expand('<amatch>')
\)
endif
if g:ale_lint_on_insert_leave
autocmd InsertLeave * call ale#Queue(0)
endif
if g:ale_echo_cursor
autocmd CursorMoved,CursorHold * if exists('*ale#engine#Cleanup') | call ale#cursor#EchoCursorWarningWithDelay() | endif
" Look for a warning to echo as soon as we leave Insert mode.
" The script's position variable used when moving the cursor will
" not be changed here.
autocmd InsertLeave * if exists('*ale#engine#Cleanup') | call ale#cursor#EchoCursorWarning() | endif
endif
endif
augroup END
endfunction

View file

@ -356,9 +356,21 @@ function! s:RunFixer(options) abort
call ale#fix#ApplyFixes(l:buffer, l:input) call ale#fix#ApplyFixes(l:buffer, l:input)
endfunction endfunction
function! s:GetCallbacks(buffer, linters) abort function! s:AddSubCallbacks(full_list, callbacks) abort
if len(a:linters) if type(a:callbacks) == type('')
let l:callback_list = a:linters call add(a:full_list, a:callbacks)
elseif type(a:callbacks) == type([])
call extend(a:full_list, a:callbacks)
else
return 0
endif
return 1
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 type([])
" Lists can be used for buffer-local variables only " Lists can be used for buffer-local variables only
let l:callback_list = b:ale_fixers let l:callback_list = b:ale_fixers
@ -367,16 +379,18 @@ function! s:GetCallbacks(buffer, linters) abort
" callbacks to run. " callbacks to run.
let l:fixers = ale#Var(a:buffer, 'fixers') let l:fixers = ale#Var(a:buffer, 'fixers')
let l:callback_list = [] let l:callback_list = []
let l:matched = 0
for l:sub_type in split(&filetype, '\.') for l:sub_type in split(&filetype, '\.')
let l:sub_type_callacks = get(l:fixers, l:sub_type, []) if s:AddSubCallbacks(l:callback_list, get(l:fixers, l:sub_type))
let l:matched = 1
if type(l:sub_type_callacks) == type('')
call add(l:callback_list, l:sub_type_callacks)
else
call extend(l:callback_list, l:sub_type_callacks)
endif endif
endfor endfor
" If we couldn't find fixers for a filetype, default to '*' fixers.
if !l:matched
call s:AddSubCallbacks(l:callback_list, get(l:fixers, '*'))
endif
endif endif
if empty(l:callback_list) if empty(l:callback_list)

View file

@ -22,6 +22,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['python'], \ 'suggested_filetypes': ['python'],
\ 'description': 'Fix PEP8 issues with black.', \ 'description': 'Fix PEP8 issues with black.',
\ }, \ },
\ 'tidy': {
\ 'function': 'ale#fixers#tidy#Fix',
\ 'suggested_filetypes': ['html'],
\ 'description': 'Fix HTML files with tidy.',
\ },
\ 'prettier_standard': { \ 'prettier_standard': {
\ 'function': 'ale#fixers#prettier_standard#Fix', \ 'function': 'ale#fixers#prettier_standard#Fix',
\ 'suggested_filetypes': ['javascript'], \ 'suggested_filetypes': ['javascript'],
@ -51,7 +56,7 @@ let s:default_registry = {
\ }, \ },
\ 'prettier': { \ 'prettier': {
\ 'function': 'ale#fixers#prettier#Fix', \ 'function': 'ale#fixers#prettier#Fix',
\ 'suggested_filetypes': ['javascript', 'typescript', 'json', 'css', 'scss', 'less', 'markdown', 'graphql', 'vue'], \ 'suggested_filetypes': ['javascript', 'typescript', 'css', 'less', 'scss', 'json', 'json5', 'graphql', 'markdown', 'vue'],
\ 'description': 'Apply prettier to a file.', \ 'description': 'Apply prettier to a file.',
\ }, \ },
\ 'prettier_eslint': { \ 'prettier_eslint': {
@ -205,6 +210,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['qml'], \ 'suggested_filetypes': ['qml'],
\ 'description': 'Fix QML files with qmlfmt.', \ 'description': 'Fix QML files with qmlfmt.',
\ }, \ },
\ 'dartfmt': {
\ 'function': 'ale#fixers#dartfmt#Fix',
\ 'suggested_filetypes': ['dart'],
\ 'description': 'Fix Dart files with dartfmt.',
\ },
\} \}
" Reset the function registry to the default entries. " Reset the function registry to the default entries.
@ -345,8 +355,7 @@ function! ale#fix#registry#CompleteFixers(ArgLead, CmdLine, CursorPos) abort
return filter(ale#fix#registry#GetApplicableFixers(&filetype), 'v:val =~? a:ArgLead') return filter(ale#fix#registry#GetApplicableFixers(&filetype), 'v:val =~? a:ArgLead')
endfunction endfunction
" Suggest functions to use from the registry. function! ale#fix#registry#SuggestedFixers(filetype) abort
function! ale#fix#registry#Suggest(filetype) abort
let l:type_list = split(a:filetype, '\.') let l:type_list = split(a:filetype, '\.')
let l:filetype_fixer_list = [] let l:filetype_fixer_list = []
@ -372,6 +381,15 @@ function! ale#fix#registry#Suggest(filetype) abort
endif endif
endfor endfor
return [l:filetype_fixer_list, l:generic_fixer_list]
endfunction
" Suggest functions to use from the registry.
function! ale#fix#registry#Suggest(filetype) abort
let l:suggested = ale#fix#registry#SuggestedFixers(a:filetype)
let l:filetype_fixer_list = l:suggested[0]
let l:generic_fixer_list = l:suggested[1]
let l:filetype_fixer_header = !empty(l:filetype_fixer_list) let l:filetype_fixer_header = !empty(l:filetype_fixer_list)
\ ? ['Try the following fixers appropriate for the filetype:', ''] \ ? ['Try the following fixers appropriate for the filetype:', '']
\ : [] \ : []

View file

@ -0,0 +1,18 @@
" Author: reisub0 <reisub0@gmail.com>
" Description: Integration of dartfmt with ALE.
call ale#Set('dart_dartfmt_executable', 'dartfmt')
call ale#Set('dart_dartfmt_options', '')
function! ale#fixers#dartfmt#Fix(buffer) abort
let l:executable = ale#Var(a:buffer, 'dart_dartfmt_executable')
let l:options = ale#Var(a:buffer, 'dart_dartfmt_options')
return {
\ 'command': ale#Escape(l:executable)
\ . ' -w'
\ . (empty(l:options) ? '' : ' ' . l:options)
\ . ' %t',
\ 'read_temporary_file': 1,
\}
endfunction

View file

@ -30,8 +30,26 @@ endfunction
function! ale#fixers#prettier#ApplyFixForVersion(buffer, version_output) abort function! ale#fixers#prettier#ApplyFixForVersion(buffer, version_output) abort
let l:executable = ale#fixers#prettier#GetExecutable(a:buffer) let l:executable = ale#fixers#prettier#GetExecutable(a:buffer)
let l:options = ale#Var(a:buffer, 'javascript_prettier_options') let l:options = ale#Var(a:buffer, 'javascript_prettier_options')
let l:version = ale#semver#GetVersion(l:executable, a:version_output) let l:version = ale#semver#GetVersion(l:executable, a:version_output)
let l:parser = ''
" Append the --parser flag depending on the current filetype (unless it's
" already set in g:javascript_prettier_options).
if empty(expand('#' . a:buffer . ':e')) && match(l:options, '--parser') == -1
let l:prettier_parsers = ['typescript', 'css', 'less', 'scss', 'json', 'json5', 'graphql', 'markdown', 'vue']
let l:parser = 'babylon'
for l:filetype in split(getbufvar(a:buffer, '&filetype'), '\.')
if index(l:prettier_parsers, l:filetype) > -1
let l:parser = l:filetype
break
endif
endfor
endif
if !empty(l:parser)
let l:options = (!empty(l:options) ? l:options . ' ' : '') . '--parser ' . l:parser
endif
" 1.4.0 is the first version with --stdin-filepath " 1.4.0 is the first version with --stdin-filepath
if ale#semver#GTE(l:version, [1, 4, 0]) if ale#semver#GTE(l:version, [1, 4, 0])

View file

@ -0,0 +1,26 @@
" Author: meain <abinsimon10@gmail.com>
" Description: Fixing HTML files with tidy.
call ale#Set('html_tidy_executable', 'tidy')
call ale#Set('html_tidy_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale#fixers#tidy#Fix(buffer) abort
let l:executable = ale#node#FindExecutable(
\ a:buffer,
\ 'html_tidy',
\ ['tidy'],
\)
if !executable(l:executable)
return 0
endif
let l:config = ale#path#FindNearestFile(a:buffer, '.tidyrc')
let l:config_options = !empty(l:config)
\ ? ' -q --tidy-mark no --show-errors 0 --show-warnings 0 -config ' . ale#Escape(l:config)
\ : ' -q --tidy-mark no --show-errors 0 --show-warnings 0'
return {
\ 'command': ale#Escape(l:executable) . l:config_options,
\}
endfunction

View file

@ -97,14 +97,14 @@ function! s:ShowDetails(linter, buffer, line, column, opt) abort
\ ? function('ale#hover#HandleTSServerResponse') \ ? function('ale#hover#HandleTSServerResponse')
\ : function('ale#hover#HandleLSPResponse') \ : function('ale#hover#HandleLSPResponse')
let l:lsp_details = ale#linter#StartLSP(a:buffer, a:linter, l:Callback) let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter, l:Callback)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root let l:language_id = l:lsp_details.language_id
if a:linter.lsp is# 'tsserver' if a:linter.lsp is# 'tsserver'
let l:column = a:column let l:column = a:column
@ -117,14 +117,14 @@ function! s:ShowDetails(linter, buffer, line, column, opt) abort
else else
" Send a message saying the buffer has changed first, or the " Send a message saying the buffer has changed first, or the
" hover position probably won't make sense. " hover position probably won't make sense.
call ale#lsp#Send(l:id, ale#lsp#message#DidChange(a:buffer), l:root) call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])]) let l:column = min([a:column, len(getbufline(a:buffer, a:line)[0])])
let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column) let l:message = ale#lsp#message#Hover(a:buffer, a:line, l:column)
endif endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:root) let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:hover_map[l:request_id] = { let s:hover_map[l:request_id] = {
\ 'buffer': a:buffer, \ 'buffer': a:buffer,

View file

@ -26,34 +26,11 @@ function! s:KillHandler(timer) abort
call job_stop(l:job, 'kill') call job_stop(l:job, 'kill')
endfunction endfunction
" Note that jobs and IDs are the same thing on NeoVim.
function! ale#job#JoinNeovimOutput(job, last_line, data, mode, callback) abort
if a:mode is# 'raw'
call a:callback(a:job, join(a:data, "\n"))
return ''
endif
let l:lines = a:data[:-2]
if len(a:data) > 1
let l:lines[0] = a:last_line . l:lines[0]
let l:new_last_line = a:data[-1]
else
let l:new_last_line = a:last_line . get(a:data, 0, '')
endif
for l:line in l:lines
call a:callback(a:job, l:line)
endfor
return l:new_last_line
endfunction
function! s:NeoVimCallback(job, data, event) abort function! s:NeoVimCallback(job, data, event) abort
let l:info = s:job_map[a:job] let l:info = s:job_map[a:job]
if a:event is# 'stdout' if a:event is# 'stdout'
let l:info.out_cb_line = ale#job#JoinNeovimOutput( let l:info.out_cb_line = ale#util#JoinNeovimOutput(
\ a:job, \ a:job,
\ l:info.out_cb_line, \ l:info.out_cb_line,
\ a:data, \ a:data,
@ -61,7 +38,7 @@ function! s:NeoVimCallback(job, data, event) abort
\ ale#util#GetFunction(l:info.out_cb), \ ale#util#GetFunction(l:info.out_cb),
\) \)
elseif a:event is# 'stderr' elseif a:event is# 'stderr'
let l:info.err_cb_line = ale#job#JoinNeovimOutput( let l:info.err_cb_line = ale#util#JoinNeovimOutput(
\ a:job, \ a:job,
\ l:info.err_cb_line, \ l:info.err_cb_line,
\ a:data, \ a:data,

View file

@ -15,6 +15,7 @@ let s:default_ale_linter_aliases = {
\ 'csh': 'sh', \ 'csh': 'sh',
\ 'plaintex': 'tex', \ 'plaintex': 'tex',
\ 'systemverilog': 'verilog', \ 'systemverilog': 'verilog',
\ 'verilog_systemverilog': ['verilog_systemverilog', 'verilog'],
\ 'vimwiki': 'markdown', \ 'vimwiki': 'markdown',
\ 'zsh': 'sh', \ 'zsh': 'sh',
\} \}
@ -451,81 +452,3 @@ function! ale#linter#GetAddress(buffer, linter) abort
\ ? ale#util#GetFunction(a:linter.address_callback)(a:buffer) \ ? ale#util#GetFunction(a:linter.address_callback)(a:buffer)
\ : a:linter.address \ : a:linter.address
endfunction 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#linter#StartLSP(buffer, linter, callback) abort
let l:command = ''
let l:address = ''
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
if empty(l:root) && a:linter.lsp isnot# 'tsserver'
" If there's no project root, then we can't check files with LSP,
" unless we are using tsserver, which doesn't use project roots.
return {}
endif
let l:initialization_options = {}
if has_key(a:linter, 'initialization_options_callback')
let l:initialization_options = ale#util#GetFunction(a:linter.initialization_options_callback)(a:buffer)
elseif has_key(a:linter, 'initialization_options')
let l:initialization_options = a:linter.initialization_options
endif
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,
\)
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
if !executable(l:executable)
return {}
endif
let l:command = ale#job#PrepareCommand(
\ a:buffer,
\ ale#linter#GetCommand(a:buffer, a:linter),
\)
let l:conn_id = ale#lsp#StartProgram(
\ l:executable,
\ l:command,
\ l:root,
\ a:callback,
\ l:initialization_options,
\)
endif
let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer)
if !l:conn_id
if g:ale_history_enabled && !empty(l:command)
call ale#history#Add(a:buffer, 'failed', l:conn_id, l:command)
endif
return {}
endif
if ale#lsp#OpenDocumentIfNeeded(l:conn_id, a:buffer, l:root, l:language_id)
if g:ale_history_enabled && !empty(l:command)
call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
endif
endif
" The change message needs to be sent for tsserver before doing anything.
if a:linter.lsp is# 'tsserver'
call ale#lsp#Send(l:conn_id, ale#lsp#tsserver_message#Change(a:buffer))
endif
return {
\ 'connection_id': l:conn_id,
\ 'command': l:command,
\ 'project_root': l:root,
\ 'language_id': l:language_id,
\}
endfunction

View file

@ -3,20 +3,23 @@
" A List of connections, used for tracking servers which have been connected " A List of connections, used for tracking servers which have been connected
" to, and programs which are run. " to, and programs which are run.
let s:connections = [] let s:connections = get(s:, 'connections', [])
let g:ale_lsp_next_message_id = 1 let g:ale_lsp_next_message_id = 1
function! s:NewConnection(initialization_options) abort " Exposed only so tests can get at it.
" Do not call this function basically anywhere.
function! ale#lsp#NewConnection(initialization_options) abort
" id: The job ID as a Number, or the server address as a string. " id: The job ID as a Number, or the server address as a string.
" data: The message data received so far. " data: The message data received so far.
" executable: An executable only set for program connections. " executable: An executable only set for program connections.
" open_documents: A list of buffers we told the server we opened. " 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. " callback_list: A list of callbacks for handling LSP responses.
let l:conn = { let l:conn = {
\ 'id': '', \ 'id': '',
\ 'data': '', \ 'data': '',
\ 'projects': {}, \ 'projects': {},
\ 'open_documents': [], \ 'open_documents': {},
\ 'callback_list': [], \ 'callback_list': [],
\ 'initialization_options': a:initialization_options, \ 'initialization_options': a:initialization_options,
\} \}
@ -26,9 +29,14 @@ function! s:NewConnection(initialization_options) abort
return l:conn return l:conn
endfunction endfunction
" Remove an LSP connection with a given ID. This is only for tests.
function! ale#lsp#RemoveConnectionWithID(id) abort
call filter(s:connections, 'v:val.id isnot a:id')
endfunction
function! s:FindConnection(key, value) abort function! s:FindConnection(key, value) abort
for l:conn in s:connections for l:conn in s:connections
if has_key(l:conn, a:key) && get(l:conn, a:key) == a:value if has_key(l:conn, a:key) && get(l:conn, a:key) is# a:value
return l:conn return l:conn
endif endif
endfor endfor
@ -233,9 +241,8 @@ function! ale#lsp#HandleMessage(conn, message) abort
endfor endfor
endfunction endfunction
function! s:HandleChannelMessage(channel, message) abort function! s:HandleChannelMessage(channel_id, message) abort
let l:info = ch_info(a:channel) let l:address = ale#socket#GetAddress(a:channel_id)
let l:address = l:info.hostname . l:info.address
let l:conn = s:FindConnection('id', l:address) let l:conn = s:FindConnection('id', l:address)
call ale#lsp#HandleMessage(l:conn, a:message) call ale#lsp#HandleMessage(l:conn, a:message)
@ -280,7 +287,7 @@ function! ale#lsp#StartProgram(executable, command, project_root, callback, init
let l:conn = s:FindConnection('executable', a:executable) let l:conn = s:FindConnection('executable', a:executable)
" Get the current connection or a new one. " Get the current connection or a new one.
let l:conn = !empty(l:conn) ? l:conn : s:NewConnection(a:initialization_options) let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options)
let l:conn.executable = a:executable let l:conn.executable = a:executable
if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id) if !has_key(l:conn, 'id') || !ale#job#IsRunning(l:conn.id)
@ -309,18 +316,16 @@ endfunction
function! ale#lsp#ConnectToAddress(address, project_root, callback, initialization_options) abort function! ale#lsp#ConnectToAddress(address, project_root, callback, initialization_options) abort
let l:conn = s:FindConnection('id', a:address) let l:conn = s:FindConnection('id', a:address)
" Get the current connection or a new one. " Get the current connection or a new one.
let l:conn = !empty(l:conn) ? l:conn : s:NewConnection(a:initialization_options) let l:conn = !empty(l:conn) ? l:conn : ale#lsp#NewConnection(a:initialization_options)
if !has_key(l:conn, 'channel') || ch_status(l:conn.channel) isnot# 'open' if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id)
let l:conn.channnel = ch_open(a:address, { let l:conn.channel_id = ale#socket#Open(a:address, {
\ 'mode': 'raw',
\ 'waittime': 0,
\ 'callback': function('s:HandleChannelMessage'), \ 'callback': function('s:HandleChannelMessage'),
\}) \})
endif endif
if ch_status(l:conn.channnel) is# 'fail' if l:conn.channel_id < 0
return 0 return ''
endif endif
let l:conn.id = a:address let l:conn.id = a:address
@ -328,15 +333,15 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback, initializati
call uniq(sort(add(l:conn.callback_list, a:callback))) call uniq(sort(add(l:conn.callback_list, a:callback)))
call ale#lsp#RegisterProject(l:conn, a:project_root) call ale#lsp#RegisterProject(l:conn, a:project_root)
return 1 return a:address
endfunction endfunction
" Stop all LSP connections, closing all jobs and channels, and removing any " Stop all LSP connections, closing all jobs and channels, and removing any
" queued messages. " queued messages.
function! ale#lsp#StopAll() abort function! ale#lsp#StopAll() abort
for l:conn in s:connections for l:conn in s:connections
if has_key(l:conn, 'channel') if has_key(l:conn, 'channel_id')
call ch_close(l:conn.channel) call ale#socket#Close(l:conn.channel_id)
else else
call ale#job#Stop(l:conn.id) call ale#job#Stop(l:conn.id)
endif endif
@ -348,9 +353,9 @@ endfunction
function! s:SendMessageData(conn, data) abort function! s:SendMessageData(conn, data) abort
if has_key(a:conn, 'executable') if has_key(a:conn, 'executable')
call ale#job#SendRaw(a:conn.id, a:data) call ale#job#SendRaw(a:conn.id, a:data)
elseif has_key(a:conn, 'channel') && ch_status(a:conn.channnel) is# 'open' elseif has_key(a:conn, 'channel_id') && ale#socket#IsOpen(a:conn.channel_id)
" Send the message to the server " Send the message to the server
call ch_sendraw(a:conn.channel, a:data) call ale#socket#Send(a:conn.channel_id, a:data)
else else
return 0 return 0
endif endif
@ -406,21 +411,72 @@ function! ale#lsp#Send(conn_id, message, ...) abort
return l:id == 0 ? -1 : l:id return l:id == 0 ? -1 : l:id
endfunction endfunction
function! ale#lsp#OpenDocumentIfNeeded(conn_id, buffer, project_root, language_id) abort " The Document details Dictionary should contain the following keys.
let l:conn = s:FindConnection('id', a:conn_id) "
" 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)
let l:opened = 0 let l:opened = 0
if !empty(l:conn) && index(l:conn.open_documents, a:buffer) < 0 if !empty(l:d.conn) && !l:d.document_open
if empty(a:language_id) if empty(l:d.language_id)
let l:message = ale#lsp#tsserver_message#Open(a:buffer) let l:message = ale#lsp#tsserver_message#Open(l:d.buffer)
else else
let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id) let l:message = ale#lsp#message#DidOpen(l:d.buffer, l:d.language_id)
endif endif
call ale#lsp#Send(a:conn_id, l:message, a:project_root) call ale#lsp#Send(l:d.connection_id, l:message, l:d.project_root)
call add(l:conn.open_documents, a:buffer) let l:d.conn.open_documents[l:d.buffer] = getbufvar(l:d.buffer, 'changedtick')
let l:opened = 1 let l:opened = 1
endif endif
return l:opened return l:opened
endfunction 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)
let l:notified = 0
if l:d.document_open
let l:new_tick = getbufvar(l:d.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)
else
let l:message = ale#lsp#message#DidChange(l:d.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
let l:notified = 1
endif
endif
return l:notified
endfunction

View file

@ -7,9 +7,9 @@ function! ale#lsp#reset#StopAllLSPs() abort
call ale#definition#ClearLSPData() call ale#definition#ClearLSPData()
endif endif
if exists('*ale#engine#ClearLSPData') if exists('*ale#lsp_linter#ClearLSPData')
" Clear the mapping for connections, etc. " Clear the mapping for connections, etc.
call ale#engine#ClearLSPData() call ale#lsp_linter#ClearLSPData()
" Remove the problems for all of the LSP linters in every buffer. " Remove the problems for all of the LSP linters in every buffer.
for l:buffer_string in keys(g:ale_buffer_info) for l:buffer_string in keys(g:ale_buffer_info)

View file

@ -0,0 +1,252 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Integration between linters and LSP/tsserver.
" This code isn't loaded if a user never users LSP features or linters.
" Associates LSP connection IDs with linter names.
if !has_key(s:, 'lsp_linter_map')
let s:lsp_linter_map = {}
endif
" Check if diagnostics for a particular linter should be ignored.
function! s:ShouldIgnore(buffer, linter_name) abort
let l:config = ale#Var(a:buffer, 'linters_ignore')
" Don't load code for ignoring diagnostics if there's nothing to ignore.
if empty(l:config)
return 0
endif
let l:filetype = getbufvar(a:buffer, '&filetype')
let l:ignore_list = ale#engine#ignore#GetList(l:filetype, l:config)
return index(l:ignore_list, a:linter_name) >= 0
endfunction
function! s:HandleLSPDiagnostics(conn_id, response) abort
let l:linter_name = s:lsp_linter_map[a:conn_id]
let l:filename = ale#path#FromURI(a:response.params.uri)
let l:buffer = bufnr(l:filename)
if s:ShouldIgnore(l:buffer, l:linter_name)
return
endif
if l:buffer <= 0
return
endif
let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist)
endfunction
function! s:HandleTSServerDiagnostics(response, error_type) abort
let l:linter_name = 'tsserver'
let l:buffer = bufnr(a:response.body.file)
let l:info = get(g:ale_buffer_info, l:buffer, {})
if empty(l:info)
return
endif
if s:ShouldIgnore(l:buffer, l:linter_name)
return
endif
let l:thislist = ale#lsp#response#ReadTSServerDiagnostics(a:response)
" tsserver sends syntax and semantic errors in separate messages, so we
" have to collect the messages separately for each buffer and join them
" back together again.
if a:error_type is# 'syntax'
let l:info.syntax_loclist = l:thislist
else
let l:info.semantic_loclist = l:thislist
endif
let l:loclist = get(l:info, 'semantic_loclist', [])
\ + get(l:info, 'syntax_loclist', [])
call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist)
endfunction
function! s:HandleLSPErrorMessage(linter_name, response) abort
if !g:ale_history_enabled || !g:ale_history_log_output
return
endif
if empty(a:linter_name)
return
endif
let l:message = ale#lsp#response#GetErrorMessage(a:response)
if empty(l:message)
return
endif
" This global variable is set here so we don't load the debugging.vim file
" until someone uses :ALEInfo.
let g:ale_lsp_error_messages = get(g:, 'ale_lsp_error_messages', {})
if !has_key(g:ale_lsp_error_messages, a:linter_name)
let g:ale_lsp_error_messages[a:linter_name] = []
endif
call add(g:ale_lsp_error_messages[a:linter_name], l:message)
endfunction
function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
let l:method = get(a:response, 'method', '')
let l:linter_name = get(s:lsp_linter_map, a:conn_id, '')
if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error')
call s:HandleLSPErrorMessage(l:linter_name, a:response)
elseif l:method is# 'textDocument/publishDiagnostics'
call s:HandleLSPDiagnostics(a:conn_id, a:response)
elseif get(a:response, 'type', '') is# 'event'
\&& get(a:response, 'event', '') is# 'semanticDiag'
call s:HandleTSServerDiagnostics(a:response, 'semantic')
elseif get(a:response, 'type', '') is# 'event'
\&& get(a:response, 'event', '') is# 'syntaxDiag'
call s:HandleTSServerDiagnostics(a:response, 'syntax')
endif
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
let l:command = ''
let l:address = ''
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
if empty(l:root) && a:linter.lsp isnot# 'tsserver'
" If there's no project root, then we can't check files with LSP,
" unless we are using tsserver, which doesn't use project roots.
return {}
endif
let l:initialization_options = {}
if has_key(a:linter, 'initialization_options_callback')
let l:initialization_options = ale#util#GetFunction(a:linter.initialization_options_callback)(a:buffer)
elseif has_key(a:linter, 'initialization_options')
let l:initialization_options = a:linter.initialization_options
endif
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,
\)
else
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
if !executable(l:executable)
return {}
endif
let l:command = ale#job#PrepareCommand(
\ a:buffer,
\ ale#linter#GetCommand(a:buffer, a:linter),
\)
let l:conn_id = ale#lsp#StartProgram(
\ l:executable,
\ l:command,
\ l:root,
\ a:callback,
\ l:initialization_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)
endif
return {}
endif
let l:details = {
\ 'buffer': a:buffer,
\ 'connection_id': l:conn_id,
\ 'command': l:command,
\ 'project_root': l:root,
\ 'language_id': l:language_id,
\}
if ale#lsp#OpenDocument(l:details)
if g:ale_history_enabled && !empty(l:command)
call ale#history#Add(a:buffer, 'started', l:conn_id, l:command)
endif
endif
" The change message needs to be sent for tsserver before doing anything.
if a:linter.lsp is# 'tsserver'
call ale#lsp#NotifyForChanges(l:details)
endif
return l:details
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'),
\)
if empty(l:lsp_details)
return 0
endif
let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
" 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
else
let l:notified = ale#lsp#NotifyForChanges(l:lsp_details)
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
endif
if l:notified
if index(l:info.active_linter_list, a:linter.name) < 0
call add(l:info.active_linter_list, a:linter.name)
endif
endif
return l:notified
endfunction
" Clear LSP linter data for the linting engine.
function! ale#lsp_linter#ClearLSPData() abort
let s:lsp_linter_map = {}
endfunction
" Just for tests.
function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
let s:lsp_linter_map = a:replacement_map
endfunction

View file

@ -2,22 +2,41 @@
" Description: Preview windows for showing whatever information in. " Description: Preview windows for showing whatever information in.
" Open a preview window and show some lines in it. " Open a preview window and show some lines in it.
" An optional second argument can set an alternative filetype for the window. " A second argument can be passed as a Dictionary with options. They are...
"
" filetype - The filetype to use, defaulting to 'ale-preview'
" stay_here - If 1, stay in the window you came from.
function! ale#preview#Show(lines, ...) abort function! ale#preview#Show(lines, ...) abort
let l:filetype = get(a:000, 0, 'ale-preview') let l:options = get(a:000, 0, {})
silent pedit ALEPreviewWindow silent pedit ALEPreviewWindow
wincmd P wincmd P
setlocal modifiable setlocal modifiable
setlocal noreadonly setlocal noreadonly
setlocal nobuflisted setlocal nobuflisted
let &l:filetype = l:filetype let &l:filetype = get(l:options, 'filetype', 'ale-preview')
setlocal buftype=nofile setlocal buftype=nofile
setlocal bufhidden=wipe setlocal bufhidden=wipe
:%d :%d
call setline(1, a:lines) call setline(1, a:lines)
setlocal nomodifiable setlocal nomodifiable
setlocal readonly setlocal readonly
if get(l:options, 'stay_here')
wincmd p
endif
endfunction
" Close the preview window if the filetype matches the given one.
function! ale#preview#CloseIfTypeMatches(filetype) abort
for l:win in getwininfo()
let l:wintype = gettabwinvar(l:win.tabnr, l:win.winnr, '&filetype')
if l:wintype is# a:filetype
silent! pclose!
endif
endfor
endfunction endfunction
" Show a location selection preview window, given some items. " Show a location selection preview window, given some items.
@ -35,7 +54,7 @@ function! ale#preview#ShowSelection(item_list) abort
\) \)
endfor endfor
call ale#preview#Show(l:lines, 'ale-preview-selection') call ale#preview#Show(l:lines, {'filetype': 'ale-preview-selection'})
let b:ale_preview_item_list = a:item_list let b:ale_preview_item_list = a:item_list
endfunction endfunction

View file

@ -72,14 +72,13 @@ function! s:FindReferences(linter) abort
\ ? function('ale#references#HandleTSServerResponse') \ ? function('ale#references#HandleTSServerResponse')
\ : function('ale#references#HandleLSPResponse') \ : function('ale#references#HandleLSPResponse')
let l:lsp_details = ale#linter#StartLSP(l:buffer, a:linter, l:Callback) let l:lsp_details = ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
if empty(l:lsp_details) if empty(l:lsp_details)
return 0 return 0
endif endif
let l:id = l:lsp_details.connection_id let l:id = l:lsp_details.connection_id
let l:root = l:lsp_details.project_root
if a:linter.lsp is# 'tsserver' if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#References( let l:message = ale#lsp#tsserver_message#References(
@ -90,14 +89,14 @@ function! s:FindReferences(linter) abort
else else
" Send a message saying the buffer has changed first, or the " Send a message saying the buffer has changed first, or the
" references position probably won't make sense. " references position probably won't make sense.
call ale#lsp#Send(l:id, ale#lsp#message#DidChange(l:buffer), l:root) call ale#lsp#NotifyForChanges(l:lsp_details)
let l:column = min([l:column, len(getline(l:line))]) let l:column = min([l:column, len(getline(l:line))])
let l:message = ale#lsp#message#References(l:buffer, l:line, l:column) let l:message = ale#lsp#message#References(l:buffer, l:line, l:column)
endif endif
let l:request_id = ale#lsp#Send(l:id, l:message, l:root) let l:request_id = ale#lsp#Send(l:id, l:message, l:lsp_details.project_root)
let s:references_map[l:request_id] = {} let s:references_map[l:request_id] = {}
endfunction endfunction

View file

@ -0,0 +1,144 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: APIs for working with asynchronous sockets, with an API
" normalised between Vim 8 and NeoVim. Socket connections only work in NeoVim
" 0.3+, and silently do nothing in earlier NeoVim versions.
"
" Important functions are described below. They are:
"
" ale#socket#Open(address, options) -> channel_id (>= 0 if successful)
" ale#socket#IsOpen(channel_id) -> 1 if open, 0 otherwise
" ale#socket#Close(channel_id)
" ale#socket#Send(channel_id, data)
" ale#socket#GetAddress(channel_id) -> Return the address for a job
let s:channel_map = get(s:, 'channel_map', {})
function! s:VimOutputCallback(channel, data) abort
let l:channel_id = ch_info(a:channel).id
" Only call the callbacks for jobs which are valid.
if l:channel_id >= 0 && has_key(s:channel_map, l:channel_id)
call ale#util#GetFunction(s:channel_map[l:channel_id].callback)(l:channel_id, a:data)
endif
endfunction
function! s:NeoVimOutputCallback(channel_id, data, event) abort
let l:info = s:channel_map[a:channel_id]
if a:event is# 'data'
let l:info.last_line = ale#util#JoinNeovimOutput(
\ a:channel_id,
\ l:info.last_line,
\ a:data,
\ l:info.mode,
\ ale#util#GetFunction(l:info.callback),
\)
endif
endfunction
" Open a socket for a given address. The following options are accepted:
"
" callback - A callback for receiving input. (required)
"
" A non-negative number representing a channel ID will be returned is the
" connection was successful. 0 is a valid channel ID in Vim, so test if the
" connection ID is >= 0.
function! ale#socket#Open(address, options) abort
let l:mode = get(a:options, 'mode', 'raw')
let l:Callback = a:options.callback
let l:channel_info = {
\ 'address': a:address,
\ 'mode': l:mode,
\ 'callback': a:options.callback,
\}
if !has('nvim')
" Vim
let l:channel_info.channel = ch_open(a:address, {
\ 'mode': l:mode,
\ 'waittime': 0,
\ 'callback': function('s:VimOutputCallback'),
\})
let l:vim_info = ch_info(l:channel_info.channel)
let l:channel_id = !empty(l:vim_info) ? l:vim_info.id : -1
elseif exists('*chansend') && exists('*sockconnect')
" NeoVim 0.3+
try
let l:channel_id = sockconnect('tcp', a:address, {
\ 'on_data': function('s:NeoVimOutputCallback'),
\})
let l:channel_info.last_line = ''
catch /connection failed/
let l:channel_id = -1
endtry
" 0 means the connection failed some times in NeoVim, so make the ID
" invalid to match Vim.
if l:channel_id is 0
let l:channel_id = -1
endif
let l:channel_info.channel = l:channel_id
else
" Other Vim versions.
let l:channel_id = -1
endif
if l:channel_id >= 0
let s:channel_map[l:channel_id] = l:channel_info
endif
return l:channel_id
endfunction
" Return 1 is a channel is open, 0 otherwise.
function! ale#socket#IsOpen(channel_id) abort
if !has_key(s:channel_map, a:channel_id)
return 0
endif
if has('nvim')
" In NeoVim, we have to check if this channel is in the global list.
return index(map(nvim_list_chans(), 'v:val.id'), a:channel_id) >= 0
endif
let l:channel = s:channel_map[a:channel_id].channel
return ch_status(l:channel) is# 'open'
endfunction
" Close a socket, if it's still open.
function! ale#socket#Close(channel_id) abort
" IsRunning isn't called here, so we don't check nvim_list_chans()
if !has_key(s:channel_map, a:channel_id)
return 0
endif
let l:channel = remove(s:channel_map, a:channel_id).channel
if has('nvim')
silent! call chanclose(l:channel)
elseif ch_status(l:channel) is# 'open'
call ch_close(l:channel)
endif
endfunction
" Send some data to a socket.
function! ale#socket#Send(channel_id, data) abort
if !has_key(s:channel_map, a:channel_id)
return
endif
let l:channel = s:channel_map[a:channel_id].channel
if has('nvim')
call chansend(l:channel, a:data)
else
call ch_sendraw(l:channel, a:data)
endif
endfunction
" Get an address for a channel, or an empty string.
function! ale#socket#GetAddress(channel_id) abort
return get(get(s:channel_map, a:channel_id, {}), 'address', '')
endfunction

View file

@ -1,14 +1,6 @@
" Author: KabbAmine <amine.kabb@gmail.com> " Author: KabbAmine <amine.kabb@gmail.com>
" Description: Statusline related function(s) " Description: Statusline related function(s)
" remove in 2.0
"
" A deprecated setting for ale#statusline#Status()
" See :help ale#statusline#Count() for getting status reports.
let g:ale_statusline_format = get(g:, 'ale_statusline_format',
\ ['%d error(s)', '%d warning(s)', 'OK']
\)
function! s:CreateCountDict() abort function! s:CreateCountDict() abort
" Keys 0 and 1 are for backwards compatibility. " Keys 0 and 1 are for backwards compatibility.
" The count object used to be a List of [error_count, warning_count]. " The count object used to be a List of [error_count, warning_count].
@ -76,47 +68,3 @@ function! ale#statusline#Count(buffer) abort
" The Dictionary is copied here before exposing it to other plugins. " The Dictionary is copied here before exposing it to other plugins.
return copy(s:GetCounts(a:buffer)) return copy(s:GetCounts(a:buffer))
endfunction endfunction
" This is the historical format setting which could be configured before.
function! s:StatusForListFormat() abort
let [l:error_format, l:warning_format, l:no_errors] = g:ale_statusline_format
let l:counts = s:GetCounts(bufnr(''))
" Build strings based on user formatting preferences.
let l:errors = l:counts[0] ? printf(l:error_format, l:counts[0]) : ''
let l:warnings = l:counts[1] ? printf(l:warning_format, l:counts[1]) : ''
" Different formats based on the combination of errors and warnings.
if empty(l:errors) && empty(l:warnings)
let l:res = l:no_errors
elseif !empty(l:errors) && !empty(l:warnings)
let l:res = printf('%s %s', l:errors, l:warnings)
else
let l:res = empty(l:errors) ? l:warnings : l:errors
endif
return l:res
endfunction
" remove in 2.0
"
" Returns a formatted string that can be integrated in the statusline.
"
" This function is deprecated, and should not be used. Use the airline plugin
" instead, or write your own status function with ale#statusline#Count()
function! ale#statusline#Status() abort
if !get(g:, 'ale_deprecation_ale_statusline_status', 0)
execute 'echom ''ale#statusline#Status() is deprecated, use ale#statusline#Count() to write your own function.'''
let g:ale_deprecation_ale_statusline_status = 1
endif
if !exists('g:ale_statusline_format')
return 'OK'
endif
if type(g:ale_statusline_format) == type([])
return s:StatusForListFormat()
endif
return ''
endfunction

View file

@ -48,7 +48,7 @@ function! ale#toggle#Toggle() abort
endif endif
endif endif
call ale#autocmd#InitAuGroups() call ale#events#Init()
endfunction endfunction
function! ale#toggle#Enable() abort function! ale#toggle#Enable() abort

View file

@ -17,11 +17,18 @@ endfunction
" but NeoVim does. Small messages can be echoed in Vim 8, and larger messages " but NeoVim does. Small messages can be echoed in Vim 8, and larger messages
" have to be shown in preview windows. " have to be shown in preview windows.
function! ale#util#ShowMessage(string) abort function! ale#util#ShowMessage(string) abort
if !has('nvim')
call ale#preview#CloseIfTypeMatches('ale-preview.message')
endif
" We have to assume the user is using a monospace font. " We have to assume the user is using a monospace font.
if has('nvim') || (a:string !~? "\n" && len(a:string) < &columns) if has('nvim') || (a:string !~? "\n" && len(a:string) < &columns)
execute 'echo a:string' execute 'echo a:string'
else else
call ale#preview#Show(split(a:string, "\n")) call ale#preview#Show(split(a:string, "\n"), {
\ 'filetype': 'ale-preview.message',
\ 'stay_here': 1,
\})
endif endif
endfunction endfunction
@ -39,6 +46,33 @@ if !exists('g:ale#util#nul_file')
endif endif
endif endif
" Given a job, a buffered line of data, a list of parts of lines, a mode data
" is being read in, and a callback, join the lines of output for a NeoVim job
" or socket together, and call the callback with the joined output.
"
" Note that jobs and IDs are the same thing on NeoVim.
function! ale#util#JoinNeovimOutput(job, last_line, data, mode, callback) abort
if a:mode is# 'raw'
call a:callback(a:job, join(a:data, "\n"))
return ''
endif
let l:lines = a:data[:-2]
if len(a:data) > 1
let l:lines[0] = a:last_line . l:lines[0]
let l:new_last_line = a:data[-1]
else
let l:new_last_line = a:last_line . get(a:data, 0, '')
endif
for l:line in l:lines
call a:callback(a:job, l:line)
endfor
return l:new_last_line
endfunction
" Return the number of lines for a given buffer. " Return the number of lines for a given buffer.
function! ale#util#GetLineCount(buffer) abort function! ale#util#GetLineCount(buffer) abort
return len(getbufline(a:buffer, 1, '$')) return len(getbufline(a:buffer, 1, '$'))
@ -56,8 +90,11 @@ function! ale#util#Open(filename, line, column, options) abort
if get(a:options, 'open_in_tab', 0) if get(a:options, 'open_in_tab', 0)
call ale#util#Execute('tabedit ' . fnameescape(a:filename)) call ale#util#Execute('tabedit ' . fnameescape(a:filename))
else else
" Open another file only if we need to.
if bufnr(a:filename) isnot bufnr('')
call ale#util#Execute('edit ' . fnameescape(a:filename)) call ale#util#Execute('edit ' . fnameescape(a:filename))
endif endif
endif
call cursor(a:line, a:column) call cursor(a:line, a:column)
endfunction endfunction
@ -241,16 +278,6 @@ function! ale#util#InSandbox() abort
return 0 return 0
endfunction endfunction
" Get the number of milliseconds since some vague, but consistent, point in
" the past.
"
" This function can be used for timing execution, etc.
"
" The time will be returned as a Number.
function! ale#util#ClockMilliseconds() abort
return float2nr(reltimefloat(reltime()) * 1000)
endfunction
" Given a single line, or a List of lines, and a single pattern, or a List " Given a single line, or a List of lines, and a single pattern, or a List
" of patterns, return all of the matches for the lines(s) from the given " of patterns, return all of the matches for the lines(s) from the given
" patterns, using matchlist(). " patterns, using matchlist().

View file

@ -0,0 +1,14 @@
===============================================================================
ALE CloudFormation Integration *ale-cloudformation-options*
===============================================================================
cfn-python-lint *ale-cloudformation-cfn-python-lint*
cfn-python-lint is a linter for AWS CloudFormation template file.
https://github.com/awslabs/cfn-python-lint
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View file

@ -35,4 +35,37 @@ g:ale_dart_dartanalyzer_executable *g:ale_dart_dartanalyzer_executable*
=============================================================================== ===============================================================================
dartfmt *ale-dart-dartfmt*
Installation
-------------------------------------------------------------------------------
Installing Dart should probably ensure that `dartfmt` is in your `$PATH`.
In case it is not, try to set the executable option to its absolute path. : >
" Set the executable path for dartfmt to the absolute path to it.
let g:ale_dart_dartfmt_executable = '/usr/lib/dart/bin/dartfmt'
>
Options
-------------------------------------------------------------------------------
g:ale_dart_dartfmt_executable *g:ale_dart_dartfmt_executable*
*b:ale_dart_dartfmt_executable*
Type: |String|
Default: `''`
This variable can be set to specify an absolute path to the
dartfmt executable (or to specify an alternate executable).
g:ale_dart_dartfmt_options *g:ale_dart_dartfmt_options*
*b:ale_dart_dartfmt_options*
Type: |String|
Default: `''`
This variable can be set to pass additional options to the dartfmt fixer.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View file

@ -0,0 +1,223 @@
*ale-development.txt* For Vim version 8.0.
*ale-development*
ALE Development Documentation
===============================================================================
CONTENTS *ale-development-contents*
1. Introduction.........................|ale-development-introduction|
2. Design Goals.........................|ale-design-goals|
3. Coding Standards.....................|ale-coding-standards|
4. Testing ALE..........................|ale-development-tests|
===============================================================================
1. Introduction *ale-development-introduction*
This document contains helpful information for ALE developers, including
design goals, information on how to run the tests, coding standards, and so
on. You should read this document if you want to get involved with ALE
development.
===============================================================================
2. Design Goals *ale-design-goals*
This section lists design goals for ALE, in no particular order. They are as
follows.
ALE code should be almost 100% VimL. This makes the plugin as portable as
possible.
ALE should run without needing any other plugins to be installed, to make
installation simple. ALE can integrate with other plugins for more advanced
functionality, non-essential functionality, or improving on basic first party
functionality.
ALE should check files with as many tools as possible by default, except where
they cause security issues or make excessive use of resources on modern
machines.
ALE should be free of breaking changes to the public API, which is comprised of
documented functions and options, until a major version is planned. Breaking
changes should be preceded by a deprecation phase complete with warnings.
Changes required for security may be an exception.
ALE supports Vim 8 and above, and NeoVim 0.2.0 or newer. These are the
earliest versions of Vim and NeoVim which support |job|, |timer|, |closure|,
and |lambda| features. All ALE code should be written so it is compatible with
these versions of Vim, or with version checks so particular features can
degrade or fail gracefully.
Just about everything should be documented and covered with tests.
By and large, people shouldn't pay for the functionality they don't use. Care
should be taken when adding new features, so supporting new features doesn't
degrade the general performance of anything ALE does.
LSP support will become more important as time goes on. ALE should provide
better support for LSP features as time goes on.
When merging pull requests, you should respond with `Cheers! :beers:`, purely
for comedy value.
===============================================================================
3. Coding Standards *ale-coding-standards*
The following general coding standards should be adhered to for Vim code.
* Check your Vim code with `Vint` and do everything it says. ALE will check
your Vim code with Vint automatically. See: https://github.com/Kuniwak/vint
Read ALE's `Dockerfile` to see which version of `Vint` it uses.
* Try to write descriptive and concise names for variables and functions.
Names shouldn't be too short or too long. Think about others reading your
code later on.
* Use `snake_case` names for variables and arguments, and `PascalCase` names
for functions. Prefix every variable name with its scope. (`l:`, `g:`, etc.)
* Try to keep lines no longer than 80 characters, but this isn't an absolute
requirement.
* Use 4 spaces for every level of indentation in Vim code.
* Add a blank line before every `function`, `if`, `for`, `while`, or `return`,
which doesn't start a new level of indentation. This makes the logic in
your code easier to follow.
* End every file with a trailing newline character, but not with extra blank
lines. Remove trailing whitespace from the ends of lines.
* Write the full names of commands instead of abbreviations. For example, write
`function` instead of `func`, and `endif` instead of `end`.
* Write functions with `!`, so files can be reloaded. Use the |abort| keyword
for all functions, so functions exit on the first error.
* Make sure to credit yourself in files you have authored with `Author:`
and `Description:` comments.
In addition to the above general guidelines for the style of your code, you
should also follow some additional rules designed to prevent mistakes. Some of
these are reported with ALE's `custom-linting-rules` script. See
|ale-development-tests|.
* Don't leave stray `:echo` lines in code. Use `execute 'echo' ...` if you must
echo something.
* For strings use |is#| instead of |==#|, `is?` instead of `==?`, `isnot#`
instead of `!=#`, and `isnot?` instead of `!=?`. This is because `'x' ==# 0`
returns 1, while `'x' is# 0` returns 0, so you will experience fewer issues
when numbers are compared with strings. `is` and `isnot` also do not throw
errors when other objects like List or Dictionaries are compared with
strings.
* Don't use the `getcwd()` function in the ALE codebase. Most of ALE's code
runs from asynchronous callback functions, and these functions can execute
from essentially random buffers. Therefore, the `getcwd()` output is
useless. Use `expand('#' . a:buffer . ':p:h')` instead. Don't use
`expand('%...')` for the same reason.
* Don't use the `simplify()` function. It doesn't simplify paths enough. Use
`ale#path#Simplify()` instead.
* Don't use the `shellescape()` function. It doesn't escape arguments properly
on Windows. Use `ale#Escape()` instead, which will avoid escaping where it
isn't needed, and generally escape arguments better on Windows.
Apply the following guidelines when writing Vader test files.
* Use 2 spaces for Vader test files, instead of the 4 spaces for Vim files.
* If you write `Before` and `After` blocks, you should typically write them at
the top of the file, so they run for all tests. There may be some tests
where it make sense to modify the `Before` and `After` code part of the way
through the file.
* If you modify any settings or global variables, reset them in `After`
blocks. The Vader `Save` and `Restore` commands can be useful for this
purpose.
* If you load or define linters in tests, write `call ale#linter#Reset()` in
an `After` block.
* Just write `Execute` blocks for Vader tests, and don't bother writing `Then`
blocks. `Then` blocks execute after `After` blocks in older versions, and
that can be confusing.
Apply the following rules when writing Bash scripts.
* Run `shellcheck`, and do everything it says.
See: https://github.com/koalaman/shellcheck
* Try to write scripts so they will run on Linux, BSD, or Mac OSX.
===============================================================================
4. Testing ALE *ale-development-tests*
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.
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
instructions on the Docker site for installing Docker.
See: https://docs.docker.com/install/
NOTE: Don't forget to add your user to the `docker` group on Linux, or Docker
just won't work. See: https://docs.docker.com/install/linux/linux-postinstall/
If you run simply `./run-tests` from the ALE repository root directory, the
latest Docker image for tests will be downloaded if needed, and the script
will run all of the tests in Vader, Vint checks, and several Bash scripts for
finding extra issues. Run `./run-tests --help` to see all of the options the
script supports. Note that the script supports selecting particular test files.
Generally write tests for any changes you make. The following types of tests
are recommended for the following types of code.
* New/edited error handler callbacks -> Write tests in `test/handler`
* New/edited command callbacks -> Write tests in `test/command_callback`
* New/edited fixer functions -> Write tests in `test/fixers`
Look at existing tests in the codebase for examples of how to write tests.
Refer to the Vader documentation for general information on how to write Vader
tests: https://github.com/junegunn/vader.vim
When you add new linters or fixers, make sure to add them into the table in
the README, and also into the |ale-support| list in the main help file. If you
forget to keep them both in sync, you should see an error like the following
in Travis CI. >
========================================
diff README.md and doc/ale.txt tables
========================================
Differences follow:
--- /tmp/readme.qLjNhJdB 2018-07-01 16:29:55.590331972 +0100
+++ /tmp/doc.dAi8zfVE 2018-07-01 16:29:55.582331877 +0100
@@ -1 +1 @@
- ASM: gcc, foobar
+ ASM: gcc
<
Make sure to list documentation entries for linters and fixers in individual
help files in the table of contents, and to align help tags to the right
margin. For example, if you add a heading for an `aardvark` tool to
`ale-python.txt` with a badly aligned doc tag, you will see errors like so. >
========================================
Look for badly aligned doc tags
========================================
Badly aligned tags follow:
doc/ale-python.txt:aardvark ...
========================================
Look for table of contents issues
========================================
Check for bad ToC sorting:
Check for mismatched ToC and headings:
--- /tmp/table-of-contents.mwCFOgSI 2018-07-01 16:33:25.068811878 +0100
+++ /tmp/headings.L4WU0hsO 2018-07-01 16:33:25.076811973 +0100
@@ -168,6 +168,7 @@
pyrex (cython), ale-pyrex-options
cython, ale-pyrex-cython
python, ale-python-options
+ aardvark, ale-python-aardvark
autopep8, ale-python-autopep8
black, ale-python-black
flake8, ale-python-flake8
<
Make sure to make the table of contents match the headings, and to keep the
doc tags on the right margin.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View file

@ -71,6 +71,14 @@ g:ale_html_tidy_options *g:ale_html_tidy_options*
(mac), sjis (shiftjis), utf-16le, utf-16, utf-8 (mac), sjis (shiftjis), utf-16le, utf-16, utf-8
g:ale_html_tidy_use_global *g:html_tidy_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
=============================================================================== ===============================================================================
write-good *ale-html-write-good* write-good *ale-html-write-good*

View file

@ -0,0 +1,25 @@
===============================================================================
ALE Pyrex (Cython) Integration *ale-pyrex-options*
===============================================================================
cython *ale-pyrex-cython*
g:ale_pyrex_cython_executable *g:ale_pyrex_cython_executable*
*b:ale_pyrex_cython_executable*
Type: |String|
Default: `'cython'`
This variable can be changed to use a different executable for cython.
g:ale_pyrex_cython_options *g:ale_pyrex_cython_options*
*b:ale_pyrex_cython_options*
Type: |String|
Default: `'--warning-extra --warning-errors'`
This variable can be changed to modify flags given to cython.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View file

@ -363,6 +363,30 @@ g:ale_python_pyls_use_global *g:ale_python_pyls_use_global*
See |ale-integrations-local-executables| See |ale-integrations-local-executables|
===============================================================================
pyre *ale-python-pyre*
`pyre` will be run from a detected project root, per |ale-python-root|.
g:ale_python_pyre_executable *g:ale_python_pyre_executable*
*b:ale_python_pyre_executable*
Type: |String|
Default: `'pyre'`
See |ale-integrations-local-executables|
Set this to `'pipenv'` to invoke `'pipenv` `run` `pyre'`.
g:ale_python_pyre_use_global *g:ale_python_pyre_use_global*
*b:ale_python_pyre_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
=============================================================================== ===============================================================================
yapf *ale-python-yapf* yapf *ale-python-yapf*

View file

@ -108,6 +108,15 @@ g:ale_rust_cargo_include_features *g:ale_rust_cargo_include_features*
When defined, ALE will set the `--features` option when invoking `cargo` to When defined, ALE will set the `--features` option when invoking `cargo` to
perform the lint check. See |g:ale_rust_cargo_default_feature_behavior|. perform the lint check. See |g:ale_rust_cargo_default_feature_behavior|.
g:ale_rust_cargo_avoid_whole_workspace *g:ale_rust_cargo_avoid_whole_workspace*
*b:ale_rust_cargo_avoid_whole_workspace*
Type: |Number|
Default: `1`
When set to 1, and ALE is used to edit a crate that is part of a Cargo
workspace, avoid building the entire entire workspace by invoking
`cargo` directly in the crate's directory. Otherwise, behave as usual.
=============================================================================== ===============================================================================
rls *ale-rust-rls* rls *ale-rust-rls*

View file

@ -2,6 +2,25 @@
ALE Shell Integration *ale-sh-options* ALE Shell Integration *ale-sh-options*
===============================================================================
sh-language-server *ale-sh-language-server*
g:ale_sh_language_server_executable *g:ale_sh_language_server_executable*
*b:ale_sh_language_server_executable*
Type: |String|
Default: `'bash-language-server'`
See |ale-integrations-local-executables|
g:ale_sh_language_server_use_global *g:ale_sh_language_server_use_global*
*b:ale_sh_language_server_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
=============================================================================== ===============================================================================
shell *ale-sh-shell* shell *ale-sh-shell*

View file

@ -35,6 +35,8 @@ CONTENTS *ale-contents*
foodcritic..........................|ale-chef-foodcritic| foodcritic..........................|ale-chef-foodcritic|
clojure...............................|ale-clojure-options| clojure...............................|ale-clojure-options|
joker...............................|ale-clojure-joker| joker...............................|ale-clojure-joker|
cloudformation........................|ale-cloudformation-options|
cfn-python-lint.....................|ale-cloudformation-cfn-python-lint|
cmake.................................|ale-cmake-options| cmake.................................|ale-cmake-options|
cmakelint...........................|ale-cmake-cmakelint| cmakelint...........................|ale-cmake-cmakelint|
cpp...................................|ale-cpp-options| cpp...................................|ale-cpp-options|
@ -57,6 +59,7 @@ CONTENTS *ale-contents*
nvcc................................|ale-cuda-nvcc| nvcc................................|ale-cuda-nvcc|
dart..................................|ale-dart-options| dart..................................|ale-dart-options|
dartanalyzer........................|ale-dart-dartanalyzer| dartanalyzer........................|ale-dart-dartanalyzer|
dartfmt.............................|ale-dart-dartfmt|
dockerfile............................|ale-dockerfile-options| dockerfile............................|ale-dockerfile-options|
hadolint............................|ale-dockerfile-hadolint| hadolint............................|ale-dockerfile-hadolint|
elixir................................|ale-elixir-options| elixir................................|ale-elixir-options|
@ -183,6 +186,8 @@ CONTENTS *ale-contents*
puglint.............................|ale-pug-puglint| puglint.............................|ale-pug-puglint|
puppet................................|ale-puppet-options| puppet................................|ale-puppet-options|
puppetlint..........................|ale-puppet-puppetlint| puppetlint..........................|ale-puppet-puppetlint|
pyrex (cython)........................|ale-pyrex-options|
cython..............................|ale-pyrex-cython|
python................................|ale-python-options| python................................|ale-python-options|
autopep8............................|ale-python-autopep8| autopep8............................|ale-python-autopep8|
black...............................|ale-python-black| black...............................|ale-python-black|
@ -194,6 +199,7 @@ CONTENTS *ale-contents*
pyflakes............................|ale-python-pyflakes| pyflakes............................|ale-python-pyflakes|
pylint..............................|ale-python-pylint| pylint..............................|ale-python-pylint|
pyls................................|ale-python-pyls| pyls................................|ale-python-pyls|
pyre................................|ale-python-pyre|
yapf................................|ale-python-yapf| yapf................................|ale-python-yapf|
qml...................................|ale-qml-options| qml...................................|ale-qml-options|
qmlfmt..............................|ale-qml-qmlfmt| qmlfmt..............................|ale-qml-qmlfmt|
@ -226,6 +232,7 @@ CONTENTS *ale-contents*
prettier............................|ale-scss-prettier| prettier............................|ale-scss-prettier|
stylelint...........................|ale-scss-stylelint| stylelint...........................|ale-scss-stylelint|
sh....................................|ale-sh-options| sh....................................|ale-sh-options|
sh-language-server..................|ale-sh-language-server|
shell...............................|ale-sh-shell| shell...............................|ale-sh-shell|
shellcheck..........................|ale-sh-shellcheck| shellcheck..........................|ale-sh-shellcheck|
shfmt...............................|ale-sh-shfmt| shfmt...............................|ale-sh-shfmt|
@ -302,6 +309,9 @@ control functionality used for checking for problems. Try using the
|ALEFixSuggest| command for browsing tools that can be used to fix problems |ALEFixSuggest| command for browsing tools that can be used to fix problems
for the current buffer. for the current buffer.
If you are interested in contributing to the development of ALE, read the
developer documentation. See |ale-development|
=============================================================================== ===============================================================================
2. Supported Languages & Tools *ale-support* 2. Supported Languages & Tools *ale-support*
@ -317,7 +327,7 @@ Notes:
* API Blueprint: `drafter` * API Blueprint: `drafter`
* AsciiDoc: `alex`!!, `proselint`, `redpen`, `write-good` * AsciiDoc: `alex`!!, `proselint`, `redpen`, `write-good`
* Awk: `gawk` * Awk: `gawk`
* Bash: `shell` (-n flag), `shellcheck`, `shfmt` * Bash: `language-server`, `shell` (-n flag), `shellcheck`, `shfmt`
* Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt` * Bourne Shell: `shell` (-n flag), `shellcheck`, `shfmt`
* C: `cppcheck`, `cpplint`!!, `clang`, `clangtidy`!!, `clang-format`, `flawfinder`, `gcc` * C: `cppcheck`, `cpplint`!!, `clang`, `clangtidy`!!, `clang-format`, `flawfinder`, `gcc`
* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc` * C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `clang-format`, `cppcheck`, `cpplint`!!, `cquery`, `flawfinder`, `gcc`
@ -325,6 +335,7 @@ Notes:
* C#: `mcs`, `mcsc`!! * C#: `mcs`, `mcsc`!!
* Chef: `foodcritic` * Chef: `foodcritic`
* Clojure: `joker` * Clojure: `joker`
* CloudFormation: `cfn-python-lint`
* CMake: `cmakelint` * CMake: `cmakelint`
* CoffeeScript: `coffee`, `coffeelint` * CoffeeScript: `coffee`, `coffeelint`
* Crystal: `crystal`!! * Crystal: `crystal`!!
@ -333,9 +344,9 @@ Notes:
* Cython (pyrex filetype): `cython` * Cython (pyrex filetype): `cython`
* D: `dmd` * D: `dmd`
* Dafny: `dafny`!! * Dafny: `dafny`!!
* Dart: `dartanalyzer`!!, `language_server` * Dart: `dartanalyzer`!!, `language_server`, dartfmt!!
* Dockerfile: `hadolint` * Dockerfile: `hadolint`
* Elixir: `credo`, `dialyxir`, `dogma`!! * Elixir: `credo`, `dialyxir`, `dogma`, `mix`!!
* Elm: `elm-format, elm-make` * Elm: `elm-format, elm-make`
* Erb: `erb`, `erubi`, `erubis` * Erb: `erb`, `erubi`, `erubis`
* Erlang: `erlc`, `SyntaxErl` * Erlang: `erlc`, `SyntaxErl`
@ -380,7 +391,7 @@ Notes:
* proto: `protoc-gen-lint` * proto: `protoc-gen-lint`
* Pug: `pug-lint` * Pug: `pug-lint`
* Puppet: `puppet`, `puppet-lint` * Puppet: `puppet`, `puppet-lint`
* Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pyls`, `pylint`!!, `yapf` * Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pyls`, `pyre`, `pylint`!!, `yapf`
* QML: `qmlfmt`, `qmllint` * QML: `qmlfmt`, `qmllint`
* R: `lintr` * R: `lintr`
* ReasonML: `merlin`, `ols`, `refmt` * ReasonML: `merlin`, `ols`, `refmt`
@ -935,6 +946,14 @@ g:ale_fixers *g:ale_fixers*
`b:ale_fixers` can be set to a |List| of callbacks instead, which can be `b:ale_fixers` can be set to a |List| of callbacks instead, which can be
more convenient. more convenient.
A special `'*'` key be used as a wildcard filetype for configuring fixers
for every other type of file. For example: >
" Fix Python files with 'bar'.
" Don't fix 'html' files.
" Fix everything else with 'foo'.
let g:ale_fixers = {'python': ['bar'], 'html': [], '*': ['foo']}
<
g:ale_fix_on_save *g:ale_fix_on_save* g:ale_fix_on_save *g:ale_fix_on_save*
b:ale_fix_on_save *b:ale_fix_on_save* b:ale_fix_on_save *b:ale_fix_on_save*
@ -1114,6 +1133,7 @@ g:ale_linter_aliases *g:ale_linter_aliases*
\ 'csh': 'sh', \ 'csh': 'sh',
\ 'plaintex': 'tex', \ 'plaintex': 'tex',
\ 'systemverilog': 'verilog', \ 'systemverilog': 'verilog',
\ 'verilog_systemverilog': ['verilog_systemverilog', 'verilog'],
\ 'vimwiki': 'markdown', \ 'vimwiki': 'markdown',
\ 'zsh': 'sh', \ 'zsh': 'sh',
\} \}
@ -1219,6 +1239,32 @@ g:ale_linters_explicit *g:ale_linters_explicit*
as possible, unless otherwise specified. as possible, unless otherwise specified.
g:ale_linters_ignore *g:ale_linters_ignore*
*b:ale_linters_ignore*
Type: |Dictionary| or |List|
Default: `{}`
Linters to ignore. Commands for ignored linters will not be run, and
diagnostics for LSP linters will be ignored. (See |ale-lsp|)
This setting can be set to a |Dictionary| mapping filetypes to linter names,
just like |g:ale_linters|, to list linters to ignore. Ignore lists will be
applied after everything else. >
" Select flake8 and pylint, and ignore pylint, so only flake8 is run.
let g:ale_linters = {'python': ['flake8', 'pylint']}
let g:ale_linters_ignore = {'python': ['pylint']}
<
This setting can be set to simply a |List| of linter names, which is
especially more convenient when using the setting in ftplugin files for
particular buffers. >
" The same as above, in a ftplugin/python.vim.
let b:ale_linters = ['flake8', 'pylint']
let b:ale_linters_ignore = ['pylint']
<
g:ale_list_vertical *g:ale_list_vertical* g:ale_list_vertical *g:ale_list_vertical*
*b:ale_list_vertical* *b:ale_list_vertical*
Type: |Number| Type: |Number|
@ -1967,9 +2013,13 @@ ALEDisableBuffer *ALEDisableBuffer*
*:ALEDetail* *:ALEDetail*
ALEDetail *ALEDetail* ALEDetail *ALEDetail*
Show the full linter message for the current line in the preview window. Show the full linter message for the problem nearest to the cursor on the
This will only have an effect on lines that contain a linter message. The given line in the preview window. The preview window can be easily closed
preview window can be easily closed with the `q` key. with the `q` key. If there is no message to show, the window will not be
opened.
If a loclist item has a `detail` key set, the message for that key will be
preferred over `text`. See |ale-loclist-format|.
A plug mapping `<Plug>(ale_detail)` is defined for this command. A plug mapping `<Plug>(ale_detail)` is defined for this command.
@ -2153,13 +2203,16 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
This argument is required, unless the linter is an This argument is required, unless the linter is an
LSP linter. In which case, this argument must not be LSP linter. In which case, this argument must not be
defined, as LSP linters handle diangostics defined, as LSP linters handle diagnostics
automatically. See |ale-lsp-linters|. automatically. See |ale-lsp-linters|.
The keys for each item in the List will be handled in The keys for each item in the List will be handled in
the following manner: the following manner:
*ale-loclist-format* *ale-loclist-format*
`text` - This error message is required. `text` - This error message is required.
`detail` - An optional, more descriptive message.
This message can be displayed with the |ALEDetail|
command instead of the message for `text`, if set.
`lnum` - The line number is required. Any strings `lnum` - The line number is required. Any strings
will be automatically converted to numbers by will be automatically converted to numbers by
using `str2nr()`. using `str2nr()`.
@ -2319,8 +2372,16 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
When this argument is set to `'stdio'`, then the When this argument is set to `'stdio'`, then the
linter will be defined as an LSP linter which keeps a linter will be defined as an LSP linter which keeps a
process for a language server runnning, and process for a language server running, and
communicates with it directly via a |channel|. communicates with it directly via a |channel|.
`executable` or `executable_callback` must be set,
and `command` or `command_callback` must be set.
When this argument is set to `'socket'`, then the
linter will be defined as an LSP linter via a TCP
socket connection. `address_callback` must be set
with a callback returning an address to connect to.
ALE will not start a server automatically.
When this argument is not empty, only one of either When this argument is not empty, only one of either
`language` or `language_callback` must be defined, `language` or `language_callback` must be defined,
@ -2336,6 +2397,13 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
`initialization_options_callback` may be defined to `initialization_options_callback` may be defined to
pass initialization options to the LSP. pass initialization options to the LSP.
`address_callback` A |String| or |Funcref| for a callback function
accepting a buffer number. A |String| should be
returned with an address to connect to.
This argument must only be set if the `lsp` argument
is set to `'socket'`.
`project_root_callback` A |String| or |Funcref| for a callback function `project_root_callback` A |String| or |Funcref| for a callback function
accepting a buffer number. A |String| should be accepting a buffer number. A |String| should be
returned representing the path to the project for the returned representing the path to the project for the
@ -2564,5 +2632,5 @@ free to send an email to devw0rp@gmail.com.
Please drink responsibly, or not at all, which is ironically the preference Please drink responsibly, or not at all, which is ironically the preference
of w0rp, who is teetotal. of w0rp, who is teetotal.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View file

@ -32,12 +32,6 @@ if !s:has_features
finish finish
endif endif
" remove in 2.0
if has('nvim') && !has('nvim-0.2.0') && !get(g:, 'ale_use_deprecated_neovim')
execute 'echom ''ALE support for NeoVim versions below 0.2.0 is deprecated.'''
execute 'echom ''Use `let g:ale_use_deprecated_neovim = 1` to silence this warning for now.'''
endif
" Set this flag so that other plugins can use it, like airline. " Set this flag so that other plugins can use it, like airline.
let g:loaded_ale = 1 let g:loaded_ale = 1
@ -221,35 +215,13 @@ nnoremap <silent> <Plug>(ale_find_references) :ALEFindReferences<Return>
nnoremap <silent> <Plug>(ale_hover) :ALEHover<Return> nnoremap <silent> <Plug>(ale_hover) :ALEHover<Return>
" Set up autocmd groups now. " Set up autocmd groups now.
call ale#autocmd#InitAuGroups() call ale#events#Init()
" Housekeeping " Housekeeping
augroup ALECleanupGroup augroup ALECleanupGroup
autocmd! autocmd!
" Clean up buffers automatically when they are unloaded. " Clean up buffers automatically when they are unloaded.
autocmd BufDelete * call ale#engine#Cleanup(str2nr(expand('<abuf>'))) autocmd BufDelete * if exists('*ale#engine#Cleanup') | call ale#engine#Cleanup(str2nr(expand('<abuf>'))) | endif
autocmd QuitPre * call ale#events#QuitEvent(str2nr(expand('<abuf>'))) autocmd QuitPre * call ale#events#QuitEvent(str2nr(expand('<abuf>')))
augroup END augroup END
" Backwards Compatibility
" remove in 2.0
function! ALELint(delay) abort
if !get(g:, 'ale_deprecation_ale_lint', 0)
execute 'echom ''ALELint() is deprecated, use ale#Queue() instead.'''
let g:ale_deprecation_ale_lint = 1
endif
call ale#Queue(a:delay)
endfunction
" remove in 2.0
function! ALEGetStatusLine() abort
if !get(g:, 'ale_deprecation_ale_get_status_line', 0)
execute 'echom ''ALEGetStatusLine() is deprecated.'''
let g:ale_deprecation_ale_get_status_line = 1
endif
return ale#statusline#Status()
endfunction

View file

@ -2595,6 +2595,20 @@ fu! s:ExitIfSingleCandidate()
return 0 return 0
endfu endfu
fu! s:IsBuiltin()
let builtins = ['tag', 'dir', 'bft', 'rts', 'bkd', 'lns', 'chs', 'mix', 'udo', 'qfx']
let curtype = s:getextvar('sname')
return s:itemtype < len(s:coretypes) || index(builtins, curtype) > -1
endfu
fu! s:DetectFileType(type, ft)
if s:IsBuiltin() || empty(a:ft) || a:ft ==# 'ctrlp'
retu 'ctrlp'
el
retu 'ctrlp.' . a:ft
en
endfu
fu! ctrlp#init(type, ...) fu! ctrlp#init(type, ...)
if exists('s:init') || s:iscmdwin() | retu | en if exists('s:init') || s:iscmdwin() | retu | en
let [s:ermsg, v:errmsg] = [v:errmsg, ''] let [s:ermsg, v:errmsg] = [v:errmsg, '']
@ -2618,7 +2632,7 @@ fu! ctrlp#init(type, ...)
en en
en en
cal ctrlp#setlines(s:settype(type)) cal ctrlp#setlines(s:settype(type))
set ft=ctrlp let &filetype = s:DetectFileType(type, &filetype)
cal ctrlp#syntax() cal ctrlp#syntax()
cal s:SetDefTxt() cal s:SetDefTxt()
let curName = s:CurTypeName() let curName = s:CurTypeName()

View file

@ -2,7 +2,7 @@
" Filename: autoload/lightline.vim " Filename: autoload/lightline.vim
" Author: itchyny " Author: itchyny
" License: MIT License " License: MIT License
" Last Change: 2017/12/31 15:55:00. " Last Change: 2018/06/22 08:50:00.
" ============================================================================= " =============================================================================
let s:save_cpo = &cpo let s:save_cpo = &cpo
@ -47,8 +47,10 @@ function! lightline#enable() abort
endif endif
augroup lightline augroup lightline
autocmd! autocmd!
autocmd WinEnter,BufWinEnter,FileType,ColorScheme,SessionLoadPost * call lightline#update() autocmd WinEnter,BufWinEnter,FileType,SessionLoadPost * call lightline#update()
autocmd ColorScheme,SessionLoadPost * call lightline#highlight() autocmd SessionLoadPost * call lightline#highlight()
autocmd ColorScheme * if !has('vim_starting') || expand('<amatch>') !=# 'macvim'
\ | call lightline#update() | call lightline#highlight() | endif
autocmd CursorMoved,BufUnload * call lightline#update_once() autocmd CursorMoved,BufUnload * call lightline#update_once()
augroup END augroup END
augroup lightline-disable augroup lightline-disable

View file

@ -2,7 +2,7 @@
" Filename: plugin/lightline.vim " Filename: plugin/lightline.vim
" Author: itchyny " Author: itchyny
" License: MIT License " License: MIT License
" Last Change: 2016/03/14 03:31:58. " Last Change: 2018/06/22 08:49:00.
" ============================================================================= " =============================================================================
if exists('g:loaded_lightline') || v:version < 700 if exists('g:loaded_lightline') || v:version < 700
@ -15,8 +15,10 @@ set cpo&vim
augroup lightline augroup lightline
autocmd! autocmd!
autocmd WinEnter,BufWinEnter,FileType,ColorScheme,SessionLoadPost * call lightline#update() autocmd WinEnter,BufWinEnter,FileType,SessionLoadPost * call lightline#update()
autocmd ColorScheme,SessionLoadPost * call lightline#highlight() autocmd SessionLoadPost * call lightline#highlight()
autocmd ColorScheme * if !has('vim_starting') || expand('<amatch>') !=# 'macvim'
\ | call lightline#update() | call lightline#highlight() | endif
autocmd CursorMoved,BufUnload * call lightline#update_once() autocmd CursorMoved,BufUnload * call lightline#update_once()
augroup END augroup END

View file

@ -225,10 +225,11 @@ endfunction
function! NERDTreeListNode() function! NERDTreeListNode()
let treenode = g:NERDTreeFileNode.GetSelected() let treenode = g:NERDTreeFileNode.GetSelected()
if !empty(treenode) if !empty(treenode)
if has("osx") let s:uname = system("uname")
let stat_cmd = 'stat -f "%z" '
else
let stat_cmd = 'stat -c "%s" ' let stat_cmd = 'stat -c "%s" '
if s:uname =~? "Darwin"
let stat_cmd = 'stat -f "%z" '
endif endif
let cmd = 'size=$(' . stat_cmd . shellescape(treenode.path.str()) . ') && ' . let cmd = 'size=$(' . stat_cmd . shellescape(treenode.path.str()) . ') && ' .

View file

@ -556,6 +556,7 @@ syn keyword ngxDirective ssl_protocols nextgroup=ngxSSLProtocol,ngxSSLProtocolDe
syn match ngxSSLProtocol 'TLSv1' contained nextgroup=ngxSSLProtocol,ngxSSLProtocolDeprecated skipwhite syn match ngxSSLProtocol 'TLSv1' contained nextgroup=ngxSSLProtocol,ngxSSLProtocolDeprecated skipwhite
syn match ngxSSLProtocol 'TLSv1\.1' contained nextgroup=ngxSSLProtocol,ngxSSLProtocolDeprecated skipwhite syn match ngxSSLProtocol 'TLSv1\.1' contained nextgroup=ngxSSLProtocol,ngxSSLProtocolDeprecated skipwhite
syn match ngxSSLProtocol 'TLSv1\.2' contained nextgroup=ngxSSLProtocol,ngxSSLProtocolDeprecated skipwhite syn match ngxSSLProtocol 'TLSv1\.2' contained nextgroup=ngxSSLProtocol,ngxSSLProtocolDeprecated skipwhite
syn match ngxSSLProtocol 'TLSv1\.3' contained nextgroup=ngxSSLProtocol,ngxSSLProtocolDeprecated skipwhite
" Do not enable highlighting of insecure protocols if sslecure is loaded " Do not enable highlighting of insecure protocols if sslecure is loaded
if !exists('g:loaded_sslsecure') if !exists('g:loaded_sslsecure')

View file

@ -66,6 +66,14 @@ function! s:shellslash(path) abort
endif endif
endfunction endfunction
function! s:PlatformSlash(path) abort
if exists('+shellslash') && !&shellslash
return tr(a:path, '/', '\')
else
return a:path
endif
endfunction
let s:executables = {} let s:executables = {}
function! s:executable(binary) abort function! s:executable(binary) abort
@ -83,7 +91,7 @@ endfunction
function! fugitive#GitVersion(...) abort function! fugitive#GitVersion(...) abort
if !has_key(s:git_versions, g:fugitive_git_executable) if !has_key(s:git_versions, g:fugitive_git_executable)
let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), "\\S\\+\n") let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), "\\S\\+\\ze\n")
endif endif
return s:git_versions[g:fugitive_git_executable] return s:git_versions[g:fugitive_git_executable]
endfunction endfunction
@ -107,7 +115,7 @@ function! s:recall() abort
endfunction endfunction
function! s:map(mode, lhs, rhs, ...) abort function! s:map(mode, lhs, rhs, ...) abort
let flags = (a:0 ? a:1 : '') . (a:rhs =~# '^<Plug>' ? '' : '<script>') let flags = (a:0 ? a:1 : '') . (a:rhs =~# '<Plug>' ? '' : '<script>')
let head = a:lhs let head = a:lhs
let tail = '' let tail = ''
let keys = get(g:, a:mode.'remap', {}) let keys = get(g:, a:mode.'remap', {})
@ -242,10 +250,6 @@ function! s:repo_bare() dict abort
endfunction endfunction
function! s:repo_translate(spec) dict abort function! s:repo_translate(spec) dict abort
let refs = self.dir('refs/')
if filereadable(self.dir('commondir'))
let refs = simplify(self.dir(get(readfile(self.dir('commondir'), 1), 0, ''))) . '/refs/'
endif
if a:spec ==# '.' || a:spec ==# '/.' if a:spec ==# '.' || a:spec ==# '/.'
return self.bare() ? self.dir() : self.tree() return self.bare() ? self.dir() : self.tree()
elseif a:spec =~# '^/\=\.git$' && self.bare() elseif a:spec =~# '^/\=\.git$' && self.bare()
@ -267,23 +271,18 @@ function! s:repo_translate(spec) dict abort
return 'fugitive://'.self.dir().'//'.ref return 'fugitive://'.self.dir().'//'.ref
elseif a:spec =~# '^:' elseif a:spec =~# '^:'
return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1] return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1]
elseif a:spec ==# '@' else
return self.dir('HEAD') let refs = self.dir('refs/')
elseif a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(refs . '../' . a:spec) if filereadable(self.dir('commondir'))
let refs = simplify(self.dir(get(readfile(self.dir('commondir'), 1), 0, ''))) . '/refs/'
endif
if a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(refs . '../' . a:spec)
return simplify(refs . '../' . a:spec) return simplify(refs . '../' . a:spec)
elseif filereadable(refs.a:spec) elseif filereadable(refs.a:spec)
return refs.a:spec return refs.a:spec
elseif filereadable(refs.'tags/'.a:spec) endif
return refs.'tags/'.a:spec
elseif filereadable(refs.'heads/'.a:spec)
return refs.'heads/'.a:spec
elseif filereadable(refs.'remotes/'.a:spec)
return refs.'remotes/'.a:spec
elseif filereadable(refs.'remotes/'.a:spec.'/HEAD')
return refs.'remotes/'.a:spec.'/HEAD'
else
try try
let ref = self.rev_parse(matchstr(a:spec,'[^:]*')) let ref = self.rev_parse(s:sub(matchstr(a:spec,'[^:]*'), '^\@%($|[^~])@=', 'HEAD'))
let path = s:sub(matchstr(a:spec,':.*'),'^:','/') let path = s:sub(matchstr(a:spec,':.*'),'^:','/')
return 'fugitive://'.self.dir().'//'.ref.path return 'fugitive://'.self.dir().'//'.ref.path
catch /^fugitive:/ catch /^fugitive:/
@ -429,6 +428,204 @@ call s:add_methods('repo',['keywordprg'])
" Section: Buffer " Section: Buffer
function! s:DirCommitFile(path) abort
let vals = matchlist(s:shellslash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\w\+\)\(/.*\)\=$')
if empty(vals)
return ['', '', '']
endif
return [vals[1], (vals[2] =~# '^.$' ? ':' : '') . vals[2], vals[3]]
endfunction
function! fugitive#Path(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
if len(dir)
let tree = FugitiveTreeForGitDir(dir)
return s:PlatformSlash((len(tree) ? tree : dir) . file)
elseif a:url =~# '^[\\/]\|^\a:[\\/]'
return s:PlatformSlash(a:url)
endif
return ''
endfunction
function! fugitive#Real(url) abort
return fugitive#Path(a:url)
endfunction
let s:trees = {}
let s:indexes = {}
function! s:TreeInfo(dir, commit) abort
let git = g:fugitive_git_executable . ' --git-dir=' . s:shellesc(a:dir)
if a:commit =~# '^:\=[0-3]$'
let index = get(s:indexes, a:dir, [])
let newftime = getftime(a:dir . '/index')
if get(index, 0, -1) < newftime
let out = system(git . ' ls-files --stage')
let s:indexes[a:dir] = [newftime, {'0': {}, '1': {}, '2': {}, '3': {}}]
if v:shell_error
return [{}, -1]
endif
for line in split(out, "\n")
let [info, filename] = split(line, "\t")
let [mode, sha, stage] = split(info, '\s\+')
let s:indexes[a:dir][1][stage][filename] = [newftime, mode, 'blob', sha, -2]
while filename =~# '/'
let filename = substitute(filename, '/[^/]*$', '', '')
let s:indexes[a:dir][1][stage][filename] = [newftime, '040000', 'tree', '', 0]
endwhile
endfor
endif
return [get(s:indexes[a:dir][1], a:commit[-1:-1], {}), newftime]
elseif a:commit =~# '^\x\{40\}$'
if !has_key(s:trees, a:dir)
let ftime = +system(git . ' log -1 --pretty=format:%ct ' . a:commit)
if v:shell_error
let s:trees[a:dir] = [{}, -1]
return s:trees[a:dir]
endif
let s:trees[a:dir] = [{}, +ftime]
let out = system(git . ' ls-tree -rtl --full-name ' . a:commit)
if v:shell_error
return s:trees[a:dir]
endif
for line in split(out, "\n")
let [info, filename] = split(line, "\t")
let [mode, type, sha, size] = split(info, '\s\+')
let s:trees[a:dir][0][filename] = [ftime, mode, type, sha, +size, filename]
endfor
endif
return s:trees[a:dir]
endif
return [{}, -1]
endfunction
function! s:PathInfo(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
if empty(dir) || !get(g:, 'fugitive_file_api', 1)
return [-1, '000000', '', '', -1]
endif
let path = substitute(file[1:-1], '/*$', '', '')
let [tree, ftime] = s:TreeInfo(dir, commit)
let entry = empty(path) ? [ftime, '040000', 'tree', '', -1] : get(tree, path, [])
if empty(entry) || file =~# '/$' && entry[1] !=# 'tree'
return [-1, '000000', '', '', -1]
else
return entry
endif
endfunction
function! fugitive#simplify(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
if empty(dir)
return ''
endif
if file =~# '/\.\.\%(/\|$\)'
let tree = FugitiveTreeForGitDir(dir)
if len(tree)
let path = simplify(tree . file)
if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/'
return s:PlatformSlash(path)
endif
endif
endif
return s:PlatformSlash('fugitive://' . simplify(dir) . '//' . commit . simplify(file))
endfunction
function! fugitive#resolve(url) abort
let url = fugitive#simplify(a:url)
if url =~? '^fugitive:'
return url
else
return resolve(url)
endif
endfunction
function! fugitive#getftime(url) abort
return s:PathInfo(a:url)[0]
endfunction
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(g:fugitive_git_executable . ' ' . s:shellesc('--git-dir=' . dir) . ' cat-file -s ' . entry[3])
let entry[4] = v:shell_error ? -1 : size
endif
return entry[4]
endfunction
function! fugitive#getftype(url) abort
return get({'tree': 'dir', 'blob': 'file'}, s:PathInfo(a:url)[2], '')
endfunction
function! fugitive#filereadable(url) abort
return s:PathInfo(a:url)[2] ==# 'blob'
endfunction
function! fugitive#isdirectory(url) abort
return s:PathInfo(a:url)[2] ==# 'tree'
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, commit, file] = s:DirCommitFile(a:url)
let cmd = g:fugitive_git_executable . ' --git-dir=' . s:shellesc(dir) .
\ ' cat-file blob ' . s:shellesc(commit . ':' . file[1:-1])
if max > 0 && s:executable('head')
let cmd .= '|head -' . max
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)
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
endfunction
let s:globsubs = {'*': '[^/]*', '**': '.*', '**/': '\%(.*/\)\=', '?': '[^/]'}
function! fugitive#glob(url, ...) abort
let [dirglob, commit, glob] = s:DirCommitFile(a:url)
let append = matchstr(glob, '/*$')
let glob = substitute(glob, '/*$', '', '')
let pattern = '^' . substitute(glob[1:-1], '\*\*/\=\|[.?*\^$]', '\=get(s:globsubs, submatch(0), "\\" . submatch(0))', 'g') . '$'
let results = []
for dir in dirglob =~# '[*?]' ? split(glob(dirglob), "\n") : [dirglob]
if empty(dir) || !get(g:, 'fugitive_file_api', 1) || !filereadable(dir . '/HEAD')
continue
endif
let files = items(s:TreeInfo(dir, commit)[0])
if len(append)
call filter(files, 'v:val[1][2] ==# "tree"')
endif
call map(files, 'v:val[0]')
call filter(files, 'v:val =~# pattern')
let prepend = 'fugitive://' . dir . '//' . substitute(commit, '^:', '', '') . '/'
call sort(files)
call map(files, 's:PlatformSlash(prepend . v:val . append)')
call extend(results, files)
endfor
if a:0 > 1 && a:2
return results
else
return join(results, "\n")
endif
endfunction
let s:buffer_prototype = {} let s:buffer_prototype = {}
function! s:buffer(...) abort function! s:buffer(...) abort
@ -510,7 +707,7 @@ function! s:buffer_name() dict abort
endfunction endfunction
function! s:buffer_commit() dict abort function! s:buffer_commit() dict abort
return matchstr(self.spec(),'^fugitive://.\{-\}//\zs\w*') return matchstr(self.spec(),'^fugitive:\%(//\)\=.\{-\}\%(//\|::\)\zs\w*')
endfunction endfunction
function! s:cpath(path) abort function! s:cpath(path) abort
@ -521,8 +718,8 @@ function! s:cpath(path) abort
endif endif
endfunction endfunction
function! s:buffer_path(...) dict abort function! s:buffer_relative(...) dict abort
let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*') let rev = matchstr(self.spec(),'^fugitive:\%(//\)\=.\{-\}\%(//\|::\)\zs.*')
if rev != '' if rev != ''
let rev = s:sub(rev,'\w*','') let rev = s:sub(rev,'\w*','')
elseif s:cpath(self.spec()[0 : len(self.repo().dir())]) ==# elseif s:cpath(self.spec()[0 : len(self.repo().dir())]) ==#
@ -536,8 +733,15 @@ function! s:buffer_path(...) dict abort
return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '') return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '')
endfunction endfunction
function! s:buffer_path(...) dict abort
if a:0
return self.relative(a:1)
endif
return self.relative()
endfunction
function! s:buffer_rev() dict abort function! s:buffer_rev() dict abort
let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*') let rev = matchstr(self.spec(),'^fugitive:\%(//\)\=.\{-\}\%(//\|::\)\zs.*')
if rev =~ '^\x/' if rev =~ '^\x/'
return ':'.rev[0].':'.rev[2:-1] return ':'.rev[0].':'.rev[2:-1]
elseif rev =~ '.' elseif rev =~ '.'
@ -547,12 +751,12 @@ function! s:buffer_rev() dict abort
elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$' elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
return self.spec()[strlen(self.repo().dir())+1 : -1] return self.spec()[strlen(self.repo().dir())+1 : -1]
else else
return self.path('/') return self.relative('/')
endif endif
endfunction endfunction
function! s:buffer_sha1() dict abort function! s:buffer_sha1() dict abort
if self.spec() =~ '^fugitive://' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$' if self.spec() =~? '^fugitive:' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
return self.repo().rev_parse(self.rev()) return self.repo().rev_parse(self.rev())
else else
return '' return ''
@ -561,21 +765,21 @@ endfunction
function! s:buffer_expand(rev) dict abort function! s:buffer_expand(rev) dict abort
if a:rev =~# '^:[0-3]$' if a:rev =~# '^:[0-3]$'
let file = a:rev.self.path(':') let file = a:rev.self.relative(':')
elseif a:rev =~# '^[-:]/$' elseif a:rev =~# '^[-:]/$'
let file = '/'.self.path() let file = '/'.self.relative()
elseif a:rev =~# '^-' elseif a:rev =~# '^-'
let file = 'HEAD^{}'.a:rev[1:-1].self.path(':') let file = 'HEAD^{}'.a:rev[1:-1].self.relative(':')
elseif a:rev =~# '^@{' elseif a:rev =~# '^@{'
let file = 'HEAD'.a:rev.self.path(':') let file = 'HEAD'.a:rev.self.relative(':')
elseif a:rev =~# '^[~^]' elseif a:rev =~# '^[~^]'
let commit = s:sub(self.commit(),'^\d=$','HEAD') let commit = s:sub(self.commit(),'^\d=$','HEAD')
let file = commit.a:rev.self.path(':') let file = commit.a:rev.self.relative(':')
else else
let file = a:rev let file = a:rev
endif endif
return s:sub(substitute(file, return s:sub(substitute(file,
\ '%$\|\\\([[:punct:]]\)','\=len(submatch(1)) ? submatch(1) : self.path()','g'), \ '%$\|\\\([[:punct:]]\)','\=len(submatch(1)) ? submatch(1) : self.relative()','g'),
\ '\.\@<=/$','') \ '\.\@<=/$','')
endfunction endfunction
@ -613,7 +817,7 @@ function! s:buffer_up(...) dict abort
return rev return rev
endfunction endfunction
call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','rev','sha1','expand','containing_commit','up']) call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','relative','rev','sha1','expand','containing_commit','up'])
" Section: Git " Section: Git
@ -992,7 +1196,11 @@ function! s:Commit(mods, args, ...) abort
let outfile = tempname() let outfile = tempname()
let errorfile = tempname() let errorfile = tempname()
try try
let guioptions = &guioptions
try try
if &guioptions =~# '!'
setglobal guioptions-=!
endif
execute cd s:fnameescape(repo.tree()) execute cd s:fnameescape(repo.tree())
if s:winshell() if s:winshell()
let command = '' let command = ''
@ -1012,6 +1220,7 @@ function! s:Commit(mods, args, ...) abort
let error = v:shell_error let error = v:shell_error
finally finally
execute cd s:fnameescape(dir) execute cd s:fnameescape(dir)
let &guioptions = guioptions
endtry endtry
if !has('gui_running') if !has('gui_running')
redraw! redraw!
@ -1263,7 +1472,7 @@ function! s:Grep(cmd,bang,arg) abort
endfunction endfunction
function! s:Log(cmd, line1, line2, ...) abort function! s:Log(cmd, line1, line2, ...) abort
let path = s:buffer().path('/') let path = s:buffer().relative('/')
if path =~# '^/\.git\%(/\|$\)' || index(a:000,'--') != -1 if path =~# '^/\.git\%(/\|$\)' || index(a:000,'--') != -1
let path = '' let path = ''
endif endif
@ -1272,11 +1481,11 @@ function! s:Log(cmd, line1, line2, ...) abort
if empty(filter(a:000[0 : index(a:000,'--')],'v:val !~# "^-"')) if empty(filter(a:000[0 : index(a:000,'--')],'v:val !~# "^-"'))
if s:buffer().commit() =~# '\x\{40\}' if s:buffer().commit() =~# '\x\{40\}'
let cmd += [s:buffer().commit()] let cmd += [s:buffer().commit()]
elseif s:buffer().path() =~# '^\.git/refs/\|^\.git/.*HEAD$' elseif s:buffer().relative() =~# '^\.git/refs/\|^\.git/.*HEAD$'
let cmd += [s:buffer().path()[5:-1]] let cmd += [s:buffer().relative()[5:-1]]
endif endif
end end
let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:buffer().path(),submatch(1))")') let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:buffer().relative(),submatch(1))")')
if path =~# '/.' if path =~# '/.'
if a:line2 if a:line2
let cmd += ['-L', a:line1 . ',' . a:line2 . ':' . path[1:-1]] let cmd += ['-L', a:line1 . ',' . a:line2 . ':' . path[1:-1]]
@ -1380,10 +1589,10 @@ function! s:Edit(cmd, bang, mods, ...) abort
let file = buffer.expand(join(a:000, ' ')) let file = buffer.expand(join(a:000, ' '))
elseif expand('%') ==# '' elseif expand('%') ==# ''
let file = ':' let file = ':'
elseif buffer.commit() ==# '' && buffer.path('/') !~# '^/.git\>' elseif buffer.commit() ==# '' && buffer.relative('/') !~# '^/.git\>'
let file = buffer.path(':') let file = buffer.relative(':')
else else
let file = buffer.path('/') let file = buffer.relative('/')
endif endif
try try
let file = buffer.repo().translate(file) let file = buffer.repo().translate(file)
@ -1433,7 +1642,7 @@ function! s:Write(force,...) abort
return 'wq' return 'wq'
elseif s:buffer().type() == 'index' elseif s:buffer().type() == 'index'
return 'Gcommit' return 'Gcommit'
elseif s:buffer().path() ==# '' && getline(4) =~# '^+++ ' elseif s:buffer().relative() ==# '' && getline(4) =~# '^+++ '
let filename = getline(4)[6:-1] let filename = getline(4)[6:-1]
setlocal buftype= setlocal buftype=
silent write silent write
@ -1454,14 +1663,14 @@ function! s:Write(force,...) abort
endif endif
let mytab = tabpagenr() let mytab = tabpagenr()
let mybufnr = bufnr('') let mybufnr = bufnr('')
let path = a:0 ? join(a:000, ' ') : s:buffer().path() let path = a:0 ? join(a:000, ' ') : s:buffer().relative()
if empty(path) if empty(path)
return 'echoerr '.string('fugitive: cannot determine file path') return 'echoerr '.string('fugitive: cannot determine file path')
endif endif
if path =~# '^:\d\>' if path =~# '^:\d\>'
return 'write'.(a:force ? '! ' : ' ').s:fnameescape(s:repo().translate(s:buffer().expand(path))) return 'write'.(a:force ? '! ' : ' ').s:fnameescape(s:repo().translate(s:buffer().expand(path)))
endif endif
let always_permitted = (s:buffer().path() ==# path && s:buffer().commit() =~# '^0\=$') 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) !=# '' 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 v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)' let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
return 'echoerr v:errmsg' return 'echoerr v:errmsg'
@ -1518,7 +1727,7 @@ function! s:Write(force,...) abort
let v:errmsg = 'fugitive: '.error let v:errmsg = 'fugitive: '.error
return 'echoerr v:errmsg' return 'echoerr v:errmsg'
endif endif
if s:buffer().path() ==# path && s:buffer().commit() =~# '^\d$' if s:buffer().relative() ==# path && s:buffer().commit() =~# '^\d$'
set nomodified set nomodified
endif endif
@ -1713,7 +1922,7 @@ endfunction
function! s:buffer_compare_age(commit) dict abort function! s:buffer_compare_age(commit) dict abort
let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5} let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
let my_score = get(scores, ':'.self.commit(), 0) let my_score = get(scores, ':'.self.commit(), 0)
let their_score = get(scores,':'.a:commit,0) let their_score = get(scores, ':'.substitute(a:commit, '^:', '', ''), 0)
if my_score || their_score if my_score || their_score
return my_score < their_score ? -1 : my_score != their_score return my_score < their_score ? -1 : my_score != their_score
elseif self.commit() ==# a:commit elseif self.commit() ==# a:commit
@ -1741,7 +1950,7 @@ function! s:Diff(vert,keepfocus,...) abort
let vert = empty(a:vert) ? s:diff_modifier(2) : a:vert let vert = empty(a:vert) ? s:diff_modifier(2) : a:vert
if exists(':DiffGitCached') if exists(':DiffGitCached')
return 'DiffGitCached' return 'DiffGitCached'
elseif (empty(args) || args[0] == ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().path()) !=# '' elseif (empty(args) || args[0] ==# ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().relative()) !=# ''
let vert = empty(a:vert) ? s:diff_modifier(3) : a:vert let vert = empty(a:vert) ? s:diff_modifier(3) : a:vert
let nr = bufnr('') let nr = bufnr('')
execute 'leftabove '.vert.'split' s:fnameescape(fugitive#repo().translate(s:buffer().expand(':2'))) execute 'leftabove '.vert.'split' s:fnameescape(fugitive#repo().translate(s:buffer().expand(':2')))
@ -1763,12 +1972,12 @@ function! s:Diff(vert,keepfocus,...) abort
if arg ==# '' if arg ==# ''
return post return post
elseif arg ==# '/' elseif arg ==# '/'
let file = s:buffer().path('/') let file = s:buffer().relative('/')
elseif arg ==# ':' elseif arg ==# ':'
let file = s:buffer().path(':0:') let file = s:buffer().relative(':0:')
elseif arg =~# '^:/.' elseif arg =~# '^:/.'
try try
let file = s:repo().rev_parse(arg).s:buffer().path(':') let file = s:repo().rev_parse(arg).s:buffer().relative(':')
catch /^fugitive:/ catch /^fugitive:/
return 'echoerr v:errmsg' return 'echoerr v:errmsg'
endtry endtry
@ -1776,20 +1985,19 @@ function! s:Diff(vert,keepfocus,...) abort
let file = s:buffer().expand(arg) let file = s:buffer().expand(arg)
endif endif
if file !~# ':' && file !~# '^/' && s:repo().git_chomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$' if file !~# ':' && file !~# '^/' && s:repo().git_chomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$'
let file = file.s:buffer().path(':') let file = file.s:buffer().relative(':')
endif endif
else else
let file = s:buffer().path(s:buffer().commit() == '' ? ':0:' : '/') let file = s:buffer().relative(empty(s:buffer().commit()) ? ':0:' : '/')
endif endif
try try
let spec = s:repo().translate(file) let spec = s:repo().translate(file)
let commit = matchstr(spec,'\C[^:/]//\zs\x\+')
let restore = s:diff_restore() let restore = s:diff_restore()
if exists('+cursorbind') if exists('+cursorbind')
setlocal cursorbind setlocal cursorbind
endif endif
let w:fugitive_diff_restore = restore let w:fugitive_diff_restore = restore
if s:buffer().compare_age(commit) < 0 if s:buffer().compare_age(s:DirCommitFile(spec)[1]) < 0
execute 'rightbelow '.vert.'diffsplit '.s:fnameescape(spec) execute 'rightbelow '.vert.'diffsplit '.s:fnameescape(spec)
else else
execute 'leftabove '.vert.'diffsplit '.s:fnameescape(spec) execute 'leftabove '.vert.'diffsplit '.s:fnameescape(spec)
@ -1816,7 +2024,7 @@ function! s:Move(force, rename, destination) abort
if a:destination =~# '^/' if a:destination =~# '^/'
let destination = a:destination[1:-1] let destination = a:destination[1:-1]
elseif a:rename elseif a:rename
let destination = fnamemodify(s:buffer().path(), ':h') . '/' . a:destination let destination = fnamemodify(s:buffer().relative(), ':h') . '/' . a:destination
else else
let destination = s:shellslash(fnamemodify(s:sub(a:destination,'[%#]%(:\w)*','\=expand(submatch(0))'),':p')) let destination = s:shellslash(fnamemodify(s:sub(a:destination,'[%#]%(:\w)*','\=expand(submatch(0))'),':p'))
if destination[0:strlen(s:repo().tree())] ==# s:repo().tree('') if destination[0:strlen(s:repo().tree())] ==# s:repo().tree('')
@ -1827,7 +2035,7 @@ function! s:Move(force, rename, destination) abort
" Work around Vim parser idiosyncrasy " Work around Vim parser idiosyncrasy
let discarded = s:buffer().setvar('&swapfile',0) let discarded = s:buffer().setvar('&swapfile',0)
endif endif
let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().path(), destination], s:repo()) let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().relative(), destination], s:repo())
if v:shell_error if v:shell_error
let v:errmsg = 'fugitive: '.message let v:errmsg = 'fugitive: '.message
return 'echoerr v:errmsg' return 'echoerr v:errmsg'
@ -1862,7 +2070,7 @@ function! s:RenameComplete(A,L,P) abort
if a:A =~# '^/' if a:A =~# '^/'
return s:repo().superglob(a:A) return s:repo().superglob(a:A)
else else
let pre = '/'. fnamemodify(s:buffer().path(), ':h') . '/' let pre = '/'. fnamemodify(s:buffer().relative(), ':h') . '/'
return map(s:repo().superglob(pre.a:A), 'strpart(v:val, len(pre))') return map(s:repo().superglob(pre.a:A), 'strpart(v:val, len(pre))')
endif endif
endfunction endfunction
@ -1879,7 +2087,7 @@ function! s:Remove(after, force) abort
if a:force if a:force
let cmd += ['--force'] let cmd += ['--force']
endif endif
let message = call(s:repo().git_chomp_in_tree,cmd+['--',s:buffer().path()],s:repo()) let message = call(s:repo().git_chomp_in_tree,cmd+['--',s:buffer().relative()],s:repo())
if v:shell_error if v:shell_error
let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)') let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
return 'echoerr '.string(v:errmsg) return 'echoerr '.string(v:errmsg)
@ -1903,7 +2111,6 @@ augroup END
augroup fugitive_blame augroup fugitive_blame
autocmd! autocmd!
autocmd BufReadPost *.fugitiveblame setfiletype fugitiveblame
autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif
autocmd Syntax fugitiveblame call s:BlameSyntax() autocmd Syntax fugitiveblame call s:BlameSyntax()
autocmd User Fugitive if s:buffer().type('file', 'blob') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(<bang>0,<line1>,<line2>,<count>,[<f-args>])" | endif autocmd User Fugitive if s:buffer().type('file', 'blob') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(<bang>0,<line1>,<line2>,<count>,[<f-args>])" | endif
@ -1926,7 +2133,7 @@ function! s:Blame(bang,line1,line2,count,args) abort
return 'bdelete' return 'bdelete'
endif endif
try try
if s:buffer().path() == '' if empty(s:buffer().relative())
call s:throw('file or blob required') call s:throw('file or blob required')
endif endif
if filter(copy(a:args),'v:val !~# "^\\%(--root\|--show-name\\|-\\=\\%([ltfnsew]\\|[MC]\\d*\\)\\+\\)$"') != [] if filter(copy(a:args),'v:val !~# "^\\%(--root\|--show-name\\|-\\=\\%([ltfnsew]\\|[MC]\\d*\\)\\+\\)$"') != []
@ -1939,7 +2146,7 @@ function! s:Blame(bang,line1,line2,count,args) abort
else else
let cmd += ['--contents', '-'] let cmd += ['--contents', '-']
endif endif
let cmd += ['--', s:buffer().path()] let cmd += ['--', s:buffer().relative()]
let basecmd = escape(call(s:repo().git_command,cmd,s:repo()),'!%#') let basecmd = escape(call(s:repo().git_command,cmd,s:repo()),'!%#')
try try
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
@ -2048,7 +2255,7 @@ function! s:BlameCommit(cmd) abort
let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]') let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]')
let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ') let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ')
if path ==# '' if path ==# ''
let path = s:buffer(b:fugitive_blamed_bufnr).path() let path = s:buffer(b:fugitive_blamed_bufnr).relative()
endif endif
execute cmd execute cmd
if search('^diff .* b/\M'.escape(path,'\').'$','W') if search('^diff .* b/\M'.escape(path,'\').'$','W')
@ -2089,7 +2296,7 @@ function! s:BlameJump(suffix) abort
let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]') let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]')
let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ') let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ')
if path ==# '' if path ==# ''
let path = s:buffer(b:fugitive_blamed_bufnr).path() let path = s:buffer(b:fugitive_blamed_bufnr).relative()
endif endif
let args = b:fugitive_blame_arguments let args = b:fugitive_blame_arguments
let offset = line('.') - line('w0') let offset = line('.') - line('w0')
@ -2197,15 +2404,20 @@ function! s:Browse(bang,line1,count,...) abort
if rev ==# '' if rev ==# ''
let expanded = s:buffer().rev() let expanded = s:buffer().rev()
elseif rev ==# ':' elseif rev ==# ':'
let expanded = s:buffer().path('/') let expanded = s:buffer().relative('/')
else else
let expanded = s:buffer().expand(rev) let expanded = s:buffer().expand(rev)
endif endif
if filereadable(s:repo().tree('refs/tags/' . expanded))
let expanded = 'refs/tags/' . expanded
endif
let full = s:repo().translate(expanded) let full = s:repo().translate(expanded)
let commit = '' let commit = ''
if full =~# '^fugitive://' if full =~? '^fugitive:'
let commit = matchstr(full,'://.*//\zs\w\w\+') let [dir, commit, path] = s:DirCommitFile(full)
let path = matchstr(full,'://.*//\w\+\zs/.*') if commit =~# '^:\=\d$'
let commit = ''
endif
if commit =~ '..' if commit =~ '..'
let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':')) let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':'))
let branch = matchstr(expanded, '^[^:]*') let branch = matchstr(expanded, '^[^:]*')
@ -2510,15 +2722,15 @@ function! fugitive#BufReadStatus() abort
if &bufhidden ==# '' if &bufhidden ==# ''
setlocal bufhidden=delete setlocal bufhidden=delete
endif endif
call s:JumpInit() call fugitive#MapJumps()
nunmap <buffer> P nunmap <buffer> P
nunmap <buffer> ~ nunmap <buffer> ~
nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>StageNext(v:count1)<CR> nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>StageNext(v:count1)<CR>
nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>StagePrevious(v:count1)<CR> nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>StagePrevious(v:count1)<CR>
nnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR> nnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR>
xnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line("'<"),line("'>"))<CR> xnoremap <buffer> <silent> - :<C-U>silent execute <SID>StageToggle(line("'<"),line("'>"))<CR>
nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe <SID>BufReadIndex()<CR> nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe fugitive#BufReadIndex()<CR>
nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe <SID>BufReadIndex()<CR> nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe fugitive#BufReadIndex()<CR>
nnoremap <buffer> <silent> C :<C-U>Gcommit<CR>:echohl WarningMsg<Bar>echo ':Gstatus C is deprecated in favor of cc'<Bar>echohl NONE<CR> nnoremap <buffer> <silent> C :<C-U>Gcommit<CR>:echohl WarningMsg<Bar>echo ':Gstatus C is deprecated in favor of cc'<Bar>echohl NONE<CR>
nnoremap <buffer> <silent> cA :<C-U>Gcommit --amend --reuse-message=HEAD<CR>:echohl WarningMsg<Bar>echo ':Gstatus cA is deprecated in favor of ce'<CR> nnoremap <buffer> <silent> cA :<C-U>Gcommit --amend --reuse-message=HEAD<CR>:echohl WarningMsg<Bar>echo ':Gstatus cA is deprecated in favor of ce'<CR>
nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR> nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
@ -2550,8 +2762,9 @@ endfunction
function! fugitive#FileRead() abort function! fugitive#FileRead() abort
try try
let repo = s:repo(FugitiveExtractGitDir(expand('<amatch>'))) let [dir, commit, file] = s:DirCommitFile(expand('<amatch>'))
let path = s:sub(s:sub(matchstr(expand('<amatch>'),'fugitive://.\{-\}//\zs.*'),'/',':'),'^\d:',':&') let repo = s:repo(dir)
let path = commit . substitute(file, '^/', ':', '')
let hash = repo.rev_parse(path) let hash = repo.rev_parse(path)
if path =~ '^:' if path =~ '^:'
let type = 'blob' let type = 'blob'
@ -2589,15 +2802,15 @@ endfunction
function! fugitive#BufWriteIndex() abort function! fugitive#BufWriteIndex() abort
let tmp = tempname() let tmp = tempname()
try try
let path = matchstr(expand('<amatch>'),'//\d/\zs.*') let [dir, commit, file] = s:DirCommitFile(expand('<amatch>'))
let stage = matchstr(expand('<amatch>'),'//\zs\d') let path = file[1:-1]
silent execute 'write !'.s:repo().git_command('hash-object','-w','--stdin').' > '.tmp silent execute 'write !'.s:repo().git_command('hash-object','-w','--stdin').' > '.tmp
let sha1 = readfile(tmp)[0] let sha1 = readfile(tmp)[0]
let old_mode = matchstr(s:repo().git_chomp('ls-files','--stage',path),'^\d\+') let old_mode = matchstr(s:repo().git_chomp('ls-files','--stage',path),'^\d\+')
if old_mode == '' if old_mode == ''
let old_mode = executable(s:repo().tree(path)) ? '100755' : '100644' let old_mode = executable(s:repo().tree(path)) ? '100755' : '100644'
endif endif
let info = old_mode.' '.sha1.' '.stage."\t".path let info = old_mode.' '.sha1.' '.commit[-1:-1]."\t".path
call writefile([info],tmp) call writefile([info],tmp)
if s:winshell() if s:winshell()
let error = system('type '.s:gsub(tmp,'/','\\').'|'.s:repo().git_command('update-index','--index-info')) let error = system('type '.s:gsub(tmp,'/','\\').'|'.s:repo().git_command('update-index','--index-info'))
@ -2688,10 +2901,10 @@ function! fugitive#BufReadObject() abort
endif endif
if b:fugitive_type !=# 'blob' if b:fugitive_type !=# 'blob'
setlocal filetype=git foldmethod=syntax setlocal filetype=git foldmethod=syntax
nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += v:count1<Bar>exe <SID>BufReadObject()<CR> nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadObject()<CR>
nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= v:count1<Bar>exe <SID>BufReadObject()<CR> nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadObject()<CR>
else else
call s:JumpInit() call fugitive#MapJumps()
endif endif
endtry endtry
@ -2705,14 +2918,6 @@ endfunction
augroup fugitive_files augroup fugitive_files
autocmd! autocmd!
autocmd FileType git
\ if exists('b:git_dir') |
\ call s:JumpInit() |
\ endif
autocmd FileType git,gitcommit,gitrebase
\ if exists('b:git_dir') |
\ call s:GFInit() |
\ endif
augroup END augroup END
" Section: Temp files " Section: Temp files
@ -2737,7 +2942,7 @@ augroup END
" Section: Go to file " Section: Go to file
nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR> nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR>
function! s:GFInit(...) abort function! fugitive#MapCfile(...) abort
cnoremap <buffer> <expr> <Plug><cfile> fugitive#Cfile() cnoremap <buffer> <expr> <Plug><cfile> fugitive#Cfile()
if !exists('g:fugitive_no_maps') if !exists('g:fugitive_no_maps')
call s:map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>') call s:map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>')
@ -2748,15 +2953,15 @@ function! s:GFInit(...) abort
endif endif
endfunction endfunction
function! s:JumpInit(...) abort function! fugitive#MapJumps(...) abort
nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>GF("edit")<CR> nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>GF("edit")<CR>
if !&modifiable if !&modifiable
nnoremap <buffer> <silent> o :<C-U>exe <SID>GF("split")<CR> nnoremap <buffer> <silent> o :<C-U>exe <SID>GF("split")<CR>
nnoremap <buffer> <silent> S :<C-U>exe <SID>GF("vsplit")<CR> nnoremap <buffer> <silent> S :<C-U>exe <SID>GF("vsplit")<CR>
nnoremap <buffer> <silent> O :<C-U>exe <SID>GF("tabedit")<CR> nnoremap <buffer> <silent> O :<C-U>exe <SID>GF("tabedit")<CR>
nnoremap <buffer> <silent> - :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().up(v:count1))<Bar> if fugitive#buffer().type('tree')<Bar>call search('^'.escape(expand('#:t'),'.*[]~\').'/\=$','wc')<Bar>endif<CR> nnoremap <buffer> <silent> - :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().up(v:count1))<Bar> if fugitive#buffer().type('tree')<Bar>call search('^'.escape(expand('#:t'),'.*[]~\').'/\=$','wc')<Bar>endif<CR>
nnoremap <buffer> <silent> P :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().commit().'^'.v:count1.<SID>buffer().path(':'))<CR> nnoremap <buffer> <silent> P :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().commit().'^'.v:count1.<SID>buffer().relative(':'))<CR>
nnoremap <buffer> <silent> ~ :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().commit().'~'.v:count1.<SID>buffer().path(':'))<CR> nnoremap <buffer> <silent> ~ :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().commit().'~'.v:count1.<SID>buffer().relative(':'))<CR>
nnoremap <buffer> <silent> C :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().containing_commit())<CR> nnoremap <buffer> <silent> C :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().containing_commit())<CR>
nnoremap <buffer> <silent> cc :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().containing_commit())<CR> nnoremap <buffer> <silent> cc :<C-U>exe <SID>Edit('edit',0,'',<SID>buffer().containing_commit())<CR>
nnoremap <buffer> <silent> co :<C-U>exe <SID>Edit('split',0,'',<SID>buffer().containing_commit())<CR> nnoremap <buffer> <silent> co :<C-U>exe <SID>Edit('split',0,'',<SID>buffer().containing_commit())<CR>
@ -2778,9 +2983,9 @@ function! s:cfile() abort
if buffer.type('tree') if buffer.type('tree')
let showtree = (getline(1) =~# '^tree ' && getline(2) == "") let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
if showtree && line('.') > 2 if showtree && line('.') > 2
return [buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(getline('.'),'/$','')] return [buffer.commit().':'.s:buffer().relative().(buffer.relative() =~# '^$\|/$' ? '' : '/').s:sub(getline('.'),'/$','')]
elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t' elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t'
return [buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')] return [buffer.commit().':'.s:buffer().relative().(buffer.relative() =~# '^$\|/$' ? '' : '/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
endif endif
elseif buffer.type('blob') elseif buffer.type('blob')
@ -2866,11 +3071,11 @@ function! s:cfile() abort
elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/' elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/'
let ref = getline('.')[4:] let ref = getline('.')[4:]
elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+,\d\+ +\d\+,','bnW') elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+\%(,\d\+\)\= +\d\+','bnW')
let type = getline('.')[0] let type = getline('.')[0]
let lnum = line('.') - 1 let lnum = line('.') - 1
let offset = 0 let offset = 0
while getline(lnum) !~# '^@@ -\d\+,\d\+ +\d\+,' while getline(lnum) !~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
if getline(lnum) =~# '^[ '.type.']' if getline(lnum) =~# '^[ '.type.']'
let offset += 1 let offset += 1
endif endif
@ -2885,7 +3090,7 @@ function! s:cfile() abort
elseif getline('.') =~# '^rename to ' elseif getline('.') =~# '^rename to '
let ref = 'b/'.getline('.')[10:] let ref = 'b/'.getline('.')[10:]
elseif getline('.') =~# '^@@ -\d\+,\d\+ +\d\+,' elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW')) let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW'))
let offset = matchstr(getline('.'), '+\zs\d\+') let offset = matchstr(getline('.'), '+\zs\d\+')

View file

@ -330,13 +330,13 @@ and the work tree file for everything else. Example revisions follow.
Revision Meaning ~ Revision Meaning ~
HEAD .git/HEAD HEAD .git/HEAD
master .git/refs/heads/master refs/heads/x .git/refs/heads/x
HEAD^{} The commit referenced by HEAD @ The commit referenced by @ aka HEAD
HEAD^ The parent of the commit referenced by HEAD master^ The parent of the commit referenced by master
HEAD: The tree referenced by HEAD master: The tree referenced by master
/HEAD The file named HEAD in the work tree /master The file named master in the work tree
Makefile The file named Makefile in the work tree Makefile The file named Makefile in the work tree
HEAD^:Makefile The file named Makefile in the parent of HEAD @^:Makefile The file named Makefile in the parent of HEAD
:Makefile The file named Makefile in the index (writable) :Makefile The file named Makefile in the index (writable)
- The current file in HEAD - The current file in HEAD
^ The current file in the previous commit ^ The current file in the previous commit

View file

@ -0,0 +1 @@
autocmd BufReadPost *.fugitiveblame setfiletype fugitiveblame

View file

@ -59,8 +59,8 @@ endfunction
function! FugitiveExtractGitDir(path) abort function! FugitiveExtractGitDir(path) abort
let path = s:shellslash(a:path) let path = s:shellslash(a:path)
if path =~# '^fugitive://.*//' if path =~# '^fugitive:'
return matchstr(path, '\C^fugitive://\zs.\{-\}\ze//') return matchstr(path, '\C^fugitive:\%(//\)\=\zs.\{-\}\ze\%(//\|::\|$\)')
elseif isdirectory(path) elseif isdirectory(path)
let path = fnamemodify(path, ':p:s?/$??') let path = fnamemodify(path, ':p:s?/$??')
else else
@ -138,6 +138,19 @@ function! FugitiveHead(...) abort
return fugitive#repo().head(a:0 ? a:1 : 0) return fugitive#repo().head(a:0 ? a:1 : 0)
endfunction endfunction
function! FugitivePath(...) abort
let file = fnamemodify(a:0 ? a:1 : @%, ':p')
if file =~? '^fugitive:'
return fugitive#Path(file)
else
return file
endif
endfunction
function! FugitiveReal(...) abort
return call('FugitivePath', a:000)
endfunction
augroup fugitive augroup fugitive
autocmd! autocmd!
@ -147,6 +160,15 @@ augroup fugitive
autocmd VimEnter * if expand('<amatch>')==''|call FugitiveDetect(getcwd())|endif autocmd VimEnter * if expand('<amatch>')==''|call FugitiveDetect(getcwd())|endif
autocmd CmdWinEnter * call FugitiveDetect(expand('#:p')) autocmd CmdWinEnter * call FugitiveDetect(expand('#:p'))
autocmd FileType git
\ if exists('b:git_dir') |
\ call fugitive#MapJumps() |
\ endif
autocmd FileType git,gitcommit,gitrebase
\ if exists('b:git_dir') |
\ call fugitive#MapCfile() |
\ endif
autocmd BufReadCmd index{,.lock} autocmd BufReadCmd index{,.lock}
\ if FugitiveIsGitDir(expand('<amatch>:p:h')) | \ if FugitiveIsGitDir(expand('<amatch>:p:h')) |
\ exe fugitive#BufReadStatus() | \ exe fugitive#BufReadStatus() |

View file

@ -146,6 +146,10 @@ endfunction
function! gitgutter#diff#handler(bufnr, diff) abort function! gitgutter#diff#handler(bufnr, diff) abort
call gitgutter#debug#log(a:diff) call gitgutter#debug#log(a:diff)
if !bufexists(a:bufnr)
return
endif
call gitgutter#hunk#set_hunks(a:bufnr, gitgutter#diff#parse_diff(a:diff)) call gitgutter#hunk#set_hunks(a:bufnr, gitgutter#diff#parse_diff(a:diff))
let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr)) let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr))

View file

@ -2,6 +2,27 @@
FEATURES: FEATURES:
* Add **:GoIfErr** command together with the `<Plug>(go-iferr)` plug key to
create a custom mapping. This command generates an `if err != nil { return ... }`
automatically which infer the type of return values and the numbers.
For example:
```
func doSomething() (string, error) {
f, err := os.Open("file")
}
```
Becomes:
```
func doSomething() (string, error) {
f, err := os.Open("file")
if err != nil {
return "", err
}
}
```
* Two new text objects has been added: * Two new text objects has been added:
* `ic` (inner comment) selects the content of the comment, excluding the start/end markers (i.e: `//`, `/*`) * `ic` (inner comment) selects the content of the comment, excluding the start/end markers (i.e: `//`, `/*`)
* `ac` (a comment) selects the content of the whole commment block, including markers * `ac` (a comment) selects the content of the whole commment block, including markers

View file

@ -114,10 +114,16 @@ endfunction
" Run runs the current file (and their dependencies if any) in a new terminal. " Run runs the current file (and their dependencies if any) in a new terminal.
function! go#cmd#RunTerm(bang, mode, files) abort function! go#cmd#RunTerm(bang, mode, files) abort
let cmd = "go run "
let tags = go#config#BuildTags()
if len(tags) > 0
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
endif
if empty(a:files) if empty(a:files)
let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) let cmd .= go#util#Shelljoin(go#tool#Files())
else else
let cmd = "go run ". go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1) let cmd .= go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1)
endif endif
call go#term#newmode(a:bang, cmd, a:mode) call go#term#newmode(a:bang, cmd, a:mode)
endfunction endfunction
@ -138,8 +144,14 @@ function! go#cmd#Run(bang, ...) abort
" anything. Once this is implemented we're going to make :GoRun async " anything. Once this is implemented we're going to make :GoRun async
endif endif
let cmd = "go run "
let tags = go#config#BuildTags()
if len(tags) > 0
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
endif
if go#util#IsWin() if go#util#IsWin()
exec '!go run ' . go#util#Shelljoin(go#tool#Files()) exec '!' . cmd . go#util#Shelljoin(go#tool#Files())
if v:shell_error if v:shell_error
redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None
else else
@ -152,9 +164,9 @@ function! go#cmd#Run(bang, ...) abort
" :make expands '%' and '#' wildcards, so they must also be escaped " :make expands '%' and '#' wildcards, so they must also be escaped
let default_makeprg = &makeprg let default_makeprg = &makeprg
if a:0 == 0 if a:0 == 0
let &makeprg = 'go run ' . go#util#Shelljoin(go#tool#Files(), 1) let &makeprg = cmd . go#util#Shelljoin(go#tool#Files(), 1)
else else
let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) let &makeprg = cmd . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
endif endif
let l:listtype = go#list#Type("GoRun") let l:listtype = go#list#Type("GoRun")

View file

@ -0,0 +1,16 @@
function! go#iferr#Generate()
let [l:out, l:err] = go#util#Exec(['iferr',
\ '-pos=' . go#util#OffsetCursor()], go#util#GetLines())
if len(l:out) == 1
return
endif
if getline('.') =~ '^\s*$'
silent delete _
silent normal! k
endif
let l:pos = getcurpos()
call append(l:pos[1], split(l:out, "\n"))
silent normal! j=2j
call setpos('.', l:pos)
silent normal! 4j
endfunction

View file

@ -36,7 +36,7 @@ function! go#tool#Files(...) abort
endif endif
endfor endfor
let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-f', l:combined]) let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:combined])
return split(l:out, '\n') return split(l:out, '\n')
endfunction endfunction
@ -46,7 +46,7 @@ function! go#tool#Deps() abort
else else
let format = "{{range $f := .Deps}}{{$f}}\n{{end}}" let format = "{{range $f := .Deps}}{{$f}}\n{{end}}"
endif endif
let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-f', l:format]) let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:format])
return split(l:out, '\n') return split(l:out, '\n')
endfunction endfunction
@ -57,14 +57,14 @@ function! go#tool#Imports() abort
else else
let format = "{{range $f := .Imports}}{{$f}}{{printf \"\\n\"}}{{end}}" let format = "{{range $f := .Imports}}{{$f}}{{printf \"\\n\"}}{{end}}"
endif endif
let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-f', l:format]) let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:format])
if l:err != 0 if l:err != 0
echo out echo out
return imports return imports
endif endif
for package_path in split(out, '\n') for package_path in split(out, '\n')
let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-f', '{{.Name}}', l:package_path]) let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', '{{.Name}}', l:package_path])
if l:err != 0 if l:err != 0
echo out echo out
return imports return imports
@ -88,7 +88,7 @@ function! go#tool#Info(auto) abort
endfunction endfunction
function! go#tool#PackageName() abort function! go#tool#PackageName() abort
let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-f', '{{.Name}}']) let [l:out, l:err] = go#tool#ExecuteInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', '{{.Name}}'])
if l:err != 0 if l:err != 0
return -1 return -1
endif endif

View file

@ -853,6 +853,27 @@ CTRL-t
} }
< <
*:GoIfErr*
:GoIfErr
Generate if err != nil { return ... } automatically which infer the type
of return values and the numbers.
For example:
>
func doSomething() (string, error) {
f, err := os.Open("file")
}
<
Becomes:
>
func doSomething() (string, error) {
f, err := os.Open("file")
if err != nil {
return "", err
}
}
<
============================================================================== ==============================================================================
MAPPINGS *go-mappings* MAPPINGS *go-mappings*
@ -1065,6 +1086,10 @@ Alternates between the implementation and test code in a new vertical split
Calls `:GoImport` for the current package Calls `:GoImport` for the current package
*(go-iferr)*
Generate if err != nil { return ... } automatically which infer the type of
return values and the numbers.
============================================================================== ==============================================================================
TEXT OBJECTS *go-text-objects* TEXT OBJECTS *go-text-objects*
@ -2090,7 +2115,7 @@ Many vim-go commands use the `guru` commandline tool to get information. Some
a reasonable amount of performance `guru` limits this analysis to a selected a reasonable amount of performance `guru` limits this analysis to a selected
list of packages. This is known as the "guru scope". list of packages. This is known as the "guru scope".
The default is to use the package the curent buffer belongs to, but this may The default is to use the package the current buffer belongs to, but this may
not always be correct. For example for the file `guthub.com/user/pkg/a/a.go` not always be correct. For example for the file `guthub.com/user/pkg/a/a.go`
the scope will be set to `github.com/user/pkg/a`, but you probably want the scope will be set to `github.com/user/pkg/a`, but you probably want
`github.com/user/pkg` `github.com/user/pkg`

View file

@ -109,4 +109,7 @@ endif
" -- issue " -- issue
command! -nargs=0 GoReportGitHubIssue call go#issue#New() command! -nargs=0 GoReportGitHubIssue call go#issue#New()
" -- iferr
command! -nargs=0 GoIfErr call go#iferr#Generate()
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -73,4 +73,6 @@ nnoremap <silent> <Plug>(go-alternate-edit) :<C-u>call go#alternate#Switch(0, "e
nnoremap <silent> <Plug>(go-alternate-vertical) :<C-u>call go#alternate#Switch(0, "vsplit")<CR> nnoremap <silent> <Plug>(go-alternate-vertical) :<C-u>call go#alternate#Switch(0, "vsplit")<CR>
nnoremap <silent> <Plug>(go-alternate-split) :<C-u>call go#alternate#Switch(0, "split")<CR> nnoremap <silent> <Plug>(go-alternate-split) :<C-u>call go#alternate#Switch(0, "split")<CR>
nnoremap <silent> <Plug>(go-iferr) :<C-u>call go#iferr#Generate()<CR>
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -47,6 +47,7 @@ let s:packages = {
\ 'impl': ['github.com/josharian/impl'], \ 'impl': ['github.com/josharian/impl'],
\ 'keyify': ['github.com/dominikh/go-tools/cmd/keyify'], \ 'keyify': ['github.com/dominikh/go-tools/cmd/keyify'],
\ 'motion': ['github.com/fatih/motion'], \ 'motion': ['github.com/fatih/motion'],
\ 'iferr': ['github.com/koron/iferr'],
\ } \ }
" These commands are available on any filetypes " These commands are available on any filetypes

View file

@ -1,5 +1,4 @@
repeat.vim # repeat.vim
==========
If you've ever tried using the `.` command after a plugin map, you were If you've ever tried using the `.` command after a plugin map, you were
likely disappointed to discover it only repeated the last native command likely disappointed to discover it only repeated the last native command
@ -11,9 +10,7 @@ The following plugins support repeat.vim:
* [surround.vim](https://github.com/tpope/vim-surround) * [surround.vim](https://github.com/tpope/vim-surround)
* [speeddating.vim](https://github.com/tpope/vim-speeddating) * [speeddating.vim](https://github.com/tpope/vim-speeddating)
* [abolish.vim](https://github.com/tpope/vim-abolish)
* [unimpaired.vim](https://github.com/tpope/vim-unimpaired) * [unimpaired.vim](https://github.com/tpope/vim-unimpaired)
* [commentary.vim](https://github.com/tpope/vim-commentary)
* [vim-easyclip](https://github.com/svermeulen/vim-easyclip) * [vim-easyclip](https://github.com/svermeulen/vim-easyclip)
Adding support to a plugin is generally as simple as the following Adding support to a plugin is generally as simple as the following
@ -21,24 +18,21 @@ command at the end of your map functions.
silent! call repeat#set("\<Plug>MyWonderfulMap", v:count) silent! call repeat#set("\<Plug>MyWonderfulMap", v:count)
Installation ## Installation
------------
If you don't have a preferred installation method, I recommend Install using your favorite package manager, or use Vim's built-in package
installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and support:
then simply copy and paste:
cd ~/.vim/bundle mkdir -p ~/.vim/pack/tpope/start
git clone git://github.com/tpope/vim-repeat.git cd ~/.vim/pack/tpope/start
git clone https://tpope.io/vim/repeat.git
Contributing ## Contributing
------------
See the contribution guidelines for See the contribution guidelines for
[pathogen.vim](https://github.com/tpope/vim-pathogen#readme). [pathogen.vim](https://github.com/tpope/vim-pathogen#readme).
Self-Promotion ## Self-Promotion
--------------
Like repeat.vim? Follow the repository on Like repeat.vim? Follow the repository on
[GitHub](https://github.com/tpope/vim-repeat) and vote for it on [GitHub](https://github.com/tpope/vim-repeat) and vote for it on
@ -47,8 +41,7 @@ you're feeling especially charitable, follow [tpope](http://tpo.pe/) on
[Twitter](http://twitter.com/tpope) and [Twitter](http://twitter.com/tpope) and
[GitHub](https://github.com/tpope). [GitHub](https://github.com/tpope).
License ## License
-------
Copyright (c) Tim Pope. Distributed under the same terms as Vim itself. Copyright (c) Tim Pope. Distributed under the same terms as Vim itself.
See `:help license`. See `:help license`.

View file

@ -48,8 +48,18 @@ snippet au augroup ... autocmd block
augroup end augroup end
snippet bun Vundle.vim Plugin definition snippet bun Vundle.vim Plugin definition
Plugin '${0}' Plugin '${0}'
snippet plug Vundle.vim Plugin definition snippet plug vim-plug Plugin definition
Plugin '${0}' Plug '${0}'
snippet plugdo vim-plug Plugin definition with { 'do': '' }
Plug '${1}', { 'do': '${0}' }
snippet plugon vim-plug Plugin definition with { 'on': '' }
Plug '${1}', { 'on': '${0}' }
snippet plugfor vim-plug Plugin definition with { 'for': '' }
Plug '${1}', { 'for': '${0}' }
snippet plugbr vim-plug Plugin definition with { 'branch': '' }
Plug '${1}', { 'branch': '${0}' }
snippet plugtag vim-plug Plugin definition with { 'tag': '' }
Plug '${1}', { 'tag': '${0}' }
snippet let snippet let
let ${1:variable} = ${0: value} let ${1:variable} = ${0: value}
snippet se snippet se

View file

@ -573,7 +573,7 @@ nnoremap <silent> <Plug>SurroundRepeat .
nnoremap <silent> <Plug>Dsurround :<C-U>call <SID>dosurround(<SID>inputtarget())<CR> nnoremap <silent> <Plug>Dsurround :<C-U>call <SID>dosurround(<SID>inputtarget())<CR>
nnoremap <silent> <Plug>Csurround :<C-U>call <SID>changesurround()<CR> nnoremap <silent> <Plug>Csurround :<C-U>call <SID>changesurround()<CR>
nnoremap <silent> <Plug>CSurround :<C-U>call <SID>changesurround(1)<CR> nnoremap <silent> <Plug>CSurround :<C-U>call <SID>changesurround(1)<CR>
nnoremap <expr> <Plug>Yssurround <SID>opfunc('setup').'g_' nnoremap <expr> <Plug>Yssurround '^'.v:count1.<SID>opfunc('setup').'g_'
nnoremap <expr> <Plug>YSsurround <SID>opfunc2('setup').'_' nnoremap <expr> <Plug>YSsurround <SID>opfunc2('setup').'_'
nnoremap <expr> <Plug>Ysurround <SID>opfunc('setup') nnoremap <expr> <Plug>Ysurround <SID>opfunc('setup')
nnoremap <expr> <Plug>YSurround <SID>opfunc2('setup') nnoremap <expr> <Plug>YSurround <SID>opfunc2('setup')