#697 - Clear all highlights every time items are set again, and refactor most things. Clear errors when linters are removed

This commit is contained in:
w0rp 2017-07-07 23:47:41 +01:00
parent 46225f3bb1
commit 8eb4f95766
14 changed files with 274 additions and 212 deletions

View File

@ -53,9 +53,17 @@ function! ale#Queue(delay, ...) abort
let s:lint_timer = -1 let s:lint_timer = -1
endif endif
let l:linters = ale#linter#Get(&filetype) let l:buffer = bufnr('')
if len(l:linters) == 0 let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype'))
" There are no linters to lint with, so stop here.
" Don't set up buffer data and so on if there are no linters to run.
if empty(l:linters)
" If we have some previous buffer data, then stop any jobs currently
" running and clear everything.
if has_key(g:ale_buffer_info, l:buffer)
call ale#engine#RunLinters(l:buffer, [], 1)
endif
return return
endif endif
@ -68,16 +76,16 @@ function! ale#Queue(delay, ...) abort
endfunction endfunction
function! ale#Lint(...) abort function! ale#Lint(...) abort
if ale#ShouldDoNothing()
return
endif
" Get the buffer number linting was queued for. " Get the buffer number linting was queued for.
" or else take the current one. " or else take the current one.
let l:buffer = len(a:0) > 1 && a:1 == s:lint_timer let l:buffer = len(a:0) > 1 && a:1 == s:lint_timer
\ ? s:queued_buffer_number \ ? s:queued_buffer_number
\ : bufnr('%') \ : bufnr('%')
if ale#ShouldDoNothing()
return
endif
" Use the filetype from the buffer " Use the filetype from the buffer
let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype')) let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype'))
let l:should_lint_file = 0 let l:should_lint_file = 0
@ -89,27 +97,7 @@ function! ale#Lint(...) abort
let l:should_lint_file = filereadable(expand('#' . l:buffer . ':p')) let l:should_lint_file = filereadable(expand('#' . l:buffer . ':p'))
endif endif
" Initialise the buffer information if needed. call ale#engine#RunLinters(l:buffer, l:linters, l:should_lint_file)
call ale#engine#InitBufferInfo(l:buffer)
" Clear the new loclist again, so we will work with all new items.
let g:ale_buffer_info[l:buffer].new_loclist = []
if l:should_lint_file
" Clear loclist items for files if we are checking files again.
let g:ale_buffer_info[l:buffer].lint_file_loclist = []
else
" Otherwise, don't run any `lint_file` linters
" We will continue running any linters which are currently checking
" the file, and the items will be mixed together with any new items.
call filter(l:linters, '!v:val.lint_file')
endif
call ale#engine#StopCurrentJobs(l:buffer, l:should_lint_file)
for l:linter in l:linters
call ale#engine#Invoke(l:buffer, l:linter)
endfor
endfunction endfunction
" Reset flags indicating that files should be checked for all buffers. " Reset flags indicating that files should be checked for all buffers.

View File

@ -1,19 +0,0 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: Utility functions related to cleaning state.
function! ale#cleanup#Buffer(buffer) abort
if has_key(g:ale_buffer_info, a:buffer)
call ale#engine#RemoveManagedFiles(a:buffer)
" When buffers are removed, clear all of the jobs.
call ale#engine#StopCurrentJobs(a:buffer, 1)
" Clear delayed highlights for a buffer being removed.
if g:ale_set_highlights
call ale#highlight#UnqueueHighlights(a:buffer)
call ale#highlight#RemoveHighlights([])
endif
call remove(g:ale_buffer_info, a:buffer)
endif
endfunction

View File

@ -46,6 +46,14 @@ function! ale#engine#InitBufferInfo(buffer) abort
endif endif
endfunction endfunction
" Return 1 if ALE is busy checking a given buffer
function! ale#engine#IsCheckingBuffer(buffer) abort
let l:info = get(g:ale_buffer_info, a:buffer, {})
return get(l:info, 'waiting_for_tsserver') == 1
\|| !empty(get(l:info, 'job_list'))
endfunction
" Register a temporary file to be managed with the ALE engine for " Register a temporary file to be managed with the ALE engine for
" a current job run. " a current job run.
function! ale#engine#ManageFile(buffer, filename) abort function! ale#engine#ManageFile(buffer, filename) abort
@ -69,9 +77,7 @@ function! ale#engine#CreateDirectory(buffer) abort
endfunction endfunction
function! ale#engine#RemoveManagedFiles(buffer) abort function! ale#engine#RemoveManagedFiles(buffer) abort
if !has_key(g:ale_buffer_info, a:buffer) let l:info = get(g:ale_buffer_info, a:buffer)
return
endif
" We can't delete anything in a sandbox, so wait until we escape from " We can't delete anything in a sandbox, so wait until we escape from
" it to delete temporary files and directories. " it to delete temporary files and directories.
@ -80,21 +86,25 @@ function! ale#engine#RemoveManagedFiles(buffer) abort
endif endif
" Delete files with a call akin to a plan `rm` command. " Delete files with a call akin to a plan `rm` command.
for l:filename in g:ale_buffer_info[a:buffer].temporary_file_list if has_key(l:info, 'temporary_file_list')
call delete(l:filename) for l:filename in l:info.temporary_file_list
endfor call delete(l:filename)
endfor
let g:ale_buffer_info[a:buffer].temporary_file_list = [] let l:info.temporary_file_list = []
endif
" Delete directories like `rm -rf`. " Delete directories like `rm -rf`.
" Directories are handled differently from files, so paths that are " Directories are handled differently from files, so paths that are
" intended to be single files can be set up for automatic deletion without " intended to be single files can be set up for automatic deletion without
" accidentally deleting entire directories. " accidentally deleting entire directories.
for l:directory in g:ale_buffer_info[a:buffer].temporary_directory_list if has_key(l:info, 'temporary_directory_list')
call delete(l:directory, 'rf') for l:directory in l:info.temporary_directory_list
endfor call delete(l:directory, 'rf')
endfor
let g:ale_buffer_info[a:buffer].temporary_directory_list = [] let l:info.temporary_directory_list = []
endif
endfunction endfunction
function! s:GatherOutput(job_id, line) abort function! s:GatherOutput(job_id, line) abort
@ -118,34 +128,11 @@ function! s:HandleLoclist(linter_name, buffer, loclist) abort
" for efficient lookup of the messages in the cursor handler. " for efficient lookup of the messages in the cursor handler.
call sort(g:ale_buffer_info[a:buffer].loclist, 'ale#util#LocItemCompare') call sort(g:ale_buffer_info[a:buffer].loclist, 'ale#util#LocItemCompare')
let l:linting_is_done = empty(g:ale_buffer_info[a:buffer].job_list) if ale#ShouldDoNothing()
\ && !get(g:ale_buffer_info[a:buffer], 'waiting_for_tsserver', 0) return
if l:linting_is_done
" Automatically remove all managed temporary files and directories
" now that all jobs have completed.
call ale#engine#RemoveManagedFiles(a:buffer)
" Figure out which linters are still enabled, and remove
" problems for linters which are no longer enabled.
let l:name_map = {}
for l:linter in ale#linter#Get(getbufvar(a:buffer, '&filetype'))
let l:name_map[l:linter.name] = 1
endfor
call filter(
\ g:ale_buffer_info[a:buffer].loclist,
\ 'get(l:name_map, v:val.linter_name)',
\)
endif endif
call ale#engine#SetResults(a:buffer, g:ale_buffer_info[a:buffer].loclist) call ale#engine#SetResults(a:buffer, g:ale_buffer_info[a:buffer].loclist)
if l:linting_is_done
" Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd User ALELint
endif
endfunction endfunction
function! s:HandleExit(job_id, exit_code) abort function! s:HandleExit(job_id, exit_code) abort
@ -217,10 +204,7 @@ function! s:HandleLSPResponse(response) abort
endfunction endfunction
function! ale#engine#SetResults(buffer, loclist) abort function! ale#engine#SetResults(buffer, loclist) abort
let l:info = get(g:ale_buffer_info, a:buffer, {}) let l:linting_is_done = !ale#engine#IsCheckingBuffer(a:buffer)
let l:job_list = get(l:info, 'job_list', [])
let l:waiting_for_tsserver = get(l:info, 'waiting_for_tsserver', 0)
let l:linting_is_done = empty(l:job_list) && !l:waiting_for_tsserver
" Set signs first. This could potentially fix some line numbers. " Set signs first. This could potentially fix some line numbers.
" The List could be sorted again here by SetSigns. " The List could be sorted again here by SetSigns.
@ -254,6 +238,15 @@ function! ale#engine#SetResults(buffer, loclist) abort
" This will only do something meaningful if we're in normal mode. " This will only do something meaningful if we're in normal mode.
call ale#cursor#EchoCursorWarning() call ale#cursor#EchoCursorWarning()
endif endif
if l:linting_is_done
" Automatically remove all managed temporary files and directories
" now that all jobs have completed.
call ale#engine#RemoveManagedFiles(a:buffer)
" Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd User ALELint
endif
endfunction endfunction
function! s:RemapItemTypes(type_map, loclist) abort function! s:RemapItemTypes(type_map, loclist) abort
@ -371,6 +364,9 @@ function! s:CreateTemporaryFileForJob(buffer, temporary_file) abort
return 1 return 1
endfunction endfunction
" Run a job.
"
" Returns 1 when the job was started successfully.
function! s:RunJob(options) abort function! s:RunJob(options) abort
let l:command = a:options.command let l:command = a:options.command
let l:buffer = a:options.buffer let l:buffer = a:options.buffer
@ -379,6 +375,10 @@ function! s:RunJob(options) abort
let l:next_chain_index = a:options.next_chain_index let l:next_chain_index = a:options.next_chain_index
let l:read_buffer = a:options.read_buffer let l:read_buffer = a:options.read_buffer
if empty(l:command)
return 0
endif
let [l:temporary_file, l:command] = ale#command#FormatCommand(l:buffer, l:command, l:read_buffer) let [l:temporary_file, l:command] = ale#command#FormatCommand(l:buffer, l:command, l:read_buffer)
if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file) if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file)
@ -457,6 +457,8 @@ function! s:RunJob(options) abort
call l:job_options.exit_cb(l:job_id, v:shell_error) call l:job_options.exit_cb(l:job_id, v:shell_error)
endif endif
return l:job_id != 0
endfunction endfunction
" Determine which commands to run for a link in a command chain, or " Determine which commands to run for a link in a command chain, or
@ -516,11 +518,6 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
let l:command = ale#linter#GetCommand(a:buffer, a:linter) let l:command = ale#linter#GetCommand(a:buffer, a:linter)
endif endif
if empty(l:command)
" Don't run any jobs if the command is an empty string.
return {}
endif
return { return {
\ 'command': l:command, \ 'command': l:command,
\ 'buffer': a:buffer, \ 'buffer': a:buffer,
@ -534,16 +531,10 @@ endfunction
function! s:InvokeChain(buffer, linter, chain_index, input) abort function! s:InvokeChain(buffer, linter, chain_index, input) abort
let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input) let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input)
if !empty(l:options) return s:RunJob(l:options)
call s:RunJob(l:options)
elseif empty(g:ale_buffer_info[a:buffer].job_list)
" If we cancelled running a command, and we have no jobs in progress,
" then delete the managed temporary files now.
call ale#engine#RemoveManagedFiles(a:buffer)
endif
endfunction endfunction
function! ale#engine#StopCurrentJobs(buffer, include_lint_file_jobs) abort function! s:StopCurrentJobs(buffer, include_lint_file_jobs) abort
let l:info = get(g:ale_buffer_info, a:buffer, {}) let l:info = get(g:ale_buffer_info, a:buffer, {})
let l:new_job_list = [] let l:new_job_list = []
@ -562,9 +553,6 @@ function! ale#engine#StopCurrentJobs(buffer, include_lint_file_jobs) abort
" Update the List, so it includes only the jobs we still need. " Update the List, so it includes only the jobs we still need.
let l:info.job_list = l:new_job_list let l:info.job_list = l:new_job_list
" Ignore current LSP commands.
" We should consider cancelling them in future.
let l:info.lsp_command_list = []
endfunction endfunction
function! s:CheckWithTSServer(buffer, linter, executable) abort function! s:CheckWithTSServer(buffer, linter, executable) abort
@ -584,7 +572,7 @@ function! s:CheckWithTSServer(buffer, linter, executable) abort
call ale#history#Add(a:buffer, 'failed', l:id, l:command) call ale#history#Add(a:buffer, 'failed', l:id, l:command)
endif endif
return return 0
endif endif
if ale#lsp#OpenTSServerDocumentIfNeeded(l:id, a:buffer) if ale#lsp#OpenTSServerDocumentIfNeeded(l:id, a:buffer)
@ -603,21 +591,88 @@ function! s:CheckWithTSServer(buffer, linter, executable) abort
if l:request_id != 0 if l:request_id != 0
let l:info.waiting_for_tsserver = 1 let l:info.waiting_for_tsserver = 1
endif endif
return l:request_id != 0
endfunction endfunction
function! ale#engine#Invoke(buffer, linter) abort function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
" Figure out which linters are still enabled, and remove
" problems for linters which are no longer enabled.
let l:name_map = {}
for l:linter in a:linters
let l:name_map[l:linter.name] = 1
endfor
call filter(
\ get(g:ale_buffer_info[a:buffer], 'loclist', []),
\ 'get(l:name_map, v:val.linter_name)',
\)
endfunction
" Run a linter for a buffer.
"
" Returns 1 if the linter was successfully run.
function! s:RunLinter(buffer, linter) abort
if empty(a:linter.lsp) || a:linter.lsp ==# 'tsserver' if empty(a:linter.lsp) || a:linter.lsp ==# 'tsserver'
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
" Run this program if it can be executed. " Run this program if it can be executed.
if s:IsExecutable(l:executable) if s:IsExecutable(l:executable)
if a:linter.lsp ==# 'tsserver' if a:linter.lsp ==# 'tsserver'
call s:CheckWithTSServer(a:buffer, a:linter, l:executable) return s:CheckWithTSServer(a:buffer, a:linter, l:executable)
else
call s:InvokeChain(a:buffer, a:linter, 0, [])
endif endif
return s:InvokeChain(a:buffer, a:linter, 0, [])
endif endif
endif endif
return 0
endfunction
function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort
" Initialise the buffer information if needed.
call ale#engine#InitBufferInfo(a:buffer)
call s:StopCurrentJobs(a:buffer, a:should_lint_file)
call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters)
let l:any_linter_ran = 0
for l:linter in a:linters
" Skip linters for checking files if we shouldn't check the file.
if l:linter.lint_file && !a:should_lint_file
continue
endif
if s:RunLinter(a:buffer, l:linter)
let l:any_linter_ran = 1
endif
endfor
" If we didn't manage to start checking the buffer with anything,
" and there's nothing running currently for the buffer, then clear the
" results.
"
" We need to use both checks, as we run some tests synchronously.
if !l:any_linter_ran && !ale#engine#IsCheckingBuffer(a:buffer)
call ale#engine#SetResults(a:buffer, [])
endif
endfunction
" Clean up a buffer.
"
" This function will stop all current jobs for the buffer,
" clear the state of everything, and remove the Dictionary for managing
" the buffer.
function! ale#engine#Cleanup(buffer) abort
call ale#engine#RunLinters(a:buffer, [], 1)
if g:ale_set_highlights
call ale#highlight#UnqueueHighlights(a:buffer)
call ale#highlight#RemoveHighlights([])
endif
call remove(g:ale_buffer_info, a:buffer)
endfunction endfunction
" Given a buffer number, return the warnings and errors for a given buffer. " Given a buffer number, return the warnings and errors for a given buffer.

View File

@ -67,27 +67,11 @@ function! s:GetALEMatches() abort
return filter(getmatches(), 'v:val.group =~# ''^ALE''') return filter(getmatches(), 'v:val.group =~# ''^ALE''')
endfunction endfunction
function! s:GetCurrentMatchIDs(loclist) abort
let l:current_id_map = {}
for l:item in a:loclist
for l:id in get(l:item, 'match_id_list', [])
let l:current_id_map[l:id] = 1
endfor
endfor
return l:current_id_map
endfunction
" Given a loclist for current items to highlight, remove all highlights " Given a loclist for current items to highlight, remove all highlights
" except these which have matching loclist item entries. " except these which have matching loclist item entries.
function! ale#highlight#RemoveHighlights(loclist) abort function! ale#highlight#RemoveHighlights(loclist) abort
let l:current_id_map = s:GetCurrentMatchIDs(a:loclist)
for l:match in s:GetALEMatches() for l:match in s:GetALEMatches()
if !has_key(l:current_id_map, l:match.id) call matchdelete(l:match.id)
call matchdelete(l:match.id)
endif
endfor endfor
endfunction endfunction
@ -100,9 +84,6 @@ function! ale#highlight#UpdateHighlights() abort
call ale#highlight#RemoveHighlights(l:loclist) call ale#highlight#RemoveHighlights(l:loclist)
endif endif
" Remove anything with a current match_id
call filter(l:loclist, '!has_key(v:val, ''match_id_list'')')
" Restore items from the map of hidden items, " Restore items from the map of hidden items,
" if we don't have some new items to set already. " if we don't have some new items to set already.
if empty(l:loclist) && has_key(s:buffer_restore_map, l:buffer) if empty(l:loclist) && has_key(s:buffer_restore_map, l:buffer)
@ -132,10 +113,7 @@ function! ale#highlight#UpdateHighlights() abort
" Set all of the positions, which are chunked into Lists which " Set all of the positions, which are chunked into Lists which
" are as large as will be accepted by matchaddpos. " are as large as will be accepted by matchaddpos.
" call map(
" We will remember the IDs we set, so we can preserve some
" highlights when linting buffers after linting files.
let l:item.match_id_list = map(
\ ale#highlight#CreatePositions(l:line, l:col, l:end_line, l:end_col), \ ale#highlight#CreatePositions(l:line, l:col, l:end_line, l:end_col),
\ 'matchaddpos(l:group, v:val)' \ 'matchaddpos(l:group, v:val)'
\) \)
@ -148,14 +126,6 @@ function! ale#highlight#BufferHidden(buffer) abort
" Remember loclist items, so they can be restored later. " Remember loclist items, so they can be restored later.
if !empty(l:loclist) if !empty(l:loclist)
" Remove match_ids, as they must be re-calculated when buffers are
" shown again.
for l:item in l:loclist
if has_key(l:item, 'match_id_list')
call remove(l:item, 'match_id_list')
endif
endfor
let s:buffer_restore_map[a:buffer] = filter( let s:buffer_restore_map[a:buffer] = filter(
\ copy(l:loclist), \ copy(l:loclist),
\ 'v:val.bufnr == a:buffer && v:val.col > 0' \ 'v:val.bufnr == a:buffer && v:val.col > 0'

View File

@ -15,5 +15,9 @@ function! ale#test#SetFilename(path) abort
let l:dir = getcwd() let l:dir = getcwd()
endif endif
silent noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:dir . '/' . a:path)) let l:full_path = ale#path#IsAbsolute(a:path)
\ ? a:path
\ : l:dir . '/' . a:path
silent noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path))
endfunction endfunction

View File

@ -293,10 +293,9 @@ function! s:ALEToggle() abort
" Make sure the buffer number is a number, not a string, " Make sure the buffer number is a number, not a string,
" otherwise things can go wrong. " otherwise things can go wrong.
for l:buffer in map(keys(g:ale_buffer_info), 'str2nr(v:val)') for l:buffer in map(keys(g:ale_buffer_info), 'str2nr(v:val)')
" Stop jobs and delete stored buffer data " Stop all jobs and clear the results for everything, and delete
call ale#cleanup#Buffer(l:buffer) " all of the data we stored for the buffer.
" Clear signs, loclist, quicklist call ale#engine#Cleanup(l:buffer)
call ale#engine#SetResults(l:buffer, [])
endfor endfor
" Remove highlights for the current buffer now. " Remove highlights for the current buffer now.
@ -368,7 +367,7 @@ nnoremap <silent> <Plug>(ale_fix) :ALEFix<Return>
augroup ALECleanupGroup augroup ALECleanupGroup
autocmd! autocmd!
" Clean up buffers automatically when they are unloaded. " Clean up buffers automatically when they are unloaded.
autocmd BufUnload * call ale#cleanup#Buffer(expand('<abuf>')) autocmd BufUnload * call ale#engine#Cleanup(str2nr(expand('<abuf>')))
augroup END augroup END
" Backwards Compatibility " Backwards Compatibility

View File

@ -8,7 +8,7 @@ After:
call ale#linter#Reset() call ale#linter#Reset()
" We need to clean up the buffer to remove the temporary directories created " We need to clean up the buffer to remove the temporary directories created
" for the command. " for the command.
call ale#cleanup#Buffer(bufnr('')) call ale#engine#Cleanup(bufnr(''))
let g:ale_java_javac_options = '' let g:ale_java_javac_options = ''
let g:ale_java_javac_classpath = '' let g:ale_java_javac_classpath = ''
@ -63,7 +63,7 @@ Execute(The javac callback should combine discovered classpaths and manual ones)
\ 'Invalid command string: ' . b:command \ 'Invalid command string: ' . b:command
Execute(The javac callback should detect source directories): Execute(The javac callback should detect source directories):
call ale#cleanup#Buffer(bufnr('')) call ale#engine#Cleanup(bufnr(''))
:e! java_paths/src/main/java/com/something/dummy :e! java_paths/src/main/java/com/something/dummy
call ale#engine#InitBufferInfo(bufnr('')) call ale#engine#InitBufferInfo(bufnr(''))
@ -73,7 +73,7 @@ Execute(The javac callback should detect source directories):
\ 'Invalid command string: ' . b:command \ 'Invalid command string: ' . b:command
Execute(The javac callback should combine detected source directories and classpaths): Execute(The javac callback should combine detected source directories and classpaths):
call ale#cleanup#Buffer(bufnr('')) call ale#engine#Cleanup(bufnr(''))
:e! java_paths/src/main/java/com/something/dummy :e! java_paths/src/main/java/com/something/dummy
call ale#engine#InitBufferInfo(bufnr('')) call ale#engine#InitBufferInfo(bufnr(''))

View File

@ -11,7 +11,6 @@ Before:
\ 'valid': 1, \ 'valid': 1,
\}] \}]
let g:expected_groups = [ let g:expected_groups = [
\ 'ALEBufferFixGroup',
\ 'ALECleanupGroup', \ 'ALECleanupGroup',
\ 'ALECursorGroup', \ 'ALECursorGroup',
\ 'ALEHighlightBufferGroup', \ 'ALEHighlightBufferGroup',
@ -43,7 +42,10 @@ Before:
for l:line in split(l:output, "\n") for l:line in split(l:output, "\n")
let l:match = matchlist(l:line, '^ALE[a-zA-Z]\+Group') let l:match = matchlist(l:line, '^ALE[a-zA-Z]\+Group')
" We don't care about checking for the completion or fixing groups here.
if !empty(l:match) if !empty(l:match)
\&& l:match[0] !=# 'ALECompletionGroup'
\&& l:match[0] !=# 'ALEBufferFixGroup'
call add(l:results, l:match[0]) call add(l:results, l:match[0])
endif endif
endfor endfor
@ -94,15 +96,17 @@ Execute(ALEToggle should reset everything and then run again):
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
\ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}')
AssertEqual g:expected_groups, ParseAuGroups() AssertEqual g:expected_groups, ParseAuGroups()
AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist
" Now Toggle ALE off. " Now Toggle ALE off.
ALEToggle ALEToggle
" Everything should be cleared. " Everything should be cleared.
AssertEqual [], getloclist(0) Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed'
AssertEqual [], ale#sign#FindCurrentSigns(bufnr('%')) AssertEqual [], getloclist(0), 'The loclist was not cleared'
AssertEqual [], getmatches() AssertEqual [], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared'
AssertEqual ['ALEBufferFixGroup', 'ALECleanupGroup', 'ALEHighlightBufferGroup'], ParseAuGroups() AssertEqual [], getmatches(), 'The highlights were not cleared'
AssertEqual ['ALECleanupGroup', 'ALEHighlightBufferGroup'], ParseAuGroups()
" Toggle ALE on, everything should be set up and run again. " Toggle ALE on, everything should be set up and run again.
ALEToggle ALEToggle
@ -114,3 +118,4 @@ Execute(ALEToggle should reset everything and then run again):
\ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
\ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}')
AssertEqual g:expected_groups, ParseAuGroups() AssertEqual g:expected_groups, ParseAuGroups()
AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist

View File

@ -0,0 +1,58 @@
Before:
Save g:ale_run_synchronously
let b:old_filetype = &filetype
let g:ale_run_synchronously = 1
noautocmd let &filetype = 'foobar'
function! TestCallback(buffer, output)
return [{'text': 'x', 'lnum': 1}]
endfunction
call ale#linter#Define('foobar', {
\ 'name': 'buffer_linter',
\ 'callback': 'TestCallback',
\ 'executable': 'true',
\ 'command': 'true',
\ 'read_buffer': 0,
\})
call ale#linter#Define('foobar2', {
\ 'name': 'buffer_linter',
\ 'callback': 'TestCallback',
\ 'executable': 'true',
\ 'command': 'true',
\ 'read_buffer': 0,
\})
After:
Restore
noautocmd let &filetype = b:old_filetype
unlet b:old_filetype
delfunction TestCallback
if has_key(g:ale_buffer_info, bufnr(''))
call remove(g:ale_buffer_info, bufnr(''))
endif
call ale#Queue(0)
Execute(Error should be removed when the filetype changes to something else we cannot check):
call ale#Queue(0)
AssertEqual 1, len(getloclist(0))
noautocmd let &filetype = 'foobar2'
call ale#Queue(0)
" We should get some items from the second filetype.
AssertEqual 1, len(getloclist(0))
noautocmd let &filetype = 'xxx'
call ale#Queue(0)
AssertEqual 0, len(getloclist(0))

View File

@ -22,6 +22,18 @@ Before:
\] \]
endfunction endfunction
" We don't care what the IDs are, just that we have some matches.
" The IDs are generated.
function! GetMatchesWithoutIDs() abort
let l:list = getmatches()
for l:item in l:list
call remove(l:item, 'id')
endfor
return l:list
endfunction
call ale#linter#Define('testft', { call ale#linter#Define('testft', {
\ 'name': 'x', \ 'name': 'x',
\ 'executable': 'echo', \ 'executable': 'echo',
@ -51,33 +63,11 @@ Execute(Highlights should be set when a linter runs):
AssertEqual AssertEqual
\ [ \ [
\ {'group': 'ALEError', 'id': 4, 'priority': 10, 'pos1': [1, 1, 1]}, \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]},
\ {'group': 'ALEWarning', 'id': 5, 'priority': 10, 'pos1': [2, 1, 1]}, \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]},
\ {'group': 'ALEError', 'id': 6, 'priority': 10, 'pos1': [3, 5, 1]} \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 5, 1]}
\ ], \ ],
\ getmatches() \ GetMatchesWithoutIDs()
AssertEqual [[4], [5], [6]], map(copy(g:ale_buffer_info[bufnr('')].loclist), 'v:val.match_id_list')
Execute(Existing highlights should be kept):
call matchaddpos('ALEError', [[1, 2, 1]], 10, 347)
call matchaddpos('ALEWarning', [[2, 2, 1]], 10, 348)
call ale#highlight#SetHighlights(bufnr('%'), [
\ {'bufnr': bufnr('%'), 'match_id_list': [347], 'type': 'E', 'lnum': 1, 'col': 2},
\ {'bufnr': bufnr('%'), 'match_id_list': [348], 'type': 'W', 'lnum': 2, 'col': 2},
\ {'bufnr': bufnr('%'), 'type': 'E', 'lnum': 3, 'col': 2},
\ {'bufnr': bufnr('%'), 'type': 'W', 'lnum': 4, 'col': 1},
\])
AssertEqual
\ [
\ {'group': 'ALEError', 'id': 347, 'priority': 10, 'pos1': [1, 2, 1]},
\ {'group': 'ALEWarning', 'id': 348, 'priority': 10, 'pos1': [2, 2, 1]},
\ {'group': 'ALEError', 'id': 7, 'priority': 10, 'pos1': [3, 2, 1]},
\ {'group': 'ALEWarning', 'id': 8, 'priority': 10, 'pos1': [4, 1, 1]},
\ ],
\ getmatches()
" This test is important for preventing ALE from showing highlights for " This test is important for preventing ALE from showing highlights for
" the wrong files. " the wrong files.
@ -89,12 +79,12 @@ Execute(Highlights set by ALE should be removed when buffer cleanup is done):
\]) \])
AssertEqual AssertEqual
\ [{'group': 'ALEError', 'id': 9, 'priority': 10, 'pos1': [3, 2, 1]}], \ [{'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}],
\ getmatches() \ GetMatchesWithoutIDs()
call ale#cleanup#Buffer(bufnr('%')) call ale#engine#Cleanup(bufnr('%'))
AssertEqual [], getmatches() AssertEqual [], GetMatchesWithoutIDs()
Execute(Highlights should be cleared when buffers are hidden): Execute(Highlights should be cleared when buffers are hidden):
call ale#engine#InitBufferInfo(bufnr('%')) call ale#engine#InitBufferInfo(bufnr('%'))
@ -108,15 +98,15 @@ Execute(Highlights should be cleared when buffers are hidden):
\ g:ale_buffer_info[bufnr('%')].loclist \ g:ale_buffer_info[bufnr('%')].loclist
\) \)
AssertEqual 1, len(getmatches()), 'The highlights weren''t initially set!' AssertEqual 1, len(GetMatchesWithoutIDs()), 'The highlights weren''t initially set!'
call ale#highlight#BufferHidden(bufnr('%')) call ale#highlight#BufferHidden(bufnr('%'))
AssertEqual 0, len(getmatches()), 'The highlights weren''t cleared!' AssertEqual 0, len(GetMatchesWithoutIDs()), 'The highlights weren''t cleared!'
call ale#highlight#UpdateHighlights() call ale#highlight#UpdateHighlights()
AssertEqual 1, len(getmatches()), 'The highlights weren''t set again!' AssertEqual 1, len(GetMatchesWithoutIDs()), 'The highlights weren''t set again!'
Execute(Only ALE highlights should be restored when buffers are restored): Execute(Only ALE highlights should be restored when buffers are restored):
call ale#engine#InitBufferInfo(bufnr('%')) call ale#engine#InitBufferInfo(bufnr('%'))
@ -131,16 +121,16 @@ Execute(Only ALE highlights should be restored when buffers are restored):
call matchaddpos('SomeOtherGroup', [[1, 1, 1]]) call matchaddpos('SomeOtherGroup', [[1, 1, 1]])
" We should have one more match here. " We should have one more match here.
AssertEqual 2, len(getmatches()), 'The highlights weren''t initially set!' AssertEqual 2, len(GetMatchesWithoutIDs()), 'The highlights weren''t initially set!'
call ale#highlight#BufferHidden(bufnr('%')) call ale#highlight#BufferHidden(bufnr('%'))
AssertEqual 0, len(getmatches()), 'The highlights weren''t cleared!' AssertEqual 0, len(GetMatchesWithoutIDs()), 'The highlights weren''t cleared!'
call ale#highlight#UpdateHighlights() call ale#highlight#UpdateHighlights()
" Only our matches should appear again. " Only our matches should appear again.
AssertEqual 1, len(getmatches()), 'The highlights weren''t set again!' AssertEqual 1, len(GetMatchesWithoutIDs()), 'The highlights weren''t set again!'
Execute(Higlight end columns should set an appropriate size): Execute(Higlight end columns should set an appropriate size):
call ale#highlight#SetHighlights(bufnr('%'), [ call ale#highlight#SetHighlights(bufnr('%'), [
@ -150,10 +140,10 @@ Execute(Higlight end columns should set an appropriate size):
AssertEqual AssertEqual
\ [ \ [
\ {'group': 'ALEError', 'id': 15, 'priority': 10, 'pos1': [3, 2, 4]}, \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 4]},
\ {'group': 'ALEWarning', 'id': 16, 'priority': 10, 'pos1': [4, 1, 5]}, \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [4, 1, 5]},
\ ], \ ],
\ getmatches() \ GetMatchesWithoutIDs()
Execute(Higlight end columns should set an appropriate size): Execute(Higlight end columns should set an appropriate size):
call ale#highlight#SetHighlights(bufnr('%'), [ call ale#highlight#SetHighlights(bufnr('%'), [
@ -168,15 +158,15 @@ Execute(Higlight end columns should set an appropriate size):
AssertEqual AssertEqual
\ [ \ [
\ {'group': 'ALEError', 'id': 17, 'priority': 10, 'pos1': [1, 1, 1]}, \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]},
\ {'group': 'ALEError', 'id': 18, 'priority': 10, 'pos1': [2, 1, 1]}, \ {'group': 'ALEError', 'priority': 10, 'pos1': [2, 1, 1]},
\ {'group': 'ALEStyleError', 'id': 19, 'priority': 10, 'pos1': [3, 1, 1]}, \ {'group': 'ALEStyleError', 'priority': 10, 'pos1': [3, 1, 1]},
\ {'group': 'ALEWarning', 'id': 20, 'priority': 10, 'pos1': [4, 1, 1]}, \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [4, 1, 1]},
\ {'group': 'ALEWarning', 'id': 21, 'priority': 10, 'pos1': [5, 1, 1]}, \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [5, 1, 1]},
\ {'group': 'ALEStyleWarning', 'id': 22, 'priority': 10, 'pos1': [6, 1, 1]}, \ {'group': 'ALEStyleWarning', 'priority': 10, 'pos1': [6, 1, 1]},
\ {'group': 'ALEInfo', 'id': 23, 'priority': 10, 'pos1': [7, 1, 1]}, \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [7, 1, 1]},
\ ], \ ],
\ getmatches() \ GetMatchesWithoutIDs()
Execute(Highlighting should support errors spanning many lines): Execute(Highlighting should support errors spanning many lines):
let g:items = [ let g:items = [
@ -189,15 +179,13 @@ Execute(Highlighting should support errors spanning many lines):
AssertEqual AssertEqual
\ [ \ [
\ { \ {
\ 'group': 'ALEError', 'id': 24, 'priority': 10, 'pos1': [1, 1, 1073741824], \ 'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1073741824],
\ 'pos2': [2], 'pos3': [3], 'pos4': [4], 'pos5': [5], 'pos6': [6], \ 'pos2': [2], 'pos3': [3], 'pos4': [4], 'pos5': [5], 'pos6': [6],
\ 'pos7': [7], 'pos8': [8], \ 'pos7': [7], 'pos8': [8],
\ }, \ },
\ { \ {
\ 'group': 'ALEError', 'id': 25, 'priority': 10, \ 'group': 'ALEError', 'priority': 10,
\ 'pos1': [9], 'pos2': [10, 1, 3] \ 'pos1': [9], 'pos2': [10, 1, 3]
\ }, \ },
\ ], \ ],
\ getmatches() \ GetMatchesWithoutIDs()
AssertEqual [[24, 25]], map(copy(g:items), 'v:val.match_id_list')

View File

@ -69,6 +69,10 @@ Before:
\ 'read_buffer': 0, \ 'read_buffer': 0,
\}) \})
let g:filename = tempname()
call writefile([], g:filename)
call ale#test#SetFilename(g:filename)
After: After:
Restore Restore
@ -79,6 +83,12 @@ After:
delfunction LintFileCallback delfunction LintFileCallback
delfunction BufferCallback delfunction BufferCallback
if filereadable(g:filename)
call delete(g:filename)
endif
unlet g:filename
Given foobar (Some imaginary filetype): Given foobar (Some imaginary filetype):
foo foo
bar bar
@ -107,6 +117,9 @@ Execute(Running linters without 'lint_file' should run only buffer linters):
Execute(Running linters with 'lint_file' should run all linters): Execute(Running linters with 'lint_file' should run all linters):
call ale#ResetLintFileMarkers() call ale#ResetLintFileMarkers()
let g:ale_buffer_info = {} let g:ale_buffer_info = {}
Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file') call ale#Queue(0, 'lint_file')
AssertEqual [ AssertEqual [
@ -139,6 +152,9 @@ Execute(Running linters with 'lint_file' should run all linters):
Execute(Linter errors from files should be kept): Execute(Linter errors from files should be kept):
call ale#ResetLintFileMarkers() call ale#ResetLintFileMarkers()
let g:ale_buffer_info = {} let g:ale_buffer_info = {}
Assert filereadable(expand('%:p')), 'The file was not readable'
call ale#Queue(0, 'lint_file') call ale#Queue(0, 'lint_file')
" Change the results for the buffer callback. " Change the results for the buffer callback.

View File

@ -30,6 +30,8 @@ After:
call setloclist(0, []) call setloclist(0, [])
Execute(The file changed event function should set b:ale_file_changed): Execute(The file changed event function should set b:ale_file_changed):
let g:ale_lint_on_enter = 0
if has('gui') if has('gui')
new new
else else

View File

@ -65,8 +65,4 @@ Execute(The loclist should be updated after linting is done):
call ale#engine#WaitForJobs(2000) call ale#engine#WaitForJobs(2000)
AssertEqual ['' . bufnr('%')], keys(g:ale_buffer_info) AssertEqual ['' . bufnr('%')], keys(g:ale_buffer_info)
let g:expected_data[0].match_id_list = [getmatches()[0].id]
let g:expected_data[1].match_id_list = [getmatches()[1].id]
AssertEqual g:expected_data, g:ale_buffer_info[bufnr('%')].loclist AssertEqual g:expected_data, g:ale_buffer_info[bufnr('%')].loclist

View File

@ -84,7 +84,7 @@ Execute(ALE should delete managed files even if no command is run):
Execute(ALE should delete managed files when the buffer is removed): Execute(ALE should delete managed files when the buffer is removed):
call ale#engine#InitBufferInfo(bufnr('%')) call ale#engine#InitBufferInfo(bufnr('%'))
call TestCommandCallback(bufnr('%')) call TestCommandCallback(bufnr('%'))
call ale#cleanup#Buffer(bufnr('%')) call ale#engine#Cleanup(bufnr('%'))
Assert !filereadable(g:filename), 'The temporary file was not deleted' Assert !filereadable(g:filename), 'The temporary file was not deleted'
Assert !isdirectory(g:directory), 'The temporary directory was not deleted' Assert !isdirectory(g:directory), 'The temporary directory was not deleted'
@ -105,7 +105,7 @@ Execute(ALE should create and delete directories for ale#engine#CreateDirectory(
" The two directories shouldn't be the same. " The two directories shouldn't be the same.
AssertNotEqual b:dir2, b:dir AssertNotEqual b:dir2, b:dir
call ale#cleanup#Buffer(bufnr('%')) call ale#engine#Cleanup(bufnr('%'))
Assert !isdirectory(b:dir), 'The directory was not deleted' Assert !isdirectory(b:dir), 'The directory was not deleted'
Assert !isdirectory(b:dir2), 'The second directory was not deleted' Assert !isdirectory(b:dir2), 'The second directory was not deleted'