summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2017-02-11 15:16:08 +0000
committerw0rp <devw0rp@gmail.com>2017-02-11 15:16:08 +0000
commit88192e8662585f809bd248c1d689638ab007ac7b (patch)
tree3fc73c99e4d8c57ec3a5bbf2797464a95ea28f14
parent8ad85858b87cb7d93ae1d93e6721e034c02275fd (diff)
downloadale-88192e8662585f809bd248c1d689638ab007ac7b.zip
Add support for managing temporary files/directories
-rw-r--r--autoload/ale/cleanup.vim2
-rw-r--r--autoload/ale/engine.vim46
-rw-r--r--doc/ale.txt35
-rw-r--r--test/test_cleanup.vader6
-rw-r--r--test/test_temporary_file_management.vader83
5 files changed, 169 insertions, 3 deletions
diff --git a/autoload/ale/cleanup.vim b/autoload/ale/cleanup.vim
index 0a1cd391..d720cc96 100644
--- a/autoload/ale/cleanup.vim
+++ b/autoload/ale/cleanup.vim
@@ -3,6 +3,8 @@
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.
for l:job in get(g:ale_buffer_info[a:buffer], 'job_list', [])
call ale#engine#ClearJob(l:job)
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 70cd0527..22e8ff93 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -30,10 +30,14 @@ function! ale#engine#InitBufferInfo(buffer) abort
" job_list will hold the list of jobs
" loclist holds the loclist items after all jobs have completed.
" 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
let g:ale_buffer_info[a:buffer] = {
\ 'job_list': [],
\ 'loclist': [],
\ 'new_loclist': [],
+ \ 'temporary_file_list': [],
+ \ 'temporary_directory_list': [],
\}
endif
endfunction
@@ -134,6 +138,40 @@ function! ale#engine#JoinNeovimOutput(output, data) abort
endif
endfunction
+" Register a temporary file to be managed with the ALE engine for
+" a current job run.
+function! ale#engine#ManageFile(buffer, filename) abort
+ call add(g:ale_buffer_info[a:buffer].temporary_file_list, a:filename)
+endfunction
+
+" Same as the above, but manage an entire directory.
+function! ale#engine#ManageDirectory(buffer, directory) abort
+ call add(g:ale_buffer_info[a:buffer].temporary_directory_list, a:directory)
+endfunction
+
+function! ale#engine#RemoveManagedFiles(buffer) abort
+ if !has_key(g:ale_buffer_info, a:buffer)
+ 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)
+ endfor
+
+ let g:ale_buffer_info[a:buffer].temporary_file_list = []
+
+ " Delete directories like `rm -rf`.
+ " Directories are handled differently from files, so paths that are
+ " intended to be single files can be set up for automatic deletion without
+ " accidentally deleting entire directories.
+ for l:directory in g:ale_buffer_info[a:buffer].temporary_directory_list
+ call delete(l:directory, 'rf')
+ endfor
+
+ let g:ale_buffer_info[a:buffer].temporary_directory_list = []
+endfunction
+
function! s:HandleExit(job) abort
if a:job ==# 'no process'
" Stop right away when the job is not valid in Vim 8.
@@ -178,6 +216,10 @@ function! s:HandleExit(job) abort
return
endif
+ " Automatically remove all managed temporary files and directories
+ " now that all jobs have completed.
+ call ale#engine#RemoveManagedFiles(l:buffer)
+
" Sort the loclist again.
" We need a sorted list so we can run a binary search against it
" for efficient lookup of the messages in the cursor handler.
@@ -424,6 +466,10 @@ function! s:InvokeChain(buffer, linter, chain_index, input) abort
if !empty(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
diff --git a/doc/ale.txt b/doc/ale.txt
index 3ec54d4f..d8f8a4c0 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -1037,6 +1037,35 @@ ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()*
|setqflist()|.
+ale#engine#ManageFile(buffer, filename) *ale#engine#ManageFile()*
+
+ Given a buffer number for a buffer currently running some linting tasks
+ and a filename, register a filename with ALE for automatic deletion after
+ linting is complete, or when Vim exits.
+
+ If Vim exits suddenly, ALE will try its best to remove temporary files, but
+ ALE cannot guarantee with absolute certainty that the files will be removed.
+ It is advised to create temporary files in the operating system's managed
+ temporary file directory, such as with |tempname()|.
+
+ Directory names should not be given to this function. ALE will only delete
+ files and symlinks given to this function. This is to prevent entire
+ directories from being accidentally deleted, say in cases of writing
+ `dir . '/' . filename` where `filename` is actually `''`, etc. ALE instead
+ manages directories separetly with the |ale#engine#ManageDirectory| function.
+
+
+ale#engine#ManageDirectory(buffer, directory) *ale#engine#ManageDirectory()*
+
+ Like |ale#engine#ManageFile()|, but directories and all of their contents
+ will be deleted, akin to `rm -rf directory`, which could lead to loss of
+ data if mistakes are made. This command will also delete any temporary
+ filenames given to it.
+
+ It is advised to use |ale#engine#ManageFile()| instead for deleting single
+ files.
+
+
ale#linter#Define(filetype, linter) *ale#linter#Define()*
Given a |String| for a filetype and a |Dictionary| Describing a linter
configuration, add a linter for the given filetype. The dictionaries each
@@ -1151,6 +1180,12 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
`command_chain` is recommended where any system calls need to be made to
retrieve some kind of information before running the final command.
+ If temporary files or directories are created for commands run with
+ `command_callback` or `command_chain`, then these tempoary files or
+ directories can be managed by ALE, for automatic deletion.
+ See |ale#engine#ManageFile()| and |ale#engine#ManageDirectory| for more
+ information.
+
Some programs for checking for errors are not capable of receiving input
from stdin, as is required by ALE. To remedy this, a wrapper script is
provided named in the variable |g:ale#util#stdin_wrapper|. This variable
diff --git a/test/test_cleanup.vader b/test/test_cleanup.vader
index ec2b38aa..23e5bcf0 100644
--- a/test/test_cleanup.vader
+++ b/test/test_cleanup.vader
@@ -2,8 +2,8 @@ Before:
let g:buffer = bufnr('%')
let g:ale_buffer_info = {
- \ g:buffer : {},
- \ 10347: {},
+ \ g:buffer : {'temporary_file_list': [], 'temporary_directory_list': []},
+ \ 10347: {'temporary_file_list': [], 'temporary_directory_list': []},
\}
After:
@@ -12,4 +12,4 @@ After:
Execute('ALE globals should be cleared when the buffer is closed.'):
:q!
- AssertEqual {10347: {}}, g:ale_buffer_info
+ AssertEqual {10347: {'temporary_file_list': [], 'temporary_directory_list': []}}, g:ale_buffer_info
diff --git a/test/test_temporary_file_management.vader b/test/test_temporary_file_management.vader
new file mode 100644
index 00000000..17a375e1
--- /dev/null
+++ b/test/test_temporary_file_management.vader
@@ -0,0 +1,83 @@
+Before:
+ let g:command = 'echo test'
+ let g:filename = tempname()
+ let g:directory = tempname()
+ let g:preserved_directory = tempname()
+
+ function! TestCommandCallback(buffer) abort
+ " We are registering a temporary file, so we should delete it.
+ call writefile(['foo'], g:filename)
+ call ale#engine#ManageFile(a:buffer, g:filename)
+
+ " We are registering this directory appropriately, so we should delete
+ " the whole thing.
+ call mkdir(g:directory)
+ call writefile(['foo'], g:directory . '/bar')
+ call ale#engine#ManageDirectory(a:buffer, g:directory)
+
+ " We are registering this directory as temporary file, so we
+ " shouldn't delete it.
+ call mkdir(g:preserved_directory)
+ call writefile(['foo'], g:preserved_directory . '/bar')
+ call ale#engine#ManageFile(a:buffer, g:preserved_directory)
+
+ return g:command
+ endfunction
+
+ function! TestCallback(buffer, output) abort
+ return []
+ endfunction
+
+ call ale#linter#Define('foobar', {
+ \ 'name': 'testlinter',
+ \ 'executable': 'echo',
+ \ 'callback': 'TestCallback',
+ \ 'command_callback': 'TestCommandCallback',
+ \})
+
+After:
+ call delete(g:preserved_directory, 'rf')
+
+ unlet! g:command
+ unlet! g:filename
+ unlet! g:directory
+ unlet! g:preserved_directory
+ delfunction TestCommandCallback
+ delfunction TestCallback
+ call ale#linter#Reset()
+
+Given foobar (Some imaginary filetype):
+ foo
+ bar
+ baz
+
+Execute(ALE should delete managed files/directories appropriately after linting):
+ AssertEqual 'foobar', &filetype
+
+ call ale#Lint()
+ call ale#engine#WaitForJobs(2000)
+
+ Assert !filereadable(g:filename), 'The tempoary file was not deleted'
+ Assert !isdirectory(g:directory), 'The tempoary directory was not deleted'
+ Assert isdirectory(g:preserved_directory), 'The tempoary directory was not kept'
+
+Execute(ALE should delete managed files even if no command is run):
+ AssertEqual 'foobar', &filetype
+
+ let g:command = ''
+
+ call ale#Lint()
+ call ale#engine#WaitForJobs(2000)
+
+ Assert !filereadable(g:filename), 'The tempoary file was not deleted'
+ Assert !isdirectory(g:directory), 'The tempoary directory was not deleted'
+ Assert isdirectory(g:preserved_directory), 'The tempoary directory was not kept'
+
+Execute(ALE should delete managed files when the buffer is removed):
+ call ale#engine#InitBufferInfo(bufnr('%'))
+ call TestCommandCallback(bufnr('%'))
+ call ale#cleanup#Buffer(bufnr('%'))
+
+ Assert !filereadable(g:filename), 'The tempoary file was not deleted'
+ Assert !isdirectory(g:directory), 'The tempoary directory was not deleted'
+ Assert isdirectory(g:preserved_directory), 'The tempoary directory was not kept'