summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md3
-rw-r--r--ISSUE_TEMPLATE.md6
-rw-r--r--Makefile11
-rw-r--r--ale_linters/fortran/gcc.vim2
-rw-r--r--autoload/ale.vim15
-rw-r--r--autoload/ale/cleanup.vim5
-rw-r--r--autoload/ale/cursor.vim3
-rw-r--r--autoload/ale/debugging.vim117
-rw-r--r--autoload/ale/engine.vim67
-rw-r--r--autoload/ale/filetypes.vim60
-rw-r--r--autoload/ale/highlight.vim75
-rw-r--r--autoload/ale/linter.vim26
-rw-r--r--autoload/ale/sign.vim8
-rw-r--r--autoload/ale/statusline.vim12
-rw-r--r--autoload/ale/util.vim16
-rw-r--r--doc/ale.txt27
-rw-r--r--plugin/ale.vim40
-rw-r--r--test/test_ale_info.vader116
-rw-r--r--test/test_ale_toggle.vader112
-rw-r--r--test/test_filetype_mapping.vader29
-rw-r--r--test/test_fortran_handler.vader118
-rw-r--r--test/test_highlight_clearing.vader4
-rw-r--r--test/test_highlight_placement.vader61
-rw-r--r--test/test_history_saving.vader69
-rw-r--r--test/test_sandbox_execution.vader51
-rw-r--r--test/test_statusline_api_without_globals.vader19
26 files changed, 1013 insertions, 59 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8cd1007a..e40ed5dc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -20,6 +20,9 @@ Have fun, and work on whatever floats your boat. Take It Easy :tm:.
Don't forget to **write documentation** for whatever it is you are doing.
See the ["Writing Documentation"](#writing-documentation) section.
+Remember to write Vader tests for most of the code you write. You can look at
+existing Vader tests in the `test` directory for examples.
+
When writing code, follow the [Google Vimscript Style
Guide](https://google.github.io/styleguide/vimscriptguide.xml), and run `vint
-s` on your files to check for most of what the guide mentions and more. If you
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..492016b3
--- /dev/null
+++ b/ISSUE_TEMPLATE.md
@@ -0,0 +1,6 @@
+For bugs, paste output from your clipboard after running :ALEInfoToClipboard
+here. If that doesn't work for some reason, try running :ALEInfo and copying
+the output from that here instead. If everything is broken, run around in
+circles and scream.
+
+Whatever the case, describe the your issue here.
diff --git a/Makefile b/Makefile
index a8c1da4d..5e939c1e 100644
--- a/Makefile
+++ b/Makefile
@@ -2,11 +2,20 @@ SHELL := /usr/bin/env bash
IMAGE ?= w0rp/ale
CURRENT_IMAGE_ID = 107e4efc4267
DOCKER_FLAGS = --rm -v $(PWD):/testplugin -v $(PWD)/test:/home "$(IMAGE)"
+tests = test/*
test-setup:
docker images -q w0rp/ale | grep ^$(CURRENT_IMAGE_ID) > /dev/null || \
docker pull $(IMAGE)
+vader: test-setup
+ @:; \
+ vims=$$(docker run --rm $(IMAGE) ls /vim-build/bin | grep -E '^n?vim'); \
+ if [ -z "$$vims" ]; then echo "No Vims found!"; exit 1; fi; \
+ for vim in $$vims; do \
+ docker run -a stderr $(DOCKER_FLAGS) $$vim '+Vader! $(tests)'; \
+ done
+
test: test-setup
@:; \
vims=$$(docker run --rm $(IMAGE) ls /vim-build/bin | grep -E '^n?vim'); \
@@ -18,7 +27,7 @@ test: test-setup
echo "Running tests for $$vim"; \
echo '========================================'; \
echo; \
- docker run -a stderr $(DOCKER_FLAGS) $$vim '+Vader! test/*' || EXIT=$$?; \
+ docker run -a stderr $(DOCKER_FLAGS) $$vim '+Vader! $(tests)' || EXIT=$$?; \
done; \
echo; \
echo '========================================'; \
diff --git a/ale_linters/fortran/gcc.vim b/ale_linters/fortran/gcc.vim
index 73ec7886..8d15cc00 100644
--- a/ale_linters/fortran/gcc.vim
+++ b/ale_linters/fortran/gcc.vim
@@ -12,7 +12,7 @@ function! ale_linters#fortran#gcc#Handle(buffer, lines) abort
"
" :21.34:
" Error: Expected comma in I/O list at (1)
- let l:line_marker_pattern = '^:\(\d\+\)\.\(\d\+\):$'
+ let l:line_marker_pattern = ':\(\d\+\)[.:]\=\(\d\+\)\=:\=$'
let l:message_pattern = '^\(Error\|Warning\): \(.\+\)$'
let l:looking_for_message = 0
let l:last_loclist_obj = {}
diff --git a/autoload/ale.vim b/autoload/ale.vim
index a5251f64..c81a57a6 100644
--- a/autoload/ale.vim
+++ b/autoload/ale.vim
@@ -4,9 +4,17 @@
let s:lint_timer = -1
+" A function for checking various conditions whereby ALE just shouldn't
+" attempt to do anything, say if particular buffer types are open in Vim.
+function! ale#ShouldDoNothing() abort
+ " Do nothing for blacklisted files
+ " OR if ALE is running in the sandbox
+ return index(g:ale_filetype_blacklist, &filetype) >= 0
+ \ || ale#util#InSandbox()
+endfunction
+
function! ale#Queue(delay) abort
- " Do nothing for blacklisted files.
- if index(g:ale_filetype_blacklist, &filetype) >= 0
+ if ale#ShouldDoNothing()
return
endif
@@ -29,8 +37,7 @@ function! ale#Queue(delay) abort
endfunction
function! ale#Lint(...) abort
- " Do nothing for blacklisted files.
- if index(g:ale_filetype_blacklist, &filetype) >= 0
+ if ale#ShouldDoNothing()
return
endif
diff --git a/autoload/ale/cleanup.vim b/autoload/ale/cleanup.vim
index d720cc96..3b0b1d90 100644
--- a/autoload/ale/cleanup.vim
+++ b/autoload/ale/cleanup.vim
@@ -10,6 +10,11 @@ function! ale#cleanup#Buffer(buffer) abort
call ale#engine#ClearJob(l:job)
endfor
+ " Clear delayed highlights for a buffer being removed.
+ if g:ale_set_highlights
+ call ale#highlight#UnqueueHighlights(a:buffer)
+ endif
+
call remove(g:ale_buffer_info, a:buffer)
endif
endfunction
diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim
index 864f6f28..9aaa8e91 100644
--- a/autoload/ale/cursor.vim
+++ b/autoload/ale/cursor.vim
@@ -73,8 +73,7 @@ let s:cursor_timer = -1
let s:last_pos = [0, 0, 0]
function! ale#cursor#EchoCursorWarningWithDelay() abort
- " Do nothing for blacklisted files.
- if index(g:ale_filetype_blacklist, &filetype) >= 0
+ if ale#ShouldDoNothing()
return
endif
diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim
new file mode 100644
index 00000000..737a90d7
--- /dev/null
+++ b/autoload/ale/debugging.vim
@@ -0,0 +1,117 @@
+" Author: w0rp <devw0rp@gmail.com>
+" Description: This file implements debugging information for ALE
+
+let s:global_variable_list = [
+\ 'ale_echo_cursor',
+\ 'ale_echo_msg_error_str',
+\ 'ale_echo_msg_format',
+\ 'ale_echo_msg_warning_str',
+\ 'ale_enabled',
+\ 'ale_keep_list_window_open',
+\ 'ale_lint_delay',
+\ 'ale_lint_on_enter',
+\ 'ale_lint_on_save',
+\ 'ale_lint_on_text_changed',
+\ 'ale_linter_aliases',
+\ 'ale_linters',
+\ 'ale_open_list',
+\ 'ale_set_highlights',
+\ 'ale_set_loclist',
+\ 'ale_set_quickfix',
+\ 'ale_set_signs',
+\ 'ale_sign_column_always',
+\ 'ale_sign_error',
+\ 'ale_sign_offset',
+\ 'ale_sign_warning',
+\ 'ale_statusline_format',
+\ 'ale_warn_about_trailing_whitespace',
+\]
+
+function! s:GetLinterVariables(filetype, linter_names) abort
+ let l:variable_list = []
+ let l:filetype_parts = split(a:filetype, '\.')
+
+ for l:key in keys(g:)
+ " Extract variable names like: 'ale_python_flake8_executable'
+ let l:match = matchlist(l:key, '\v^ale_([^_]+)_([^_]+)_.+$')
+
+ " Include matching variables.
+ if !empty(l:match)
+ \&& index(l:filetype_parts, l:match[1]) >= 0
+ \&& index(a:linter_names, l:match[2]) >= 0
+ call add(l:variable_list, l:key)
+ endif
+ endfor
+
+ call sort(l:variable_list)
+
+ return l:variable_list
+endfunction
+
+function! s:EchoLinterVariables(variable_list) abort
+ for l:key in a:variable_list
+ echom 'let g:' . l:key . ' = ' . string(g:[l:key])
+ endfor
+endfunction
+
+function! s:EchoGlobalVariables() abort
+ for l:key in s:global_variable_list
+ echom 'let g:' . l:key . ' = ' . string(get(g:, l:key, v:null))
+ endfor
+endfunction
+
+function! s:EchoCommandHistory() abort
+ let l:buffer = bufnr('%')
+
+ if !has_key(g:ale_buffer_info, l:buffer)
+ return
+ endif
+
+ for l:item in g:ale_buffer_info[l:buffer].history
+ echom '(' . l:item.status . ') ' . string(l:item.command)
+ endfor
+endfunction
+
+function! ale#debugging#Info() abort
+ let l:filetype = &filetype
+
+ " We get the list of enabled linters for free by the above function.
+ let l:enabled_linters = deepcopy(ale#linter#Get(l:filetype))
+
+ " But have to build the list of available linters ourselves.
+ let l:all_linters = []
+ let l:linter_variable_list = []
+
+ for l:part in split(l:filetype, '\.')
+ let l:aliased_filetype = ale#linter#ResolveFiletype(l:part)
+ call extend(l:all_linters, ale#linter#GetAll(l:aliased_filetype))
+ endfor
+
+ let l:all_names = map(l:all_linters, 'v:val[''name'']')
+ let l:enabled_names = map(l:enabled_linters, 'v:val[''name'']')
+
+ " Load linter variables to display
+ " This must be done after linters are loaded.
+ let l:variable_list = s:GetLinterVariables(l:filetype, l:enabled_names)
+
+ echom ' Current Filetype: ' . l:filetype
+ echom 'Available Linters: ' . string(l:all_names)
+ echom ' Enabled Linters: ' . string(l:enabled_names)
+ echom ' Linter Variables:'
+ echom ''
+ call s:EchoLinterVariables(l:variable_list)
+ echom ' Global Variables:'
+ echom ''
+ call s:EchoGlobalVariables()
+ echom ' Command History:'
+ echom ''
+ call s:EchoCommandHistory()
+endfunction
+
+function! ale#debugging#InfoToClipboard() abort
+ redir @+>
+ silent call ale#debugging#Info()
+ redir END
+
+ echom 'ALEInfo copied to your clipboard'
+endfunction
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 7d22ad11..b54ad6ee 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -9,6 +9,30 @@
" output: The array of lines for the output of the job.
let s:job_info_map = {}
+function! ale#engine#AddToHistory(buffer, status, job_id, command) abort
+ if g:ale_max_buffer_history_size <= 0
+ " Don't save anything if the history isn't a positive number.
+ let g:ale_buffer_info[a:buffer].history = []
+
+ return
+ endif
+
+ let l:history = g:ale_buffer_info[a:buffer].history
+
+ " Remove the first item if we hit the max history size.
+ if len(l:history) >= g:ale_max_buffer_history_size
+ let l:history = l:history[1:]
+ endif
+
+ call add(l:history, {
+ \ 'status': a:status,
+ \ 'job_id': a:job_id,
+ \ 'command': a:command,
+ \})
+
+ let g:ale_buffer_info[a:buffer].history = l:history
+endfunction
+
function! s:GetJobID(job) abort
if has('nvim')
"In NeoVim, job values are just IDs.
@@ -19,6 +43,7 @@ function! s:GetJobID(job) abort
" job. We'll use the ID of the channel instead as the job ID.
try
return ch_info(job_getchannel(a:job)).id
+ catch
endtry
" If we fail to get the channel ID for a job, just return a 0 ID.
@@ -32,12 +57,14 @@ function! ale#engine#InitBufferInfo(buffer) abort
" new_loclist holds loclist items while jobs are being run.
" temporary_file_list holds temporary files to be cleaned up
" temporary_directory_list holds temporary directories to be cleaned up
+ " history holds a list of previously run commands for this buffer
let g:ale_buffer_info[a:buffer] = {
\ 'job_list': [],
\ 'loclist': [],
\ 'new_loclist': [],
\ 'temporary_file_list': [],
\ 'temporary_directory_list': [],
+ \ 'history': [],
\}
endif
endfunction
@@ -154,6 +181,12 @@ function! ale#engine#RemoveManagedFiles(buffer) abort
return
endif
+ " We can't delete anything in a sandbox, so wait until we escape from
+ " it to delete temporary files and directories.
+ if ale#util#InSandbox()
+ return
+ endif
+
" Delete files with a call akin to a plan `rm` command.
for l:filename in g:ale_buffer_info[a:buffer].temporary_file_list
call delete(l:filename)
@@ -194,6 +227,12 @@ function! s:HandleExit(job) abort
" which just closed.
call s:StopPreviousJobs(l:buffer, l:linter)
+ " Stop here if we land in the handle for a job completing if we're in
+ " a sandbox.
+ if ale#util#InSandbox()
+ return
+ endif
+
if l:next_chain_index < len(get(l:linter, 'command_chain', []))
call s:InvokeChain(l:buffer, l:linter, l:next_chain_index, l:output)
return
@@ -234,9 +273,6 @@ function! s:HandleExit(job) abort
" Call user autocommands. This allows users to hook into ALE's lint cycle.
silent doautocmd User ALELint
-
- " Mark line 200, column 17 with a squiggly line or something
- " matchadd('ALEError', '\%200l\%17v')
endfunction
function! ale#engine#SetResults(buffer, loclist) abort
@@ -252,6 +288,10 @@ function! ale#engine#SetResults(buffer, loclist) abort
" Don't load/run if not already loaded.
call ale#statusline#Update(a:buffer, a:loclist)
endif
+
+ if g:ale_set_highlights
+ call ale#highlight#SetHighlights(a:buffer, a:loclist)
+ endif
endfunction
function! s:HandleExitNeoVim(job, data, event) abort
@@ -285,11 +325,17 @@ function! ale#engine#EscapeCommandPart(command_part) abort
endfunction
function! s:TemporaryFilename(buffer) abort
+ let l:filename = fnamemodify(bufname(a:buffer), ':t')
+
+ if empty(l:filename)
+ " If the buffer's filename is empty, create a dummy filename.
+ let l:ft = getbufvar(a:buffer, '&filetype')
+ let l:filename = 'file' . ale#filetypes#GuessExtension(l:ft)
+ endif
+
" Create a temporary filename, <temp_dir>/<original_basename>
" The file itself will not be created by this function.
- return tempname()
- \ . (has('win32') ? '\' : '/')
- \ . fnamemodify(bufname(a:buffer), ':t')
+ return tempname() . (has('win32') ? '\' : '/') . l:filename
endfunction
" Given a command string, replace every...
@@ -417,19 +463,26 @@ function! s:RunJob(options) abort
let l:job = job_start(l:command, l:job_options)
endif
+ let l:status = 'failed'
+ let l:job_id = 0
+
" Only proceed if the job is being run.
if has('nvim') || (l:job !=# 'no process' && job_status(l:job) ==# 'run')
" Add the job to the list of jobs, so we can track them.
call add(g:ale_buffer_info[l:buffer].job_list, l:job)
+ let l:status = 'ran'
+ let l:job_id = s:GetJobID(l:job)
" Store the ID for the job in the map to read back again.
- let s:job_info_map[s:GetJobID(l:job)] = {
+ let s:job_info_map[l:job_id] = {
\ 'linter': l:linter,
\ 'buffer': l:buffer,
\ 'output': [],
\ 'next_chain_index': l:next_chain_index,
\}
endif
+
+ call ale#engine#AddToHistory(l:buffer, l:status, l:job_id, l:command)
endfunction
" Determine which commands to run for a link in a command chain, or
diff --git a/autoload/ale/filetypes.vim b/autoload/ale/filetypes.vim
new file mode 100644
index 00000000..6174aa0e
--- /dev/null
+++ b/autoload/ale/filetypes.vim
@@ -0,0 +1,60 @@
+" Author: w0rp <devw0rp@gmail.com>
+" Description: This file handles guessing file extensions for filetypes, etc.
+
+function! ale#filetypes#LoadExtensionMap() abort
+ " Output includes:
+ " '*.erl setf erlang'
+ redir => l:output
+ silent exec 'autocmd'
+ redir end
+
+ let l:map = {}
+
+ for l:line in split(l:output, "\n")
+ " Parse filetypes, like so:
+ "
+ " *.erl setf erlang
+ " *.md set filetype=markdown
+ " *.snippet setlocal filetype=snippets
+ let l:match = matchlist(l:line, '\v^ *\*(\.[^ ]+).*set(f *| *filetype=|local *filetype=)([^ ]+)')
+
+ if !empty(l:match)
+ let l:map[substitute(l:match[3], '^=', '', '')] = l:match[1]
+ endif
+ endfor
+
+ return l:map
+endfunction
+
+let s:cached_map = {}
+
+function! s:GetCachedExtensionMap() abort
+ if empty(s:cached_map)
+ let s:cached_map = ale#filetypes#LoadExtensionMap()
+ endif
+
+ return s:cached_map
+endfunction
+
+function! ale#filetypes#GuessExtension(filetype) abort
+ let l:map = s:GetCachedExtensionMap()
+ let l:ext = get(l:map, a:filetype, '')
+
+ " If we have an exact match, like something for javascript.jsx, use that.
+ if !empty(l:ext)
+ return l:ext
+ endif
+
+ " If we don't have an exact match, use the first filetype in the compound
+ " filetype.
+ for l:part in split(a:filetype, '\.')
+ let l:ext = get(l:map, l:part, '')
+
+ if !empty(l:ext)
+ return l:ext
+ endif
+ endfor
+
+ " Return an empty string if we don't find anything.
+ return ''
+endfunction
diff --git a/autoload/ale/highlight.vim b/autoload/ale/highlight.vim
new file mode 100644
index 00000000..1669ebd2
--- /dev/null
+++ b/autoload/ale/highlight.vim
@@ -0,0 +1,75 @@
+scriptencoding utf8
+" Author: w0rp <devw0rp@gmail.com>
+" Description: This module implements error/warning highlighting.
+
+if !hlexists('ALEError')
+ highlight link ALEError SpellBad
+endif
+
+if !hlexists('ALEWarning')
+ highlight link ALEWarning SpellCap
+endif
+
+" This map holds highlights to be set when buffers are opened.
+" We can only set highlights for whatever the current buffer is, so we will
+" wait until the buffer is entered again to show the highlights, unless
+" the buffer is in focus when linting completes.
+let s:buffer_highlights = {}
+
+function! ale#highlight#UnqueueHighlights(buffer) abort
+ if has_key(s:buffer_highlights, a:buffer)
+ call remove(s:buffer_highlights, a:buffer)
+ endif
+endfunction
+
+function! s:RemoveOldHighlights() abort
+ for l:match in getmatches()
+ if l:match['group'] ==# 'ALEError' || l:match['group'] ==# 'ALEWarning'
+ call matchdelete(l:match['id'])
+ endif
+ endfor
+endfunction
+
+function! ale#highlight#UpdateHighlights() abort
+ let l:buffer = bufnr('%')
+ let l:has_new_items = has_key(s:buffer_highlights, l:buffer)
+ let l:loclist = l:has_new_items ? remove(s:buffer_highlights, l:buffer) : []
+
+ if l:has_new_items || !g:ale_enabled
+ call s:RemoveOldHighlights()
+ endif
+
+ if l:has_new_items
+ for l:item in l:loclist
+ let l:col = l:item.col
+ let l:group = l:item.type ==# 'E' ? 'ALEError' : 'ALEWarning'
+ let l:line = l:item.lnum
+ let l:size = 1
+
+ call matchaddpos(l:group, [[l:line, l:col, l:size]])
+ endfor
+ endif
+endfunction
+
+augroup ALEHighlightBufferGroup
+ autocmd!
+ autocmd BufEnter * call ale#highlight#UpdateHighlights()
+augroup END
+
+function! ale#highlight#SetHighlights(buffer, loclist) abort
+ " Only set set items for the buffer if ALE is enabled.
+ if g:ale_enabled
+ " Set a list of items to be set as highlights for a buffer when
+ " we next open it.
+ "
+ " We'll filter the loclist down to items we can set now.
+ let s:buffer_highlights[a:buffer] = filter(
+ \ deepcopy(a:loclist),
+ \ 'v:val.bufnr == a:buffer && v:val.col > 0'
+ \)
+
+ " Update highlights for the current buffer, which may or may not
+ " be the buffer we just set highlights for.
+ call ale#highlight#UpdateHighlights()
+ endif
+endfunction
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index 260c145b..e586c1f3 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -180,7 +180,7 @@ function! ale#linter#GetAll(filetype) abort
return s:linters[a:filetype]
endfunction
-function! s:ResolveFiletype(original_filetype) abort
+function! ale#linter#ResolveFiletype(original_filetype) abort
" Try and get an aliased file type either from the user's Dictionary, or
" our default Dictionary, otherwise use the filetype as-is.
let l:filetype = get(
@@ -201,7 +201,7 @@ function! ale#linter#Get(original_filetypes) abort
" Handle dot-seperated filetypes.
for l:original_filetype in split(a:original_filetypes, '\.')
- let l:filetype = s:ResolveFiletype(l:original_filetype)
+ let l:filetype = ale#linter#ResolveFiletype(l:original_filetype)
" Try and get a list of linters to run, using the original file type,
" not the aliased filetype. We have some linters to limit by default,
@@ -235,25 +235,3 @@ function! ale#linter#Get(original_filetypes) abort
return l:combined_linters
endfunction
-
-function! ale#linter#Info() abort
- let l:original_filetypes = &filetype
-
- " We get the list of enabled linters for free by the above function.
- let l:enabled_linters = deepcopy(ale#linter#Get(l:original_filetypes))
-
- " But have to build the list of available linters ourselves.
- let l:all_linters = []
- for l:original_filetype in split(l:original_filetypes, '\.')
- let l:filetype = s:ResolveFiletype(l:original_filetype)
- let l:filetype_linters = ale#linter#GetAll(l:filetype)
- call extend(l:all_linters, l:filetype_linters)
- endfor
-
- let l:all_names = map(l:all_linters, 'v:val[''name'']')
- let l:enabled_names = map(l:enabled_linters, 'v:val[''name'']')
-
- echom ' Current Filetype: ' . l:original_filetypes
- echom 'Available Linters: ' . string(l:all_names)
- echom ' Enabled Linters: ' . string(l:enabled_names)
-endfunction
diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim
index e2b3fa74..9d613480 100644
--- a/autoload/ale/sign.vim
+++ b/autoload/ale/sign.vim
@@ -12,14 +12,6 @@ if !hlexists('ALEWarningSign')
highlight link ALEWarningSign todo
endif
-if !hlexists('ALEError')
- highlight link ALEError SpellBad
-endif
-
-if !hlexists('ALEWarning')
- highlight link ALEWarning SpellCap
-endif
-
" Signs show up on the left for error markers.
execute 'sign define ALEErrorSign text=' . g:ale_sign_error
\ . ' texthl=ALEErrorSign'
diff --git a/autoload/ale/statusline.vim b/autoload/ale/statusline.vim
index 7269ddca..efb7e9e5 100644
--- a/autoload/ale/statusline.vim
+++ b/autoload/ale/statusline.vim
@@ -3,6 +3,10 @@
" Update the buffer error/warning count with data from loclist.
function! ale#statusline#Update(buffer, loclist) abort
+ if !exists('g:ale_buffer_info')
+ return
+ endif
+
if !has_key(g:ale_buffer_info, a:buffer)
return
endif
@@ -39,6 +43,10 @@ endfunction
" Returns a tuple of errors and warnings for use in third-party integrations.
function! ale#statusline#Count(buffer) abort
+ if !exists('g:ale_buffer_info')
+ return [0, 0]
+ endif
+
if !s:SetupCount(a:buffer)
return [0, 0]
endif
@@ -48,6 +56,10 @@ endfunction
" Returns a formatted string that can be integrated in the statusline.
function! ale#statusline#Status() abort
+ if !exists('g:ale_buffer_info')
+ return 'OK'
+ endif
+
let [l:error_format, l:warning_format, l:no_errors] = g:ale_statusline_format
let l:buffer = bufnr('%')
diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim
index 6367489c..bf00051a 100644
--- a/autoload/ale/util.vim
+++ b/autoload/ale/util.vim
@@ -118,3 +118,19 @@ function! ale#util#BinarySearch(loclist, line, column) abort
endif
endwhile
endfunction
+
+" A function for testing if a function is running inside a sandbox.
+" See :help sandbox
+function! ale#util#InSandbox() abort
+ try
+ call setbufvar('%', '', '')
+ catch /^Vim\%((\a\+)\)\=:E48/
+ " E48 is the sandbox error.
+ return 1
+ catch
+ " If we're not in a sandbox, we'll get another error about an
+ " invalid buffer variable name.
+ endtry
+
+ return 0
+endfunction
diff --git a/doc/ale.txt b/doc/ale.txt
index abb0c000..93cd184b 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -176,6 +176,16 @@ g:ale_echo_msg_warning_str *g:ale_echo_msg_warning_str*
Note |`g:ale_echo_msg_format`| should contain the `%severity%` handler
+g:ale_enabled *g:ale_enabled*
+
+ Type: |Number|
+ Default: `1`
+
+ When set to `0`, this option will completely disable ALE, such that no
+ error checking will be performed, etc. ALE can be toggled on and off with
+ the |ALEToggle| command, which changes this option.
+
+
g:ale_keep_list_window_open *g:ale_keep_list_window_open*
Type: |Number|
@@ -313,6 +323,17 @@ g:ale_open_list *g:ale_open_list*
to `1`, in which case the window will be kept open until closed manually.
+g:ale_set_highlights *g:ale_set_highlights*
+
+ Type: |Number|
+ Default: `has('syntax')`
+
+ When this option is set to `1`, highlights will be set in for erros and
+ warnings. The `ALEError` and `ALEWarning` highlight groups will be used to
+ provide highlights, and default to linking to `SpellBad` and `SpellCap`
+ respectively by default.
+
+
g:ale_set_loclist *g:ale_set_loclist*
Type: |Number|
@@ -1060,6 +1081,12 @@ ALENextWrap *ALENextWrap*
nmap <silent> <C-j> <Plug>(ale_next_wrap)
<
+ALEToggle *ALEToggle*
+
+ Enable or disable ALE, including all of its autocmd events, loclist items,
+ quickfix items, signs, current jobs, etc. Calling this option will change
+ the |g:ale_enabled| variable.
+
===============================================================================
7. API *ale-api*
diff --git a/plugin/ale.vim b/plugin/ale.vim
index d65a99cb..32aea550 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -4,11 +4,13 @@
" Sanity Checks
-if exists('g:loaded_ale')
+if exists('g:loaded_ale_dont_use_this_in_other_plugins_please')
finish
endif
-let g:loaded_ale = 1
+" Set a special flag used only by this plugin for preventing doubly
+" loading the script.
+let g:loaded_ale_dont_use_this_in_other_plugins_please = 1
" A flag for detecting if the required features are set.
if has('nvim')
@@ -20,11 +22,19 @@ else
endif
if !s:has_features
- echoerr 'ALE requires NeoVim >= 0.1.5 or Vim 8 with +timers +job +channel'
- echoerr 'Please update your editor appropriately.'
+ " Only output a warning if editing some special files.
+ if index(['', 'gitcommit'], &filetype) == -1
+ echoerr 'ALE requires NeoVim >= 0.1.5 or Vim 8 with +timers +job +channel'
+ echoerr 'Please update your editor appropriately.'
+ endif
+
+ " Stop here, as it won't work.
finish
endif
+" Set this flag so that other plugins can use it, like airline.
+let g:loaded_ale = 1
+
" Set the TMPDIR environment variable if it is not set automatically.
" This can automatically fix some environments.
if has('unix') && empty($TMPDIR)
@@ -81,6 +91,9 @@ let g:ale_keep_list_window_open = get(g:, 'ale_keep_list_window_open', 0)
" This is enabled by default only if the 'signs' feature exists.
let g:ale_set_signs = get(g:, 'ale_set_signs', has('signs'))
+" This flag can be set to 0 to disable setting error highlights.
+let g:ale_set_highlights = get(g:, 'ale_set_highlights', has('syntax'))
+
" These variables dicatate what sign is used to indicate errors and warnings.
let g:ale_sign_error = get(g:, 'ale_sign_error', '>>')
let g:ale_sign_warning = get(g:, 'ale_sign_warning', '--')
@@ -118,6 +131,9 @@ let g:ale_statusline_format = get(g:, 'ale_statusline_format',
let g:ale_warn_about_trailing_whitespace =
\ get(g:, 'ale_warn_about_trailing_whitespace', 1)
+" A flag for controlling the maximum size of the command history to store.
+let g:ale_max_buffer_history_size = get(g:, 'ale_max_buffer_history_size', 20)
+
function! s:ALEInitAuGroups() abort
augroup ALERunOnTextChangedGroup
autocmd!
@@ -146,6 +162,13 @@ function! s:ALEInitAuGroups() abort
autocmd CursorMoved,CursorHold * call ale#cursor#EchoCursorWarningWithDelay()
endif
augroup END
+
+ if !g:ale_enabled
+ augroup! ALERunOnTextChangedGroup
+ augroup! ALERunOnEnterGroup
+ augroup! ALERunOnSaveGroup
+ augroup! ALECursorGroup
+ endif
endfunction
function! s:ALEToggle() abort
@@ -161,6 +184,11 @@ function! s:ALEToggle() abort
" Clear signs, loclist, quicklist
call ale#engine#SetResults(l:buffer, [])
endfor
+
+ " Remove highlights for the current buffer now.
+ if g:ale_set_highlights
+ call ale#highlight#UpdateHighlights()
+ endif
endif
call s:ALEInitAuGroups()
@@ -177,7 +205,9 @@ command! ALENextWrap :call ale#loclist_jumping#Jump('after', 1)
command! ALEToggle :call s:ALEToggle()
" Define command to get information about current filetype.
-command! ALEInfo :call ale#linter#Info()
+command! ALEInfo :call ale#debugging#Info()
+" The same, but copy output to your clipboard.
+command! ALEInfoToClipboard :call ale#debugging#InfoToClipboard()
" <Plug> mappings for commands
nnoremap <silent> <Plug>(ale_previous) :ALEPrevious<Return>
diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader
index f8172c0c..76699d60 100644
--- a/test/test_ale_info.vader
+++ b/test/test_ale_info.vader
@@ -5,9 +5,46 @@ Before:
call ale#linter#Reset()
let g:ale_linters = {}
let g:ale_linter_aliases = {}
+ let g:ale_buffer_info = {}
+ let g:globals_string = join([
+ \ '',
+ \ ' Global Variables:',
+ \ '',
+ \ 'let g:ale_echo_cursor = 1',
+ \ 'let g:ale_echo_msg_error_str = ''Error''',
+ \ 'let g:ale_echo_msg_format = ''%s''',
+ \ 'let g:ale_echo_msg_warning_str = ''Warning''',
+ \ 'let g:ale_enabled = 1',
+ \ 'let g:ale_keep_list_window_open = 0',
+ \ 'let g:ale_lint_delay = 200',
+ \ 'let g:ale_lint_on_enter = 1',
+ \ 'let g:ale_lint_on_save = 0',
+ \ 'let g:ale_lint_on_text_changed = 1',
+ \ 'let g:ale_linter_aliases = {}',
+ \ 'let g:ale_linters = {}',
+ \ 'let g:ale_open_list = 0',
+ \ 'let g:ale_set_highlights = 1',
+ \ 'let g:ale_set_loclist = 1',
+ \ 'let g:ale_set_quickfix = 0',
+ \ 'let g:ale_set_signs = 1',
+ \ 'let g:ale_sign_column_always = 0',
+ \ 'let g:ale_sign_error = ''>>''',
+ \ 'let g:ale_sign_offset = 1000000',
+ \ 'let g:ale_sign_warning = ''--''',
+ \ 'let g:ale_statusline_format = [''%d error(s)'', ''%d warning(s)'', ''OK'']',
+ \ 'let g:ale_warn_about_trailing_whitespace = 1',
+ \], "\n")
+ let g:command_header = "\n Command History:\n"
After:
unlet! g:output
+ unlet! g:globals_string
+ unlet! g:command_header
+ let g:ale_buffer_info = {}
+ unlet! g:ale_testft_testlinter1_foo
+ unlet! g:ale_testft_testlinter1_bar
+ unlet! g:ale_testft2_testlinter2_foo
+ unlet! g:ale_testft2_testlinter2_bar
Given nolintersft (Empty buffer with no linters):
Execute (ALEInfo with no linters should return the right output):
@@ -17,7 +54,9 @@ Execute (ALEInfo with no linters should return the right output):
AssertEqual "\n
\ Current Filetype: nolintersft\n
\Available Linters: []\n
- \ Enabled Linters: []", g:output
+ \ Enabled Linters: []\n
+ \ Linter Variables:\n
+ \" . g:globals_string . g:command_header, g:output
Given (Empty buffer with no filetype):
Execute (ALEInfo with no filetype should return the right output):
@@ -27,7 +66,9 @@ Execute (ALEInfo with no filetype should return the right output):
AssertEqual "\n
\ Current Filetype: \n
\Available Linters: []\n
- \ Enabled Linters: []", g:output
+ \ Enabled Linters: []\n
+ \ Linter Variables:\n
+ \" . g:globals_string . g:command_header, g:output
Given testft (Empty buffer):
Execute (ALEInfo with a single linter should return the right output):
@@ -38,7 +79,9 @@ Execute (ALEInfo with a single linter should return the right output):
AssertEqual "\n
\ Current Filetype: testft\n
\Available Linters: ['testlinter1']\n
- \ Enabled Linters: ['testlinter1']", g:output
+ \ Enabled Linters: ['testlinter1']\n
+ \ Linter Variables:\n
+ \" . g:globals_string . g:command_header, g:output
Given testft (Empty buffer):
Execute (ALEInfo with two linters should return the right output):
@@ -50,7 +93,9 @@ Execute (ALEInfo with two linters should return the right output):
AssertEqual "\n
\ Current Filetype: testft\n
\Available Linters: ['testlinter1', 'testlinter2']\n
- \ Enabled Linters: ['testlinter1', 'testlinter2']", g:output
+ \ Enabled Linters: ['testlinter1', 'testlinter2']\n
+ \ Linter Variables:\n
+ \" . g:globals_string . g:command_header, g:output
Given testft (Empty buffer):
Execute (ALEInfo should calculate enabled linters correctly):
@@ -63,7 +108,10 @@ Execute (ALEInfo should calculate enabled linters correctly):
AssertEqual "\n
\ Current Filetype: testft\n
\Available Linters: ['testlinter1', 'testlinter2']\n
- \ Enabled Linters: ['testlinter2']", g:output
+ \ Enabled Linters: ['testlinter2']\n
+ \ Linter Variables:\n
+ \",
+ \ "\n" . join(split(g:output, "\n")[:4], "\n")
Given testft (Empty buffer):
Execute (ALEInfo should only return linters for current filetype):
@@ -75,7 +123,9 @@ Execute (ALEInfo should only return linters for current filetype):
AssertEqual "\n
\ Current Filetype: testft\n
\Available Linters: ['testlinter1']\n
- \ Enabled Linters: ['testlinter1']", g:output
+ \ Enabled Linters: ['testlinter1']\n
+ \ Linter Variables:\n
+ \" . g:globals_string . g:command_header, g:output
Given testft.testft2 (Empty buffer with two filetypes):
Execute (ALEInfo with compound filetypes should return linters for both of them):
@@ -87,5 +137,57 @@ Execute (ALEInfo with compound filetypes should return linters for both of them)
AssertEqual "\n
\ Current Filetype: testft.testft2\n
\Available Linters: ['testlinter1', 'testlinter2']\n
- \ Enabled Linters: ['testlinter1', 'testlinter2']", g:output
+ \ Enabled Linters: ['testlinter1', 'testlinter2']\n
+ \ Linter Variables:\n
+ \" . g:globals_string . g:command_header, g:output
+Given testft.testft2 (Empty buffer with two filetypes):
+Execute (ALEInfo should return appropriately named global variables):
+ let g:ale_testft_testlinter1_foo = 'abc'
+ let g:ale_testft_testlinter1_bar = ['abc']
+ let g:ale_testft2_testlinter2_foo = 123
+ let g:ale_testft2_testlinter2_bar = {'x': 'y'}
+
+ call ale#linter#Define('testft', g:testlinter1)
+ call ale#linter#Define('testft2', g:testlinter2)
+ redir => g:output
+ silent ALEInfo
+ redir END
+ AssertEqual "\n
+ \ Current Filetype: testft.testft2\n
+ \Available Linters: ['testlinter1', 'testlinter2']\n
+ \ Enabled Linters: ['testlinter1', 'testlinter2']\n
+ \ Linter Variables:\n
+ \\n
+ \let g:ale_testft2_testlinter2_bar = {'x': 'y'}\n
+ \let g:ale_testft2_testlinter2_foo = 123\n
+ \let g:ale_testft_testlinter1_bar = ['abc']\n
+ \let g:ale_testft_testlinter1_foo = 'abc'"
+ \ . g:globals_string . g:command_header, g:output
+
+Given testft.testft2 (Empty buffer with two filetypes):
+Execute (ALEInfo should return command history):
+ let g:ale_buffer_info[bufnr('%')] = {
+ \ 'history': [
+ \ {'status': 'ran', 'job_id': 347, 'command': 'first command'},
+ \ {'status': 'ran', 'job_id': 347, 'command': ['/bin/bash', '\c', 'last command']},
+ \ ],
+ \}
+
+ call ale#linter#Define('testft', g:testlinter1)
+ call ale#linter#Define('testft2', g:testlinter2)
+ redir => g:output
+ silent ALEInfo
+ redir END
+ AssertEqual
+ \ join([
+ \ '',
+ \ ' Current Filetype: testft.testft2',
+ \ 'Available Linters: [''testlinter1'', ''testlinter2'']',
+ \ ' Enabled Linters: [''testlinter1'', ''testlinter2'']',
+ \ ' Linter Variables:',
+ \ g:globals_string . g:command_header,
+ \ '(ran) ''first command''',
+ \ '(ran) [''/bin/bash'', ''\c'', ''last command'']',
+ \ ], "\n"),
+ \ g:output
diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader
new file mode 100644
index 00000000..61557e8a
--- /dev/null
+++ b/test/test_ale_toggle.vader
@@ -0,0 +1,112 @@
+Before:
+ let g:expected_loclist = [{
+ \ 'bufnr': bufnr('%'),
+ \ 'lnum': 2,
+ \ 'vcol': 0,
+ \ 'col': 3,
+ \ 'text': 'foo bar',
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'pattern': '',
+ \ 'valid': 1,
+ \}]
+ let g:expected_groups = [
+ \ 'ALECleanupGroup',
+ \ 'ALECursorGroup',
+ \ 'ALEHighlightBufferGroup',
+ \ 'ALERunOnEnterGroup',
+ \ 'ALERunOnTextChangedGroup',
+ \]
+
+ function! ToggleTestCallback(buffer, output)
+ return [{
+ \ 'bufnr': a:buffer,
+ \ 'lnum': 2,
+ \ 'vcol': 0,
+ \ 'col': 3,
+ \ 'text': a:output[0],
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \}]
+ endfunction
+
+ function! ParseAuGroups()
+ redir => l:output
+ silent exec 'autocmd'
+ redir end
+
+ let l:results = []
+
+ for l:line in split(l:output, "\n")
+ let l:match = matchlist(l:line, '^ALE[a-zA-Z]\+Group')
+
+ if !empty(l:match)
+ call add(l:results, l:match[0])
+ endif
+ endfor
+
+ call uniq(sort(l:results))
+
+ return l:results
+ endfunction
+
+ call ale#linter#Define('foobar', {
+ \ 'name': 'testlinter',
+ \ 'callback': 'ToggleTestCallback',
+ \ 'executable': 'echo',
+ \ 'command': 'echo foo bar',
+ \})
+
+After:
+ unlet! g:expected_loclist
+ unlet! g:expected_groups
+
+ let g:ale_buffer_info = {}
+ call ale#linter#Reset()
+
+ " Toggle ALE back on if we fail when it's disabled.
+ if !g:ale_enabled
+ ALEToggle
+ endif
+
+ delfunction ToggleTestCallback
+ delfunction ParseAuGroups
+
+Given foobar (Some imaginary filetype):
+ foo
+ bar
+ baz
+
+Execute(ALEToggle should reset everything and then run again):
+ AssertEqual 'foobar', &filetype
+
+ call ale#Lint()
+ call ale#engine#WaitForJobs(2000)
+
+ " First check that everything is there...
+ AssertEqual g:expected_loclist, getloclist(0)
+ AssertEqual [1000001], ale#sign#FindCurrentSigns(bufnr('%'))
+ AssertEqual
+ \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
+ \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}')
+ AssertEqual g:expected_groups, ParseAuGroups()
+
+ " Now Toggle ALE off.
+ ALEToggle
+
+ " Everything should be cleared.
+ AssertEqual [], getloclist(0)
+ AssertEqual [], ale#sign#FindCurrentSigns(bufnr('%'))
+ AssertEqual [], getmatches()
+ AssertEqual ['ALECleanupGroup', 'ALEHighlightBufferGroup'], ParseAuGroups()
+
+ " Toggle ALE on, everything should be set up and run again.
+ ALEToggle
+ call ale#engine#WaitForJobs(2000)
+
+ AssertEqual g:expected_loclist, getloclist(0)
+ AssertEqual [1000001], ale#sign#FindCurrentSigns(bufnr('%'))
+ AssertEqual
+ \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}],
+ \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}')
+ AssertEqual g:expected_groups, ParseAuGroups()
diff --git a/test/test_filetype_mapping.vader b/test/test_filetype_mapping.vader
new file mode 100644
index 00000000..2d72491d
--- /dev/null
+++ b/test/test_filetype_mapping.vader
@@ -0,0 +1,29 @@
+Before:
+ augroup TestFiletypeGroup
+ autocmd!
+ autocmd BufEnter,BufRead *.x setf xfiletype
+ autocmd BufEnter,BufRead *.y set filetype=yfiletype
+ autocmd BufEnter,BufRead *.z setlocal filetype=zfiletype
+ autocmd BufEnter,BufRead *.jsx set filetype=javascript.jsx
+ augroup END
+
+After:
+ unlet! g:map
+ augroup TestFiletypeGroup
+ autocmd!
+ augroup END
+ augroup! TestFiletypeGroup
+
+Execute(ALE should parse autocmd filetypes correctly):
+ let g:map = ale#filetypes#LoadExtensionMap()
+
+ AssertEqual '.x', g:map['xfiletype']
+ AssertEqual '.y', g:map['yfiletype']
+ AssertEqual '.z', g:map['zfiletype']
+ AssertEqual '.jsx', g:map['javascript.jsx']
+
+Execute(ALE should guess file extensions appropriately):
+ " The whole string should be used, if there's a match.
+ AssertEqual '.jsx', ale#filetypes#GuessExtension('javascript.jsx')
+ " The first part should be used.
+ AssertEqual '.x', ale#filetypes#GuessExtension('xfiletype.yfiletype')
diff --git a/test/test_fortran_handler.vader b/test/test_fortran_handler.vader
new file mode 100644
index 00000000..7648ff66
--- /dev/null
+++ b/test/test_fortran_handler.vader
@@ -0,0 +1,118 @@
+Execute(The fortran handler should parse lines from GCC 4.1.2 correctly):
+ runtime ale_linters/fortran/gcc.vim
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'bufnr': 357,
+ \ 'lnum': 4,
+ \ 'vcol': 0,
+ \ 'col': 0,
+ \ 'text': "Symbol ‘b’ at (1) has no IMPLICIT type",
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ },
+ \ {
+ \ 'bufnr': 357,
+ \ 'lnum': 3,
+ \ 'vcol': 0,
+ \ 'col': 0,
+ \ 'text': "Symbol ‘a’ at (1) has no IMPLICIT type",
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ },
+ \ ],
+ \ ale_linters#fortran#gcc#Handle(357, [
+ \ " In file :4",
+ \ "",
+ \ "write(*,*) b",
+ \ " 1",
+ \ "Error: Symbol ‘b’ at (1) has no IMPLICIT type",
+ \ " In file :3",
+ \ "",
+ \ "write(*,*) a",
+ \ " 1",
+ \ "Error: Symbol ‘a’ at (1) has no IMPLICIT type",
+ \ ])
+
+After:
+ call ale#linter#Reset()
+
+
+Execute(The fortran handler should parse lines from GCC 4.9.3 correctly):
+ runtime ale_linters/fortran/gcc.vim
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'bufnr': 357,
+ \ 'lnum': 3,
+ \ 'vcol': 0,
+ \ 'col': 12,
+ \ 'text': "Symbol ‘a’ at (1) has no IMPLICIT type",
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ },
+ \ {
+ \ 'bufnr': 357,
+ \ 'lnum': 4,
+ \ 'vcol': 0,
+ \ 'col': 12,
+ \ 'text': "Symbol ‘b’ at (1) has no IMPLICIT type",
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ },
+ \ ],
+ \ ale_linters#fortran#gcc#Handle(357, [
+ \ ":3.12:",
+ \ "",
+ \ "write(*,*) a",
+ \ " 1",
+ \ "Error: Symbol ‘a’ at (1) has no IMPLICIT type",
+ \ ":4.12:",
+ \ "",
+ \ "write(*,*) b",
+ \ " 1",
+ \ "Error: Symbol ‘b’ at (1) has no IMPLICIT type",
+ \ ])
+
+After:
+ call ale#linter#Reset()
+
+
+
+Execute(The fortran handler should parse lines from GCC 6.3.1 correctly):
+ runtime ale_linters/fortran/gcc.vim
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'bufnr': 337,
+ \ 'lnum': 3,
+ \ 'vcol': 0,
+ \ 'col': 12,
+ \ 'text': "Symbol ‘a’ at (1) has no IMPLICIT type",
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ },
+ \ {
+ \ 'bufnr': 337,
+ \ 'lnum': 4,
+ \ 'vcol': 0,
+ \ 'col': 12,
+ \ 'text': "Symbol ‘b’ at (1) has no IMPLICIT type",
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ },
+ \ ],
+ \ ale_linters#fortran#gcc#Handle(337, [
+ \ "<stdin>:3:12:",
+ \ "",
+ \ "Error: Symbol ‘a’ at (1) has no IMPLICIT type",
+ \ "<stdin>:4:12:",
+ \ "",
+ \ "Error: Symbol ‘b’ at (1) has no IMPLICIT type",
+ \ ])
+
+After:
+ call ale#linter#Reset()
diff --git a/test/test_highlight_clearing.vader b/test/test_highlight_clearing.vader
new file mode 100644
index 00000000..bf805924
--- /dev/null
+++ b/test/test_highlight_clearing.vader
@@ -0,0 +1,4 @@
+Execute(ALE should be able to queue highlights and clear them for some other buffer):
+ " We'll just make sure that this doesn't blow up.
+ call ale#highlight#SetHighlights(bufnr('%') + 1, [])
+ call ale#highlight#UnqueueHighlights(bufnr('%') + 1)
diff --git a/test/test_highlight_placement.vader b/test/test_highlight_placement.vader
new file mode 100644
index 00000000..b3e40c18
--- /dev/null
+++ b/test/test_highlight_placement.vader
@@ -0,0 +1,61 @@
+Before:
+ function! GenerateResults(buffer, output)
+ return [
+ \ {
+ \ 'lnum': 1,
+ \ 'col': 1,
+ \ 'bufnr': bufnr('%'),
+ \ 'vcol': 0,
+ \ 'nr': -1,
+ \ 'type': 'E',
+ \ 'text': 'foo',
+ \ },
+ \ {
+ \ 'lnum': 2,
+ \ 'col': 1,
+ \ 'bufnr': bufnr('%'),
+ \ 'vcol': 0,
+ \ 'nr': -1,
+ \ 'type': 'W',
+ \ 'text': 'bar',
+ \ },
+ \ {
+ \ 'lnum': 3,
+ \ 'col': 5,
+ \ 'bufnr': bufnr('%'),
+ \ 'vcol': 0,
+ \ 'nr': -1,
+ \ 'type': 'E',
+ \ 'text': 'wat',
+ \ },
+ \]
+ endfunction
+
+ call ale#linter#Define('testft', {
+ \ 'name': 'x',
+ \ 'executable': 'echo',
+ \ 'command': 'echo',
+ \ 'callback': 'GenerateResults',
+ \})
+
+After:
+ delfunction GenerateResults
+ call ale#linter#Reset()
+ let g:ale_buffer_info = {}
+
+Given testft(A Javscript file with warnings/errors):
+ foo
+ bar
+ baz wat
+
+Execute(Highlights should be set when a linter runs):
+ call ale#Lint()
+ call ale#engine#WaitForJobs(2000)
+
+ AssertEqual
+ \ [
+ \ {'group': 'ALEError', 'id': 4, 'priority': 10, 'pos1': [1, 1, 1]},
+ \ {'group': 'ALEWarning', 'id': 5, 'priority': 10, 'pos1': [2, 1, 1]},
+ \ {'group': 'ALEError', 'id': 6, 'priority': 10, 'pos1': [3, 5, 1]}
+ \ ],
+ \ getmatches()
diff --git a/test/test_history_saving.vader b/test/test_history_saving.vader
new file mode 100644
index 00000000..a06f9a78
--- /dev/null
+++ b/test/test_history_saving.vader
@@ -0,0 +1,69 @@
+Before:
+ let g:history = []
+ let g:ale_buffer_info = {}
+ let g:ale_max_buffer_history_size = 20
+
+ function! CollectResults(buffer, output)
+ return []
+ endfunction
+
+ call ale#linter#Define('foobar', {
+ \ 'name': 'testlinter',
+ \ 'callback': 'CollectResults',
+ \ 'executable': 'echo',
+ \ 'command': 'echo command history test',
+ \ 'read_buffer': 0,
+ \})
+
+After:
+ unlet g:history
+ let g:ale_buffer_info = {}
+ let g:ale_max_buffer_history_size = 20
+ call ale#linter#Reset()
+ delfunction CollectResults
+
+Given foobar (Some imaginary filetype):
+ anything
+
+Execute(History should be set when commands are run):
+ AssertEqual 'foobar', &filetype
+
+ call ale#Lint()
+ call ale#engine#WaitForJobs(2000)
+
+ let g:history = g:ale_buffer_info[bufnr('%')].history
+
+ AssertEqual 1, len(g:history)
+ AssertEqual ['status', 'job_id', 'command'], keys(g:history[0])
+ AssertEqual ['/bin/bash', '-c', 'echo command history test'], g:history[0].command
+ AssertEqual 'ran', g:history[0].status
+ " The Job ID will change each time, but we can check the type.
+ AssertEqual type(1), type(g:history[0].job_id)
+
+Execute(History items should be popped after going over the max):
+ let g:ale_buffer_info[1] = {
+ \ 'history': map(range(20), '{''status'': ''ran'', ''job_id'': v:val, ''command'': ''foobar''}'),
+ \}
+
+ call ale#engine#AddToHistory(1, 'ran', 347, 'last command')
+
+ AssertEqual
+ \ (
+ \ map(range(1, 19), '{''status'': ''ran'', ''job_id'': v:val, ''command'': ''foobar''}')
+ \ + [{'status': 'ran', 'job_id': 347, 'command': 'last command'}]
+ \ ),
+ \ g:ale_buffer_info[1].history
+
+Execute(Nothing should be added to history if the size is too low):
+ let g:ale_max_buffer_history_size = 0
+ let g:ale_buffer_info[1] = {'history': []}
+
+ call ale#engine#AddToHistory(1, 'ran', 347, 'last command')
+
+ AssertEqual [], g:ale_buffer_info[1].history
+
+ let g:ale_max_buffer_history_size = -2
+
+ call ale#engine#AddToHistory(1, 'ran', 347, 'last command')
+
+ AssertEqual [], g:ale_buffer_info[1].history
diff --git a/test/test_sandbox_execution.vader b/test/test_sandbox_execution.vader
new file mode 100644
index 00000000..a4cd84d6
--- /dev/null
+++ b/test/test_sandbox_execution.vader
@@ -0,0 +1,51 @@
+Before:
+ function! TestCallback(buffer, output)
+ return [
+ \ {
+ \ 'lnum': 1,
+ \ 'bufnr': 1,
+ \ 'vcol': 0,
+ \ 'linter_name': 'testlinter',
+ \ 'nr': -1,
+ \ 'type': 'E',
+ \ 'col': 1,
+ \ 'text': 'Test Error',
+ \ },
+ \]
+ endfunction
+
+ call ale#linter#Define('foobar', {
+ \ 'name': 'testlinter',
+ \ 'callback': 'TestCallback',
+ \ 'executable': 'echo',
+ \ 'command': 'echo',
+ \})
+
+ let g:ale_buffer_info = {}
+
+After:
+ delfunction TestCallback
+ call ale#linter#Reset()
+ let g:ale_buffer_info = {}
+
+Given foobar (Some imaginary filetype):
+ foo
+ bar
+ baz
+
+Execute(ALE shouldn't blow up when run from a sandbox):
+ AssertEqual 'foobar', &filetype
+
+ sandbox call ale#Queue(0)
+ sandbox call ale#Queue(1)
+ sandbox call ale#Lint()
+
+Execute(ALE shouldn't blow up if file cleanup happens in a sandbox):
+ let g:ale_buffer_info[3] = {
+ \ 'temporary_file_list': ['/tmp/foo'],
+ \ 'temporary_directory_list': ['/tmp/bar'],
+ \}
+ sandbox call ale#engine#RemoveManagedFiles(3)
+
+ AssertEqual ['/tmp/foo'], g:ale_buffer_info[3].temporary_file_list
+ AssertEqual ['/tmp/bar'], g:ale_buffer_info[3].temporary_directory_list
diff --git a/test/test_statusline_api_without_globals.vader b/test/test_statusline_api_without_globals.vader
new file mode 100644
index 00000000..29677f3b
--- /dev/null
+++ b/test/test_statusline_api_without_globals.vader
@@ -0,0 +1,19 @@
+" This file tests that statusline functions return meaningful output even
+" when most of ALE itself has not been loaded.
+"
+" This is important for plugins which integrate with ALE like airline.
+
+Before:
+ unlet! g:ale_buffer_info
+
+After:
+ let g:ale_buffer_info = {}
+
+Execute(ale#statusline#Update shouldn't blow up when globals are undefined):
+ call ale#statusline#Update(1, [])
+
+Execute(ale#statusline#Count should return 0 counts when globals are undefined):
+ AssertEqual [0, 0], ale#statusline#Count(1)
+
+Execute(ale#statusline#Status should return 'OK' when globals are undefined):
+ AssertEqual 'OK', ale#statusline#Status()