From 84af543907e222dd6035a37f7a7b9a99b9fd11ad Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 30 Jul 2017 14:42:00 +0100 Subject: [PATCH 1/9] Fix some bugs so the PHP language server will show errors at least once --- ale_linters/php/langserver.vim | 2 +- autoload/ale/lsp.vim | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ale_linters/php/langserver.vim b/ale_linters/php/langserver.vim index 8dad5ac795..29545542b6 100644 --- a/ale_linters/php/langserver.vim +++ b/ale_linters/php/langserver.vim @@ -25,7 +25,7 @@ endfunction function! ale_linters#php#langserver#GetProjectRoot(buffer) abort let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git') - return !empty(l:git_path) ? fnamemodify(l:git_path, ':h') : '' + return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : '' endfunction call ale#linter#Define('php', { diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index 2c9b2990b4..eb4073141b 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -164,6 +164,8 @@ function! s:HandleInitializeResponse(conn, response) abort return endif + let l:project.initialized = 1 + " After the server starts, send messages we had queued previously. for l:message_data in l:project.message_queue call s:SendMessageData(a:conn, l:message_data) @@ -211,7 +213,7 @@ function! s:RegisterProject(conn, project_root) abort " Tools without project roots are ready right away, like tsserver. let a:conn.projects[a:project_root] = { \ 'initialized': empty(a:project_root), - \ 'init_messsage_id': 0, + \ 'init_request_id': 0, \ 'message_queue': [], \} endif @@ -319,7 +321,7 @@ function! ale#lsp#Send(conn_id, message, ...) abort " Only send the init message once. if !l:project.init_request_id let [l:init_id, l:init_data] = ale#lsp#CreateMessageData( - \ ale#lsp#message#Initialize(l:conn.project_root), + \ ale#lsp#message#Initialize(l:project_root), \) let l:project.init_request_id = l:init_id From 5810d7faa0fe1fe8ae91c6bceb7dde518646a387 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 30 Jul 2017 22:17:29 +0100 Subject: [PATCH 2/9] Add some error message handling for LSP, for test purposes --- autoload/ale/engine.vim | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index d66126ab74..455772f387 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -225,10 +225,21 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort call s:HandleLoclist('tsserver', l:buffer, l:loclist) endfunction +function! s:HandleLSPErrorMessage(error_message) abort + echoerr 'Error from LSP:' + + for l:line in split(a:error_message, "\n") + echoerr l:line + endfor +endfunction + function! ale#engine#HandleLSPResponse(response) abort let l:method = get(a:response, 'method', '') - if l:method ==# 'textDocument/publishDiagnostics' + if get(a:response, 'jsonrpc', '') ==# '2.0' && has_key(a:response, 'error') + " Uncomment this line to print LSP error messages. + " call s:HandleLSPErrorMessage(a:response.error.message) + elseif l:method ==# 'textDocument/publishDiagnostics' call s:HandleLSPDiagnostics(a:response) elseif get(a:response, 'type', '') ==# 'event' \&& get(a:response, 'event', '') ==# 'semanticDiag' From 6000d956f0a3e217c3a0563abef738616feb73e6 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 30 Jul 2017 22:18:19 +0100 Subject: [PATCH 3/9] When servers never send an initialize response, but instead just publish diagnostics straight away, handle that as an initialize response --- autoload/ale/lsp.vim | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index eb4073141b..855abb1146 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -156,23 +156,49 @@ function! s:FindProjectWithInitRequestID(conn, init_request_id) abort return {} endfunction +function! s:MarkProjectAsInitialized(conn, project) abort + let a:project.initialized = 1 + + " After the server starts, send messages we had queued previously. + for l:message_data in a:project.message_queue + call s:SendMessageData(a:conn, l:message_data) + endfor + + " Remove the messages now. + let a:conn.message_queue = [] +endfunction + function! s:HandleInitializeResponse(conn, response) abort let l:request_id = a:response.request_id let l:project = s:FindProjectWithInitRequestID(a:conn, l:request_id) - if empty(l:project) - return + if !empty(l:project) + call s:MarkProjectAsInitialized(a:conn, l:project) endif +endfunction - let l:project.initialized = 1 +function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort + let l:uninitialized_projects = [] - " After the server starts, send messages we had queued previously. - for l:message_data in l:project.message_queue - call s:SendMessageData(a:conn, l:message_data) + for [l:key, l:value] in items(a:conn.projects) + if l:value.initialized == 0 + call add(l:uninitialized_projects, [l:key, l:value]) + endif endfor - " Remove the messages now. - let a:conn.message_queue = [] + if empty(l:uninitialized_projects) + return + endif + + if get(a:response, 'method', '') ==# 'textDocument/publishDiagnostics' + let l:filename = ale#path#FromURI(a:response.params.uri) + + for [l:dir, l:project] in l:uninitialized_projects + if l:filename[:len(l:dir) - 1] ==# l:dir + call s:MarkProjectAsInitialized(a:conn, l:project) + endif + endfor + endif endfunction function! ale#lsp#HandleMessage(conn, message) abort @@ -186,6 +212,8 @@ function! ale#lsp#HandleMessage(conn, message) abort if get(l:response, 'method', '') ==# 'initialize' call s:HandleInitializeResponse(a:conn, l:response) else + call ale#lsp#HandleOtherInitializeResponses(a:conn, l:response) + " Call all of the registered handlers with the response. for l:Callback in a:conn.callback_list call ale#util#GetFunction(l:Callback)(l:response) From 2c252c0f12868c5bbe9a00bd8bc37b32ae3802c4 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 30 Jul 2017 23:34:58 +0100 Subject: [PATCH 4/9] #517 - Get the Rust language server working in a basic way --- ale_linters/rust/langserver.vim | 33 +++++++++++++++++++++++++++++++++ autoload/ale/linter.vim | 6 ++++++ autoload/ale/lsp.vim | 10 ++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 ale_linters/rust/langserver.vim diff --git a/ale_linters/rust/langserver.vim b/ale_linters/rust/langserver.vim new file mode 100644 index 0000000000..5e42a97f7b --- /dev/null +++ b/ale_linters/rust/langserver.vim @@ -0,0 +1,33 @@ +" Author: w0rp +" Description: A language server for Rust + +call ale#Set('rust_langserver_executable', 'rls') + +function! ale_linters#rust#langserver#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'rust_langserver_executable') +endfunction + +function! ale_linters#rust#langserver#GetCommand(buffer) abort + let l:executable = ale_linters#rust#langserver#GetExecutable(a:buffer) + + return ale#Escape(l:executable) . ' +nightly' +endfunction + +function! ale_linters#rust#langserver#GetLanguage(buffer) abort + return 'rust' +endfunction + +function! ale_linters#rust#langserver#GetProjectRoot(buffer) abort + let l:cargo_file = ale#path#FindNearestFile(a:buffer, 'Cargo.toml') + + return !empty(l:cargo_file) ? fnamemodify(l:cargo_file, ':h') : '' +endfunction + +call ale#linter#Define('rust', { +\ 'name': 'langserver', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale_linters#rust#langserver#GetExecutable', +\ 'command_callback': 'ale_linters#rust#langserver#GetCommand', +\ 'language_callback': 'ale_linters#rust#langserver#GetLanguage', +\ 'project_root_callback': 'ale_linters#rust#langserver#GetProjectRoot', +\}) diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 0af42af99f..8fc685101a 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -377,6 +377,12 @@ function! ale#linter#StartLSP(buffer, linter, callback) abort let l:address = '' let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer) + if empty(l:root) && a:linter.lsp !=# 'tsserver' + " If there's no project root, then we can't check files with LSP, + " unless we are using tsserver, which doesn't use project roots. + return {} + endif + if a:linter.lsp ==# 'socket' let l:address = ale#linter#GetAddress(a:buffer, a:linter) let l:conn_id = ale#lsp#ConnectToAddress( diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index 855abb1146..eb66eb475f 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -190,7 +190,13 @@ function! ale#lsp#HandleOtherInitializeResponses(conn, response) abort return endif - if get(a:response, 'method', '') ==# 'textDocument/publishDiagnostics' + if get(a:response, 'method', '') ==# '' + if has_key(get(a:response, 'result', {}), 'capabilities') + for [l:dir, l:project] in l:uninitialized_projects + call s:MarkProjectAsInitialized(a:conn, l:project) + endfor + endif + elseif get(a:response, 'method', '') ==# 'textDocument/publishDiagnostics' let l:filename = ale#path#FromURI(a:response.params.uri) for [l:dir, l:project] in l:uninitialized_projects @@ -237,7 +243,7 @@ function! s:HandleCommandMessage(job_id, message) abort endfunction function! s:RegisterProject(conn, project_root) abort - if !has_key(a:conn, a:project_root) + if !has_key(a:conn.projects, a:project_root) " Tools without project roots are ready right away, like tsserver. let a:conn.projects[a:project_root] = { \ 'initialized': empty(a:project_root), From 79d4935ccf95166838dff4feaf78467bc5f86096 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 30 Jul 2017 23:53:46 +0100 Subject: [PATCH 5/9] Cover special LSP initialize response handling with Vader tests --- ...st_other_initialize_message_handling.vader | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 test/lsp/test_other_initialize_message_handling.vader diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader new file mode 100644 index 0000000000..3a7c7f627c --- /dev/null +++ b/test/lsp/test_other_initialize_message_handling.vader @@ -0,0 +1,66 @@ +Before: + let b:project = { + \ 'initialized': 0, + \ 'init_request_id': 3, + \ 'message_queue': [], + \} + + let b:conn = { + \ 'projects': { + \ '/foo/bar': b:project, + \ }, + \} + +After: + unlet! b:project + unlet! b:conn + +Execute(publishDiagnostics messages with files inside project directories should initialize projects): + " This is for some other file, ignore this one. + call ale#lsp#HandleOtherInitializeResponses(b:conn, { + \ 'method': 'textDocument/publishDiagnostics', + \ 'params': {'uri': 'file:///xyz/bar/baz.txt'}, + \}) + + AssertEqual + \ { + \ 'initialized': 0, + \ 'init_request_id': 3, + \ 'message_queue': [], + \ }, + \ b:project + + call ale#lsp#HandleOtherInitializeResponses(b:conn, { + \ 'method': 'textDocument/publishDiagnostics', + \ 'params': {'uri': 'file:///foo/bar/baz.txt'}, + \}) + + AssertEqual + \ { + \ 'initialized': 1, + \ 'init_request_id': 3, + \ 'message_queue': [], + \ }, + \ b:project + +Execute(Messages with no method and capabilities should initialize projects): + call ale#lsp#HandleOtherInitializeResponses(b:conn, { + \ 'result': {'capabilities': {}}, + \}) + + AssertEqual + \ { + \ 'initialized': 1, + \ 'init_request_id': 3, + \ 'message_queue': [], + \ }, + \ b:project + +Execute(Other messages should not initialize projects): + call ale#lsp#HandleOtherInitializeResponses(b:conn, {'method': 'lolwat'}) + + AssertEqual 0, b:project.initialized + + call ale#lsp#HandleOtherInitializeResponses(b:conn, {'result': {'x': {}}}) + + AssertEqual 0, b:project.initialized From eaeb71993f259c5014e22291f6cf08e623c1047b Mon Sep 17 00:00:00 2001 From: Mahmoud Mostafa Date: Mon, 31 Jul 2017 02:51:08 +0200 Subject: [PATCH 6/9] Add stylelint fixer --- autoload/ale/fix/registry.vim | 5 +++ autoload/ale/fixers/stylelint.vim | 31 ++++++++++++++++++ .../node_modules/stylelint/bin/stylelint.js | 0 .../react-app/subdir/testfile.css | 0 .../test_stylelint_fixer_callback.vader | 32 +++++++++++++++++++ 5 files changed, 68 insertions(+) create mode 100644 autoload/ale/fixers/stylelint.vim create mode 100644 test/eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js create mode 100644 test/eslint-test-files/react-app/subdir/testfile.css create mode 100644 test/fixers/test_stylelint_fixer_callback.vader diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 6568a477ba..ce30c36e97 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -67,6 +67,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['javascript'], \ 'description': 'Fix JavaScript files using standard --fix', \ }, +\ 'stylelint': { +\ 'function': 'ale#fixers#stylelint#Fix', +\ 'suggested_filetypes': ['css', 'sass', 'scss', 'stylus'], +\ 'description': 'Fix stylesheet files using stylelint --fix.', +\ }, \} " Reset the function registry to the default entries. diff --git a/autoload/ale/fixers/stylelint.vim b/autoload/ale/fixers/stylelint.vim new file mode 100644 index 0000000000..7d5abb739c --- /dev/null +++ b/autoload/ale/fixers/stylelint.vim @@ -0,0 +1,31 @@ +" Author: Mahmoud Mostafa +" Description: Fixing files with stylelint. + +call ale#Set('stylelint_executable', 'stylelint') +call ale#Set('stylelint_use_global', 0) + +function! ale#fixers#stylelint#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'stylelint', [ + \ 'node_modules/stylelint/bin/stylelint.js', + \ 'node_modules/.bin/stylelint', + \]) +endfunction + + +function! ale#fixers#stylelint#Fix(buffer) abort + let l:executable = ale#fixers#stylelint#GetExecutable(a:buffer) + + if ale#Has('win32') && l:executable =~? 'stylelint\.js$' + " For Windows, if we detect an stylelint.js script, we need to execute + " it with node, or the file can be opened with a text editor. + let l:head = 'node ' . ale#Escape(l:executable) + else + let l:head = ale#Escape(l:executable) + endif + + return { + \ 'command': l:head + \ . ' --fix %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/test/eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js b/test/eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/eslint-test-files/react-app/subdir/testfile.css b/test/eslint-test-files/react-app/subdir/testfile.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixers/test_stylelint_fixer_callback.vader b/test/fixers/test_stylelint_fixer_callback.vader new file mode 100644 index 0000000000..6c99196953 --- /dev/null +++ b/test/fixers/test_stylelint_fixer_callback.vader @@ -0,0 +1,32 @@ +Before: + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + let g:ale_has_override = {} + call ale#test#RestoreDirectory() + +Execute(The path to stylelint.js should be run on Unix): + call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.css') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': + \ ale#Escape(simplify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js')) + \ . ' --fix %t', + \ }, + \ ale#fixers#stylelint#Fix(bufnr('')) + +Execute(The stylelint fixer with stylelint.js should be run with node on Windows): + call ale#test#SetFilename('../eslint-test-files/react-app/subdir/testfile.css') + let g:ale_has_override['win32'] = 1 + + " We have to execute the file with node. + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': 'node ' + \ . ale#Escape(simplify(g:dir . '/../eslint-test-files/react-app/node_modules/stylelint/bin/stylelint.js')) + \ . ' --fix %t', + \ }, + \ ale#fixers#stylelint#Fix(bufnr('')) From ec82530247547a696f888cdefd4aeec5cf3d40cf Mon Sep 17 00:00:00 2001 From: w0rp Date: Mon, 31 Jul 2017 22:36:30 +0100 Subject: [PATCH 7/9] #734 - Do not clear file linter results when no buffers are run --- autoload/ale/engine.vim | 29 +++++++++--------- test/test_lint_file_linters.vader | 51 ++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 455772f387..76c529fb6c 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -667,25 +667,26 @@ function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort call s:StopCurrentJobs(a:buffer, a:should_lint_file) call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters) - let l:any_linter_ran = 0 + " We can only clear the results if we aren't checking the buffer. + let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer) 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 + " Only run lint_file linters if we should. + if !l:linter.lint_file || a:should_lint_file + if s:RunLinter(a:buffer, l:linter) + " If a single linter ran, we shouldn't clear everything. + let l:can_clear_results = 0 + endif + else + " If we skipped running a lint_file linter still in the list, + " we shouldn't clear everything. + let l:can_clear_results = 0 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) + " Clear the results if we can. This needs to be done when linters are + " disabled, or ALE itself is disabled. + if l:can_clear_results call ale#engine#SetResults(a:buffer, []) endif endfunction diff --git a/test/test_lint_file_linters.vader b/test/test_lint_file_linters.vader index b0dc1eb3be..a82db72704 100644 --- a/test/test_lint_file_linters.vader +++ b/test/test_lint_file_linters.vader @@ -1,6 +1,11 @@ Before: Save g:ale_run_synchronously + Save g:ale_buffer_info + Save g:ale_linters + + let g:ale_buffer_info = {} let g:ale_run_synchronously = 1 + call ale#ResetLintFileMarkers() let g:buffer_result = [ \ { @@ -95,8 +100,6 @@ Given foobar (Some imaginary filetype): baz Execute(Running linters without 'lint_file' should run only buffer linters): - call ale#ResetLintFileMarkers() - let g:ale_buffer_info = {} call ale#Queue(0) AssertEqual [ @@ -115,9 +118,6 @@ Execute(Running linters without 'lint_file' should run only buffer linters): \], GetSimplerLoclist() Execute(Running linters with 'lint_file' should run all linters): - call ale#ResetLintFileMarkers() - let g:ale_buffer_info = {} - Assert filereadable(expand('%:p')), 'The file was not readable' call ale#Queue(0, 'lint_file') @@ -150,9 +150,6 @@ Execute(Running linters with 'lint_file' should run all linters): \], GetSimplerLoclist() Execute(Linter errors from files should be kept): - call ale#ResetLintFileMarkers() - let g:ale_buffer_info = {} - Assert filereadable(expand('%:p')), 'The file was not readable' call ale#Queue(0, 'lint_file') @@ -189,3 +186,41 @@ Execute(Linter errors from files should be kept): \ 'type': 'E', \ }, \], GetSimplerLoclist() + +Execute(Linter errors from files should be kept when no other linters are run): + let g:ale_linters = {'foobar': ['lint_file_linter']} + Assert filereadable(expand('%:p')), 'The file was not readable' + + call ale#Queue(0, 'lint_file') + + AssertEqual [ + \ { + \ 'lnum': 1, + \ 'col': 3, + \ 'text': 'file warning', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 3, + \ 'text': 'file error', + \ 'type': 'E', + \ }, + \], GetSimplerLoclist() + + call ale#Queue(0) + + AssertEqual [ + \ { + \ 'lnum': 1, + \ 'col': 3, + \ 'text': 'file warning', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 3, + \ 'text': 'file error', + \ 'type': 'E', + \ }, + \], GetSimplerLoclist() From a4ffd2f37c5d535d62170f7e3021cd2981689988 Mon Sep 17 00:00:00 2001 From: w0rp Date: Tue, 1 Aug 2017 00:03:24 +0100 Subject: [PATCH 8/9] #734 - Use the buffer number from the events for entering buffers and saving buffers for checking buffers --- autoload/ale.vim | 38 ++++++++++++------- autoload/ale/cursor.vim | 6 +-- autoload/ale/engine.vim | 2 +- autoload/ale/events.vim | 20 +++++----- plugin/ale.vim | 6 +-- test/test_ale_fix.vader | 6 +-- test/test_ale_init_au_groups.vader | 12 +++--- ...test_lint_on_enter_when_file_changed.vader | 2 +- 8 files changed, 52 insertions(+), 40 deletions(-) diff --git a/autoload/ale.vim b/autoload/ale.vim index 5efe15ab8c..aba3fda171 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -15,19 +15,19 @@ endfunction " 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 +function! ale#ShouldDoNothing(buffer) abort " Do nothing for blacklisted files " OR if ALE is running in the sandbox return index(g:ale_filetype_blacklist, &filetype) >= 0 \ || (exists('*getcmdwintype') && !empty(getcmdwintype())) \ || ale#util#InSandbox() - \ || !ale#Var(bufnr(''), 'enabled') + \ || !ale#Var(a:buffer, 'enabled') \ || ale#FileTooLarge() endfunction -" (delay, [linting_flag]) +" (delay, [linting_flag, buffer_number]) function! ale#Queue(delay, ...) abort - if len(a:0) > 1 + if a:0 > 2 throw 'too many arguments!' endif @@ -38,7 +38,13 @@ function! ale#Queue(delay, ...) abort throw "linting_flag must be either '' or 'lint_file'" endif - if ale#ShouldDoNothing() + let l:buffer = get(a:000, 1, bufnr('')) + + if type(l:buffer) != type(0) + throw 'buffer_number must be a Number' + endif + + if ale#ShouldDoNothing(l:buffer) return endif @@ -53,7 +59,6 @@ function! ale#Queue(delay, ...) abort let s:lint_timer = -1 endif - let l:buffer = bufnr('') let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype')) " Don't set up buffer data and so on if there are no linters to run. @@ -68,21 +73,26 @@ function! ale#Queue(delay, ...) abort endif if a:delay > 0 - let s:queued_buffer_number = bufnr('%') + let s:queued_buffer_number = l:buffer let s:lint_timer = timer_start(a:delay, function('ale#Lint')) else - call ale#Lint() + call ale#Lint(-1, l:buffer) endif endfunction function! ale#Lint(...) abort - " Get the buffer number linting was queued for. - " or else take the current one. - let l:buffer = len(a:0) > 1 && a:1 == s:lint_timer - \ ? s:queued_buffer_number - \ : bufnr('%') + if a:0 > 1 + " Use the buffer number given as the optional second argument. + let l:buffer = a:2 + elseif a:0 > 0 && a:1 == s:lint_timer + " Use the buffer number for the buffer linting was queued for. + let l:buffer = s:queued_buffer_number + else + " Use the current buffer number. + let l:buffer = bufnr('') + endif - if ale#ShouldDoNothing() + if ale#ShouldDoNothing(l:buffer) return endif diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim index 3e19b6fc69..0c6a863499 100644 --- a/autoload/ale/cursor.vim +++ b/autoload/ale/cursor.vim @@ -66,7 +66,7 @@ function! s:StopCursorTimer() abort endfunction function! ale#cursor#EchoCursorWarning(...) abort - if ale#ShouldDoNothing() + if ale#ShouldDoNothing(bufnr('')) return endif @@ -93,7 +93,7 @@ let s:cursor_timer = -1 let s:last_pos = [0, 0, 0] function! ale#cursor#EchoCursorWarningWithDelay() abort - if ale#ShouldDoNothing() + if ale#ShouldDoNothing(bufnr('')) return endif @@ -112,7 +112,7 @@ function! ale#cursor#EchoCursorWarningWithDelay() abort endfunction function! ale#cursor#ShowCursorDetail() abort - if ale#ShouldDoNothing() + if ale#ShouldDoNothing(bufnr('')) return endif diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 76c529fb6c..52acfe7f14 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -139,7 +139,7 @@ function! s:HandleLoclist(linter_name, buffer, loclist) abort " for efficient lookup of the messages in the cursor handler. call sort(g:ale_buffer_info[a:buffer].loclist, 'ale#util#LocItemCompare') - if ale#ShouldDoNothing() + if ale#ShouldDoNothing(a:buffer) return endif diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim index f740fdaa58..4722afa971 100644 --- a/autoload/ale/events.vim +++ b/autoload/ale/events.vim @@ -1,7 +1,7 @@ " Author: w0rp -function! ale#events#SaveEvent() abort - let l:should_lint = g:ale_enabled && g:ale_lint_on_save +function! ale#events#SaveEvent(buffer) abort + let l:should_lint = ale#Var(a:buffer, 'enabled') && g:ale_lint_on_save if g:ale_fix_on_save let l:will_fix = ale#fix#Fix('save_file') @@ -9,25 +9,27 @@ function! ale#events#SaveEvent() abort endif if l:should_lint - call ale#Queue(0, 'lint_file') + call ale#Queue(0, 'lint_file', a:buffer) endif endfunction -function! s:LintOnEnter() abort - if g:ale_enabled && g:ale_lint_on_enter && has_key(b:, 'ale_file_changed') +function! s:LintOnEnter(buffer) abort + if ale#Var(a:buffer, 'enabled') + \&& g:ale_lint_on_enter + \&& has_key(b:, 'ale_file_changed') call remove(b:, 'ale_file_changed') - call ale#Queue(0, 'lint_file') + call ale#Queue(0, 'lint_file', a:buffer) endif endfunction -function! ale#events#EnterEvent() abort - call s:LintOnEnter() +function! ale#events#EnterEvent(buffer) abort + call s:LintOnEnter(a:buffer) endfunction function! ale#events#FileChangedEvent(buffer) abort call setbufvar(a:buffer, 'ale_file_changed', 1) if bufnr('') == a:buffer - call s:LintOnEnter() + call s:LintOnEnter(a:buffer) endif endfunction diff --git a/plugin/ale.vim b/plugin/ale.vim index dc9b93ee24..79e6836bc4 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -219,11 +219,11 @@ function! ALEInitAuGroups() abort augroup ALERunOnEnterGroup autocmd! if g:ale_enabled && g:ale_lint_on_enter - autocmd BufWinEnter,BufRead * call ale#Queue(300, 'lint_file') + autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand(''))) " Track when the file is changed outside of Vim. autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand(''))) " If the file has been changed, then check it again on enter. - autocmd BufEnter * call ale#events#EnterEvent() + autocmd BufEnter * call ale#events#EnterEvent(str2nr(expand(''))) endif augroup END @@ -245,7 +245,7 @@ function! ALEInitAuGroups() abort augroup ALERunOnSaveGroup autocmd! if (g:ale_enabled && g:ale_lint_on_save) || g:ale_fix_on_save - autocmd BufWritePost * call ale#events#SaveEvent() + autocmd BufWritePost * call ale#events#SaveEvent(str2nr(expand(''))) endif augroup END diff --git a/test/test_ale_fix.vader b/test/test_ale_fix.vader index d7c3fb353a..bafeee2b50 100644 --- a/test/test_ale_fix.vader +++ b/test/test_ale_fix.vader @@ -246,7 +246,7 @@ Execute(ALEFix should save files on the save event): let g:ale_fixers.testft = ['AddDollars'] call SetUpLinters() - call ale#events#SaveEvent() + call ale#events#SaveEvent(bufnr('')) " We should save the file. AssertEqual ['$a', '$b', '$c'], readfile('fix_test_file') @@ -285,7 +285,7 @@ Execute(ALEFix should still lint with no linters to be applied): let g:ale_fixers.testft = [] call SetUpLinters() - call ale#events#SaveEvent() + call ale#events#SaveEvent(bufnr('')) Assert !filereadable('fix_test_file'), 'The file should not have been saved' @@ -317,7 +317,7 @@ Execute(ALEFix should still lint when nothing was fixed on save): let g:ale_fixers.testft = ['DoNothing'] call SetUpLinters() - call ale#events#SaveEvent() + call ale#events#SaveEvent(bufnr('')) Assert !filereadable('fix_test_file'), 'The file should not have been saved' diff --git a/test/test_ale_init_au_groups.vader b/test/test_ale_init_au_groups.vader index c20f4f9720..e036343bcf 100644 --- a/test/test_ale_init_au_groups.vader +++ b/test/test_ale_init_au_groups.vader @@ -116,9 +116,9 @@ Execute (g:ale_lint_on_enter = 1 should bind the required events): let g:ale_lint_on_enter = 1 AssertEqual [ - \ 'BufEnter * call ale#events#EnterEvent()', - \ 'BufReadPost * call ale#Queue(300, ''lint_file'')', - \ 'BufWinEnter * call ale#Queue(300, ''lint_file'')', + \ 'BufEnter * call ale#events#EnterEvent(str2nr(expand('''')))', + \ 'BufReadPost * call ale#Queue(0, ''lint_file'', str2nr(expand('''')))', + \ 'BufWinEnter * call ale#Queue(0, ''lint_file'', str2nr(expand('''')))', \ 'FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('''')))', \], CheckAutocmd('ALERunOnEnterGroup') @@ -151,7 +151,7 @@ Execute (g:ale_lint_on_save = 1 should bind no events): let g:ale_fix_on_save = 0 AssertEqual [ - \ 'BufWritePost * call ale#events#SaveEvent()', + \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand('''')))', \], CheckAutocmd('ALERunOnSaveGroup') Execute (g:ale_lint_on_save = 0 and g:ale_fix_on_save = 1 should bind events): @@ -159,7 +159,7 @@ Execute (g:ale_lint_on_save = 0 and g:ale_fix_on_save = 1 should bind events): let g:ale_fix_on_save = 1 AssertEqual [ - \ 'BufWritePost * call ale#events#SaveEvent()', + \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand('''')))', \], CheckAutocmd('ALERunOnSaveGroup') Execute (g:ale_fix_on_save = 1 should bind events even when ALE is disabled): @@ -168,7 +168,7 @@ Execute (g:ale_fix_on_save = 1 should bind events even when ALE is disabled): let g:ale_fix_on_save = 1 AssertEqual [ - \ 'BufWritePost * call ale#events#SaveEvent()', + \ 'BufWritePost * call ale#events#SaveEvent(str2nr(expand('''')))', \], CheckAutocmd('ALERunOnSaveGroup') Execute (g:ale_echo_cursor = 0 should bind no events): diff --git a/test/test_lint_on_enter_when_file_changed.vader b/test/test_lint_on_enter_when_file_changed.vader index cfa5387820..4d4f19cc35 100644 --- a/test/test_lint_on_enter_when_file_changed.vader +++ b/test/test_lint_on_enter_when_file_changed.vader @@ -64,7 +64,7 @@ Execute(The buffer should be checked after entering it after the file has change let b:ale_file_changed = 1 set filetype=foobar - call ale#events#EnterEvent() + call ale#events#EnterEvent(bufnr('')) AssertEqual [{ \ 'bufnr': bufnr(''), From 35913d9ce7c9679c1242ffdf14bfa1126c999e7e Mon Sep 17 00:00:00 2001 From: w0rp Date: Tue, 1 Aug 2017 00:42:22 +0100 Subject: [PATCH 9/9] Cover the SaveEvent function with a test --- test/test_lint_file_linters.vader | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/test_lint_file_linters.vader b/test/test_lint_file_linters.vader index a82db72704..4110c059ce 100644 --- a/test/test_lint_file_linters.vader +++ b/test/test_lint_file_linters.vader @@ -224,3 +224,30 @@ Execute(Linter errors from files should be kept when no other linters are run): \ 'type': 'E', \ }, \], GetSimplerLoclist() + +Execute(The Save event should respect the buffer number): + let g:ale_linters = {'foobar': ['lint_file_linter']} + Assert filereadable(expand('%:p')), 'The file was not readable' + + call ale#events#SaveEvent(bufnr('') + 1) + + " We shouldn't get any prblems yet. + AssertEqual [], GetSimplerLoclist() + + call ale#events#SaveEvent(bufnr('')) + + " We should get them now we used the right buffer number. + AssertEqual [ + \ { + \ 'lnum': 1, + \ 'col': 3, + \ 'text': 'file warning', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 2, + \ 'col': 3, + \ 'text': 'file error', + \ 'type': 'E', + \ }, + \], GetSimplerLoclist()