diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim index 737a90d..1ca7736 100644 --- a/autoload/ale/debugging.vim +++ b/autoload/ale/debugging.vim @@ -68,7 +68,14 @@ function! s:EchoCommandHistory() abort endif for l:item in g:ale_buffer_info[l:buffer].history - echom '(' . l:item.status . ') ' . string(l:item.command) + let l:status_message = l:item.status + + " Include the exit code in output if we have it. + if l:item.status ==# 'finished' + let l:status_message .= ' - exit code ' . l:item.exit_code + endif + + echom '(' . l:status_message . ') ' . string(l:item.command) endfor endfunction diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index d072d45..a6b9df3 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -270,7 +270,23 @@ function! ale#engine#SetResults(buffer, loclist) abort endif endfunction -function! s:HandleExitNeoVim(job, data, event) abort +function! s:SetExitCode(job, exit_code) abort + let l:job_id = s:GetJobID(a:job) + + if !has_key(s:job_info_map, l:job_id) + return + endif + + let l:buffer = s:job_info_map[l:job_id].buffer + + call ale#history#SetExitCode(l:buffer, l:job_id, a:exit_code) +endfunction + +function! s:HandleExitNeoVim(job, exit_code, event) abort + if g:ale_history_enabled + call s:SetExitCode(a:job, a:exit_code) + endif + call s:HandleExit(a:job) endfunction @@ -278,6 +294,12 @@ function! s:HandleExitVim(channel) abort call s:HandleExit(ch_getjob(a:channel)) endfunction +" Vim returns the exit status with one callback, +" and the channel will close later in another callback. +function! s:HandleExitStatusVim(job, exit_code) abort + call s:SetExitCode(a:job, a:exit_code) +endfunction + function! s:FixLocList(buffer, loclist) abort " Some errors have line numbers beyond the end of the file, " so we need to adjust them so they set the error at the last line @@ -415,6 +437,12 @@ function! s:RunJob(options) abort \ 'close_cb': function('s:HandleExitVim'), \} + if g:ale_history_enabled + " We only need to capture the exit status if we are going to + " save it in the history. Otherwise, we don't care. + let l:job_options.exit_cb = function('s:HandleExitStatusVim') + endif + if l:output_stream ==# 'stderr' " Read from stderr instead of stdout. let l:job_options.err_cb = function('s:GatherOutputVim') diff --git a/autoload/ale/history.vim b/autoload/ale/history.vim index 6524b91..f52c1d7 100644 --- a/autoload/ale/history.vim +++ b/autoload/ale/history.vim @@ -24,3 +24,18 @@ function! ale#history#Add(buffer, status, job_id, command) abort let g:ale_buffer_info[a:buffer].history = l:history endfunction + +" Set an exit code for a command which finished. +function! ale#history#SetExitCode(buffer, job_id, exit_code) abort + " Search backwards to find a matching job ID. IDs might be recycled, + " so finding the last one should be good enough. + for l:obj in reverse(g:ale_buffer_info[a:buffer].history[:]) + if l:obj.job_id == a:job_id + " If we find a match, then set the code and status, and stop here. + let l:obj.exit_code = a:exit_code + let l:obj.status = 'finished' + + return + endif + endfor +endfunction diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader index c8c2067..92025f2 100644 --- a/test/test_ale_info.vader +++ b/test/test_ale_info.vader @@ -191,3 +191,30 @@ Execute (ALEInfo should return command history): \ '(started) [''/bin/bash'', ''\c'', ''last command'']', \ ], "\n"), \ g:output + +Given testft.testft2 (Empty buffer with two filetypes): +Execute (ALEInfo command history should print exit codes correctly): + let g:ale_buffer_info[bufnr('%')] = { + \ 'history': [ + \ {'status': 'finished', 'exit_code': 0, 'job_id': 347, 'command': 'first command'}, + \ {'status': 'finished', 'exit_code': 1, '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, + \ '(finished - exit code 0) ''first command''', + \ '(finished - exit code 1) [''/bin/bash'', ''\c'', ''last command'']', + \ ], "\n"), + \ g:output diff --git a/test/test_history_saving.vader b/test/test_history_saving.vader index b3afdca..23b9170 100644 --- a/test/test_history_saving.vader +++ b/test/test_history_saving.vader @@ -35,9 +35,10 @@ Execute(History should be set when commands are run): let g:history = g:ale_buffer_info[bufnr('%')].history AssertEqual 1, len(g:history) - AssertEqual ['status', 'job_id', 'command'], keys(g:history[0]) + AssertEqual sort(['status', 'exit_code', 'job_id', 'command']), sort(keys(g:history[0])) AssertEqual ['/bin/bash', '-c', 'echo command history test'], g:history[0].command - AssertEqual 'started', g:history[0].status + AssertEqual 'finished', g:history[0].status + AssertEqual 0, g:history[0].exit_code " The Job ID will change each time, but we can check the type. AssertEqual type(1), type(g:history[0].job_id)