+" 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
+ " Remember when ALE is quitting for BufWrite, etc.
+ call setbufvar(a:buffer, 'ale_quitting', ale#events#ClockMilliseconds())
+endfunction
+
+function! ale#events#QuitRecently(buffer) abort
+ let l:time = getbufvar(a:buffer, 'ale_quitting', 0)
+
+ return l:time && ale#events#ClockMilliseconds() - l:time < 1000
+endfunction
+
+function! ale#events#SaveEvent(buffer) abort
+ let l:should_lint = ale#Var(a:buffer, 'enabled') && g:ale_lint_on_save
+
+ if l:should_lint
+ call setbufvar(a:buffer, 'ale_save_event_fired', 1)
+ endif
+
+ if ale#Var(a:buffer, 'fix_on_save') && !ale#events#QuitRecently(a:buffer)
+ let l:will_fix = ale#fix#Fix(a:buffer, 'save_file')
+ let l:should_lint = l:should_lint && !l:will_fix
+ endif
+
+ if l:should_lint && !ale#events#QuitRecently(a:buffer)
+ call ale#Queue(0, 'lint_file', a:buffer)
+ endif
+endfunction
+
+function! ale#events#LintOnEnter(buffer) abort
+ " Unmark a file as being changed outside of Vim after we try to check it.
+ call setbufvar(a:buffer, 'ale_file_changed', 0)
+
+ if ale#Var(a:buffer, 'enabled') && g:ale_lint_on_enter
+ call ale#Queue(0, 'lint_file', a:buffer)
+ endif
+endfunction
+
+function! ale#events#ReadOrEnterEvent(buffer) abort
+ " Apply pattern options if the variable is set.
+ if get(g:, 'ale_pattern_options_enabled', 1)
+ \&& !empty(get(g:, 'ale_pattern_options'))
+ call ale#pattern_options#SetOptions(a:buffer)
+ endif
+
+ " When entering a buffer, we are no longer quitting it.
+ call setbufvar(a:buffer, 'ale_quitting', 0)
+ let l:filetype = getbufvar(a:buffer, '&filetype')
+ call setbufvar(a:buffer, 'ale_original_filetype', l:filetype)
+
+ " If the file changed outside of Vim, check it on BufEnter,BufRead
+ if getbufvar(a:buffer, 'ale_file_changed')
+ call ale#events#LintOnEnter(a:buffer)
+ endif
+endfunction
+
+function! ale#events#FileTypeEvent(buffer, new_filetype) abort
+ " The old filetype will be set to an empty string by the BuFEnter event,
+ " and not linting when the old filetype hasn't been set yet prevents
+ " buffers being checked when you enter them when linting on enter is off.
+ let l:old_filetype = getbufvar(a:buffer, 'ale_original_filetype', v:null)
+
+ if l:old_filetype isnot v:null
+ \&& !empty(a:new_filetype)
+ \&& a:new_filetype isnot# l:old_filetype
+ " Remember what the new filetype is.
+ call setbufvar(a:buffer, 'ale_original_filetype', a:new_filetype)
+
+ if g:ale_lint_on_filetype_changed
+ call ale#Queue(300, 'lint_file', a:buffer)
+ endif
+ endif
+endfunction
+
+function! ale#events#FileChangedEvent(buffer) abort
+ call setbufvar(a:buffer, 'ale_file_changed', 1)
+
+ if bufnr('') == a:buffer
+ call ale#events#LintOnEnter(a:buffer)
+ endif
+endfunction
+
+" A timer for emulating InsertLeave.
+"
+" We only need a single timer, and we'll lint the last buffer we entered
+" insert mode on.
+if !exists('s:insert_leave_timer')
+ let s:insert_leave_timer = -1
+endif
+
+" True if the ModeChanged event exists.
+" In this case, ModeChanged will be used instead of InsertLeave emulation.
+let s:mode_changed_exists = exists('##ModeChanged')
+
+function! ale#events#EmulateInsertLeave(buffer) abort
+ if mode() is# 'n'
+ call timer_stop(s:insert_leave_timer)
+ call ale#Queue(0, '', a:buffer)
+ endif
+endfunction
+
+function! ale#events#InsertEnterEvent(buffer) abort
+ if g:ale_close_preview_on_insert && exists('*ale#preview#CloseIfTypeMatches')
+ call ale#preview#CloseIfTypeMatches('ale-preview')
+ endif
+
+ " Start a repeating timer if the use might not trigger InsertLeave, so we
+ " can emulate its behavior.
+ " If the ModeChanged autocmd exists, it will be used instead of this
+ " timer; as ModeChanged will be sent regardless of how the insert mode is
+ " exited, including <Esc>, <C-c> and <C-]>.
+ if ale#Var(a:buffer, 'lint_on_insert_leave')
+ \&& maparg("\<C-c>", 'i') isnot# '<Esc>'
+ \&& !s:mode_changed_exists
+ call timer_stop(s:insert_leave_timer)
+ let s:insert_leave_timer = timer_start(
+ \ 100,
+ \ {-> ale#events#EmulateInsertLeave(a:buffer) },
+ \ {'repeat': -1}
+ \)
+ endif
+endfunction
+
+function! ale#events#InsertLeaveEvent(buffer) abort
+ " Kill the InsertLeave emulation if the event fired.
+ " If the ModeChanged event is available, it will be used instead of
+ " a timer.
+ if !s:mode_changed_exists
+ call timer_stop(s:insert_leave_timer)
+ endif
+
+ if ale#Var(a:buffer, 'lint_on_insert_leave')
+ call ale#Queue(0, '', a:buffer)
+ 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.
+ "
+ " We don't echo this message in emulated insert leave mode, as the user
+ " may want less work to happen on pressing <C-c> versus <Esc>
+ if exists('*ale#engine#Cleanup')
+ call ale#cursor#EchoCursorWarning()
+
+ if g:ale_virtualtext_cursor is# 'current' || g:ale_virtualtext_cursor is# 1 || g:ale_virtualtext_cursor is# '1'
+ " Show a virtualtext message if enabled.
+ call ale#virtualtext#ShowCursorWarning()
+ endif
+ endif
+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#events#ReadOrEnterEvent(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'
+ \|| g:ale_lint_on_text_changed is v:true
+ autocmd TextChanged,TextChangedI * call ale#Queue(ale#Var(str2nr(expand('<abuf>')), 'lint_delay'))
+ elseif l:text_changed is? 'normal'
+ autocmd TextChanged * call ale#Queue(ale#Var(str2nr(expand('<abuf>')), 'lint_delay'))
+ elseif l:text_changed is? 'insert'
+ autocmd TextChangedI * call ale#Queue(ale#Var(str2nr(expand('<abuf>')), 'lint_delay'))
+ endif
+
+ if g:ale_lint_on_enter
+ autocmd BufWinEnter * call ale#events#LintOnEnter(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
+
+ " Add an InsertEnter event if we need to close the preview window
+ " on entering insert mode, or if we want to run ALE on leaving
+ " insert mode and <C-c> is not the same as <Esc>.
+ "
+ " We will emulate leaving insert mode for users that might not
+ " trigger InsertLeave.
+ "
+ " If the ModeChanged event is available, this timer will not
+ " be used.
+ if g:ale_close_preview_on_insert
+ \|| (g:ale_lint_on_insert_leave && maparg("\<C-c>", 'i') isnot# '<Esc>' && !s:mode_changed_exists)
+ autocmd InsertEnter * call ale#events#InsertEnterEvent(str2nr(expand('<abuf>')))
+ endif
+
+ let l:add_insert_leave_event = g:ale_lint_on_insert_leave
+
+ if g:ale_echo_cursor || g:ale_cursor_detail
+ " We need to make the message display on InsertLeave
+ let l:add_insert_leave_event = 1
+
+ autocmd CursorMoved,CursorHold * if exists('*ale#engine#Cleanup') | call ale#cursor#EchoCursorWarningWithDelay() | endif
+ endif
+
+ if g:ale_virtualtext_cursor is# 'current' || g:ale_virtualtext_cursor is# 1 || g:ale_virtualtext_cursor is# '1'
+ " We need to make the message display on InsertLeave
+ let l:add_insert_leave_event = 1
+
+ autocmd CursorMoved,CursorHold * if exists('*ale#engine#Cleanup') | call ale#virtualtext#ShowCursorWarningWithDelay() | endif
+ endif
+
+ if l:add_insert_leave_event
+ if s:mode_changed_exists
+ " If the ModeChanged event is available, handle any
+ " transition from the Insert mode to any other mode.
+ autocmd ModeChanged i*:* call ale#events#InsertLeaveEvent(str2nr(expand('<abuf>')))
+ else
+ " If ModeChanged is not available, handle InsertLeave events.
+ autocmd InsertLeave * call ale#events#InsertLeaveEvent(str2nr(expand('<abuf>')))
+ endif
+ endif
+
+ if g:ale_hover_cursor
+ autocmd CursorHold * if exists('*ale#lsp#Send') | call ale#hover#ShowTruncatedMessageAtCursor() | endif
+ endif
+ endif
+ augroup END
+
+ augroup AleURISchemes
+ autocmd!
+
+ autocmd BufNewFile,BufReadPre jdt://** call ale#uri#jdt#ReadJDTLink(expand('<amatch>'))
+ augroup END
+endfunction