From aca5a00fb7b00655685a4306f1517d4e0f9126ee Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 27 May 2017 21:27:42 +0100 Subject: [PATCH] Fix #500 - Support defining aliases for linter names --- autoload/ale/debugging.vim | 26 +++++++- autoload/ale/linter.vim | 18 +++++- doc/ale.txt | 7 +++ test/test_ale_info.vader | 26 ++++++++ test/test_linter_defintion_processing.vader | 42 +++++++++++++ test/test_linter_retrieval.vader | 70 ++++++++++++--------- 6 files changed, 156 insertions(+), 33 deletions(-) diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim index f42c9e8..5e4b7a2 100644 --- a/autoload/ale/debugging.vim +++ b/autoload/ale/debugging.vim @@ -105,6 +105,22 @@ function! s:EchoCommandHistory() abort endfor endfunction +function! s:EchoLinterAliases(all_linters) abort + let l:first = 1 + + for l:linter in a:all_linters + if !empty(l:linter.aliaes) + if !l:first + echom ' Linter Aliases:' + endif + + let l:first = 0 + + echom string(l:linter.name) . ' -> ' . string(l:linter.aliaes) + endif + endfor +endfunction + function! ale#debugging#Info() abort let l:filetype = &filetype @@ -120,8 +136,13 @@ function! ale#debugging#Info() abort 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'']') + let l:all_names = map(copy(l:all_linters), 'v:val[''name'']') + let l:enabled_names = map(copy(l:enabled_linters), 'v:val[''name'']') + let l:linter_aliases = [] + + for l:linter in l:all_linters + call add(l:linter_aliases, [l:linter.name, l:linter.aliaes]) + endfor " Load linter variables to display " This must be done after linters are loaded. @@ -129,6 +150,7 @@ function! ale#debugging#Info() abort echom ' Current Filetype: ' . l:filetype echom 'Available Linters: ' . string(l:all_names) + call s:EchoLinterAliases(l:all_linters) echom ' Enabled Linters: ' . string(l:enabled_names) echom ' Linter Variables:' echom '' diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 0515621..12c6e84 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -164,6 +164,13 @@ function! ale#linter#PreProcess(linter) abort throw 'Only one of `lint_file` or `read_buffer` can be `1`' endif + let l:obj.aliases = get(a:linter, 'aliases', []) + + if type(l:obj.aliases) != type([]) + \|| len(filter(copy(l:obj.aliases), 'type(v:val) != type('''')')) > 0 + throw '`aliases` must be a List of String values' + endif + return l:obj endfunction @@ -256,9 +263,14 @@ function! ale#linter#Get(original_filetypes) abort elseif type(l:linter_names) == type([]) " Select only the linters we or the user has specified. for l:linter in l:all_linters - if index(l:linter_names, l:linter.name) >= 0 - call add(l:filetype_linters, l:linter) - endif + let l:name_list = [l:linter.name] + l:linter.aliases + + for l:name in l:name_list + if index(l:linter_names, l:name) >= 0 + call add(l:filetype_linters, l:linter) + break + endif + endfor endfor endif diff --git a/doc/ale.txt b/doc/ale.txt index 514ba73..8fb048e 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1078,6 +1078,13 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* be set automatically to `0`. The two options cannot be used together. + `aliases` A |List| of aliases for the linter name. + + This option can be set with alternative names for + for selecting the linter with |g:ale_linters|. This + setting can make it easier to guess the linter name + by offering a few alternatives. + Only one of `command`, `command_callback`, or `command_chain` should be specified. `command_callback` is generally recommended when a command string needs to be generated dynamically, or any global options are used. diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader index 83d32cb..3c4e2b1 100644 --- a/test/test_ale_info.vader +++ b/test/test_ale_info.vader @@ -208,6 +208,32 @@ Execute (ALEInfo should buffer-local linter variables): \let b:ale_testft2_testlinter2_foo = 456" \ . g:globals_string . g:command_header, g:output +Given testft.testft2 (Empty buffer with two filetypes): +Execute (ALEInfo should output linter aliases): + let g:testlinter1.aliases = ['testftalias1', 'testftalias2'] + let g:testlinter2.aliases = ['testftalias3', 'testftalias4'] + + let g:ale_testft2_testlinter2_foo = 123 + let b:ale_testft2_testlinter2_foo = 456 + + 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 + \ Linter Aliases:\n + \ 'testlinter1' -> ['testftalias1', 'testftalias2']\n + \ 'testlinter2' -> ['testftalias3', 'testftalias4']\n + \ Enabled Linters: ['testlinter1', 'testlinter2']\n + \ Linter Variables:\n + \\n + \let g:ale_testft2_testlinter2_foo = 123\n + \let b:ale_testft2_testlinter2_foo = 456" + \ . 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('%')] = { diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader index 91667e0..0956655 100644 --- a/test/test_linter_defintion_processing.vader +++ b/test/test_linter_defintion_processing.vader @@ -323,3 +323,45 @@ Execute(PreProcess should set a default value for lint_file): \} AssertEqual 0, ale#linter#PreProcess(g:linter).lint_file + +Execute(PreProcess should set a default value for aliases): + let g:linter = { + \ 'name': 'x', + \ 'callback': 'x', + \ 'executable': 'x', + \ 'command': 'x', + \} + + AssertEqual [], ale#linter#PreProcess(g:linter).aliases + +Execute(PreProcess should complain about invalid `aliases` values): + let g:linter = { + \ 'name': 'x', + \ 'callback': 'x', + \ 'executable': 'x', + \ 'command': 'x', + \ 'aliases': 'foo', + \} + + AssertThrows call ale#linter#PreProcess(g:linter) + AssertEqual '`aliases` must be a List of String values', g:vader_exception + + let g:linter.aliases = [1] + + AssertThrows call ale#linter#PreProcess(g:linter) + AssertEqual '`aliases` must be a List of String values', g:vader_exception + +Execute(PreProcess should accept `aliases` lists): + let g:linter = { + \ 'name': 'x', + \ 'callback': 'x', + \ 'executable': 'x', + \ 'command': 'x', + \ 'aliases': [], + \} + + AssertEqual [], ale#linter#PreProcess(g:linter).aliases + + let g:linter.aliases = ['foo', 'bar'] + + AssertEqual ['foo', 'bar'], ale#linter#PreProcess(g:linter).aliases diff --git a/test/test_linter_retrieval.vader b/test/test_linter_retrieval.vader index ecbae8d..39258be 100644 --- a/test/test_linter_retrieval.vader +++ b/test/test_linter_retrieval.vader @@ -1,85 +1,99 @@ Before: - let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout', 'read_buffer': 1, 'lint_file': 0} - let g:testlinter2 = {'name': 'testlinter2', 'executable': 'testlinter2', 'command': 'testlinter2', 'callback': 'testCB2', 'output_stream': 'stdout', 'read_buffer': 0, 'lint_file': 1} + Save g:ale_linters, g:ale_linter_aliases + let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout', 'read_buffer': 1, 'lint_file': 0, 'aliases': []} + let g:testlinter2 = {'name': 'testlinter2', 'executable': 'testlinter2', 'command': 'testlinter2', 'callback': 'testCB2', 'output_stream': 'stdout', 'read_buffer': 0, 'lint_file': 1, 'aliases': []} call ale#linter#Reset() - let g:ale_linters = {} - let g:ale_linter_aliases = {} + +After: + Restore + + unlet! g:testlinter1 + unlet! g:testlinter2 unlet! b:ale_linters unlet! b:ale_linter_aliases + call ale#linter#Reset() -Execute (Define a linter): +Execute (You should be able to get a defined linter): call ale#linter#Define('testft', g:testlinter1) -Then (Get the defined linter): AssertEqual [g:testlinter1], ale#linter#Get('testft') -Execute (Define a couple linters, filtering one): +Execute (You should be able get select a single linter): call ale#linter#Define('testft', g:testlinter1) call ale#linter#Define('testft', g:testlinter2) let g:ale_linters = {'testft': ['testlinter1']} -Then (Only the configured linter should be returned): + AssertEqual [g:testlinter1], ale#linter#Get('testft') -Execute (Define a couple linters, and set a buffer override): +Execute (You should be able to select a linter by an alias): + let g:testlinter1.aliases = ['foo', 'linter1alias'] + + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + let g:ale_linters = {'testft': ['linter1alias']} + + AssertEqual [g:testlinter1], ale#linter#Get('testft') + +Execute (You should be able to select linters with a buffer option): call ale#linter#Define('testft', g:testlinter1) call ale#linter#Define('testft', g:testlinter2) let g:ale_linters = {'testft': ['testlinter1', 'testlinter2']} let b:ale_linters = {'testft': ['testlinter1']} -Then (The buffer setting should be used): + AssertEqual [g:testlinter1], ale#linter#Get('testft') -Execute (Define a couple linters, and set a buffer override for another filetype): +Execute (Buffer settings shouldn't completely replace global settings): call ale#linter#Define('testft', g:testlinter1) call ale#linter#Define('testft', g:testlinter2) let g:ale_linters = {'testft': ['testlinter1']} let b:ale_linters = {'testft2': ['testlinter1', 'testlinter2']} -Then (The global value should be used): + AssertEqual [g:testlinter1], ale#linter#Get('testft') -Execute (Define a linter for a filetype, and create a filetype alias): +Execute (You should be able to alias linters from one filetype to another): call ale#linter#Define('testft1', g:testlinter1) let g:ale_linter_aliases = {'testft2': 'testft1'} -Then (Linters should be transparently aliased): + AssertEqual [g:testlinter1], ale#linter#Get('testft2') -Execute (Define multiple linters, with filters and aliases): +Execute (You should be able to filter aliased linters): call ale#linter#Define('testft1', g:testlinter1) call ale#linter#Define('testft1', g:testlinter2) let g:ale_linters = {'testft1': ['testlinter1'], 'testft2': ['testlinter2']} let g:ale_linter_aliases = {'testft2': 'testft1'} -Then (Linters should be transparently filtered and aliased): + AssertEqual [g:testlinter1], ale#linter#Get('testft1') AssertEqual [g:testlinter2], ale#linter#Get('testft2') -Execute (Define multiple linters for different filetypes): +Execute (Dot-separated filetypes should be handled correctly): call ale#linter#Define('testft1', g:testlinter1) call ale#linter#Define('testft2', g:testlinter2) -Then (Linters for dot-seperated filetypes should be properly handled): + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft1.testft2') -Execute (Define multiple aliases for a filetype): +Execute (Linters for multiple aliases should be loaded): call ale#linter#Define('testft1', g:testlinter1) call ale#linter#Define('testft2', g:testlinter2) let ale_linter_aliases = {'testft3': ['testft1', 'testft2']} -Then (Linters should be transparently aliased): + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft3') -Execute (Alias a filetype to itself plus another one): +Execute (You should be able to alias filetypes to themselves and another): call ale#linter#Define('testft1', g:testlinter1) call ale#linter#Define('testft2', g:testlinter2) let ale_linter_aliases = {'testft1': ['testft1', 'testft2']} -Then (The original linters should still be there): + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft1') -Execute (Set up aliases in the buffer): +Execute (Buffer-local overrides for aliases should be used): call ale#linter#Define('testft1', g:testlinter1) call ale#linter#Define('testft2', g:testlinter2) let g:ale_linter_aliases = {'testft1': ['testft2']} let b:ale_linter_aliases = {'testft1': ['testft1', 'testft2']} -Then (The buffer-local override should be used): + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft1') -Execute (Set up aliases in the buffer for another filetype): +Execute (The local alias option shouldn't completely replace the global one): call ale#linter#Define('testft1', g:testlinter1) call ale#linter#Define('testft2', g:testlinter2) let g:ale_linter_aliases = {'testft1': ['testft1', 'testft2']} @@ -87,8 +101,8 @@ Execute (Set up aliases in the buffer for another filetype): " We should look for a key in this Dictionary first, and then check the " global Dictionary. let b:ale_linter_aliases = {'testft3': ['testft1']} -Then (The global value should be used): + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft1') -Execute (Try to load a linter from disk): - AssertEqual [{'name': 'testlinter', 'output_stream': 'stdout', 'executable': 'testlinter', 'command': 'testlinter', 'callback': 'testCB', 'read_buffer': 1, 'lint_file': 0}], ale#linter#Get('testft') +Execute (Linters should be loaded from disk appropriately): + AssertEqual [{'name': 'testlinter', 'output_stream': 'stdout', 'executable': 'testlinter', 'command': 'testlinter', 'callback': 'testCB', 'read_buffer': 1, 'lint_file': 0, 'aliases': []}], ale#linter#Get('testft')