From 1997a8f7e22a930e3d47b61a7f9ab948d04f2b92 Mon Sep 17 00:00:00 2001 From: Eddie Lebow Date: Sat, 23 Nov 2019 00:06:03 -0500 Subject: ShellDetect: fall back to filetype if no hashbang Some files lack a hashbang line but still have an unambiguous filetype. For example, the file `.zshrc` has the filetype `zsh`. Augment ale#handlers#sh#GetShellType to fall back to the filetype if no hashbang line can be found. --- autoload/ale/handlers/sh.vim | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/handlers/sh.vim b/autoload/ale/handlers/sh.vim index 75eaf71f..1e50cb89 100644 --- a/autoload/ale/handlers/sh.vim +++ b/autoload/ale/handlers/sh.vim @@ -4,17 +4,24 @@ function! ale#handlers#sh#GetShellType(buffer) abort let l:bang_line = get(getbufline(a:buffer, 1), 0, '') + let l:command = '' + " Take the shell executable from the hashbang, if we can. if l:bang_line[:1] is# '#!' " Remove options like -e, etc. let l:command = substitute(l:bang_line, ' --\?[a-zA-Z0-9]\+', '', 'g') + endif - for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'ksh', 'sh'] - if l:command =~# l:possible_shell . '\s*$' - return l:possible_shell - endif - endfor + " If we couldn't find a hashbang, try the filetype + if l:command is# '' + let l:command = &filetype endif + for l:possible_shell in ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'ksh', 'sh'] + if l:command =~# l:possible_shell . '\s*$' + return l:possible_shell + endif + endfor + return '' endfunction -- cgit v1.2.3 From efe120ce9a9100129f1a811a659b0d7526cbf266 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 3 Dec 2019 16:42:10 -0800 Subject: Fix prettier_standard to respect the configuration file Before this change, prettier_standard would run and ignore any .prettierrc, now it will respect the configuration of the file being linted. This change relies on prettier-standard 16.1.0 for the --stdin-filepath flag, but is backward compatible: older versions of prettier-standard will ignore the unknown flag and continue to run with no configuration file. --- autoload/ale/fixers/prettier_standard.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/fixers/prettier_standard.vim b/autoload/ale/fixers/prettier_standard.vim index b6e0a6f9..9d982ff6 100644 --- a/autoload/ale/fixers/prettier_standard.vim +++ b/autoload/ale/fixers/prettier_standard.vim @@ -17,8 +17,8 @@ function! ale#fixers#prettier_standard#Fix(buffer) abort return { \ 'command': ale#Escape(ale#fixers#prettier_standard#GetExecutable(a:buffer)) - \ . ' %t' + \ . ' --stdin' + \ . ' --stdin-filepath=%s' \ . ' ' . l:options, - \ 'read_temporary_file': 1, \} endfunction -- cgit v1.2.3 From c207d6a550916f4d34a41711c64024251466fea5 Mon Sep 17 00:00:00 2001 From: Horacio Sanson Date: Thu, 12 Mar 2020 16:00:30 +0900 Subject: Fix 2816 - Standard fix does not work. The standard linter --fix fails if the file being input is not relative to the project root (https://github.com/standard/standard/issues/1384). This MR attempts to fix this by changing the command so the input file is relative to the project root and the output is to a temporary file. Preliminary tests with toy javascript projects seem to indicate this works fine. --- autoload/ale/fixers/standard.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/fixers/standard.vim b/autoload/ale/fixers/standard.vim index cffa9f9d..46decebf 100644 --- a/autoload/ale/fixers/standard.vim +++ b/autoload/ale/fixers/standard.vim @@ -27,7 +27,7 @@ function! ale#fixers#standard#Fix(buffer) abort return { \ 'command': ale#node#Executable(a:buffer, l:executable) \ . (!empty(l:options) ? ' ' . l:options : '') - \ . ' --fix %t', + \ . ' --fix --stdin < %s > %t', \ 'read_temporary_file': 1, \} endfunction -- cgit v1.2.3 From 1c6b9354a825a0dbab310077dd05666b8e081974 Mon Sep 17 00:00:00 2001 From: John Gehrig Date: Thu, 26 Mar 2020 09:25:18 -0400 Subject: Issue 2598: Addtional ^M characters on Windows Windows may insert carriage return line endings, which ALE does not handle well. These characters should not be displayed. Adds a line to remove these characters for all messages. --- autoload/ale.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'autoload') diff --git a/autoload/ale.vim b/autoload/ale.vim index ee1a0d54..6d0afb9e 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -263,6 +263,8 @@ function! ale#GetLocItemMessage(item, format_string) abort let l:msg = substitute(l:msg, '\v\%([^\%]*)code([^\%]*)\%', l:code_repl, 'g') " Replace %s with the text. let l:msg = substitute(l:msg, '\V%s', '\=a:item.text', 'g') + " Windows may insert carriage return line endings (^M), strip these characters. + let l:msg = substitute(l:msg, '\r', '', 'g') return l:msg endfunction -- cgit v1.2.3 From 15d590ee5e6779b6dad2640cdb55abc9d357bbe9 Mon Sep 17 00:00:00 2001 From: "James C. Davis" Date: Thu, 30 Apr 2020 03:43:17 -0400 Subject: fix: don't append newline when buffer is noeol and nofixeol --- autoload/ale/util.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim index ee62af28..1f716a13 100644 --- a/autoload/ale/util.vim +++ b/autoload/ale/util.vim @@ -418,7 +418,10 @@ function! ale#util#Writefile(buffer, lines, filename) abort \ ? map(copy(a:lines), 'substitute(v:val, ''\r*$'', ''\r'', '''')') \ : a:lines - call writefile(l:corrected_lines, a:filename, 'S') " no-custom-checks + " Set binary flag if buffer doesn't have eol and nofixeol to avoid appending newline + let l:flags = !getbufvar(a:buffer, '&eol') && exists('+fixeol') && !&fixeol ? 'bS' : 'S' + + call writefile(l:corrected_lines, a:filename, l:flags) " no-custom-checks endfunction if !exists('s:patial_timers') -- cgit v1.2.3 From b20931571484108d1ec29eaab5b731e754649664 Mon Sep 17 00:00:00 2001 From: Stephen Robinson Date: Thu, 2 Apr 2020 15:49:03 -0700 Subject: Fixes #3092 - Implement loading `@file` c arguments --- autoload/ale/c.vim | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 9b428700..e8212dcd 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -68,10 +68,58 @@ function! ale#c#ShellSplit(line) abort return l:args endfunction +" Takes the path prefix and a list of cflags and expands @file arguments to +" the contents of the file. +" +" @file arguments are command line arguments recognised by gcc and clang. For +" instance, if @./path/to/file was given to gcc, it would load .path/to/file +" and use the contents of that file as arguments. +function! ale#c#ExpandAtArgs(path_prefix, raw_split_lines) abort + let l:out_lines = [] + + for l:option in a:raw_split_lines + if stridx(l:option, '@') == 0 + " This is an argument specifying a location of a file containing other arguments + let l:path = join(split(l:option, '\zs')[1:], '') + + " Make path absolute + if stridx(l:path, s:sep) != 0 && stridx(l:path, '/') != 0 + let l:rel_path = substitute(l:path, '"', '', 'g') + let l:rel_path = substitute(l:rel_path, '''', '', 'g') + let l:path = a:path_prefix . s:sep . l:rel_path + endif + + " Read the file and add all the arguments + try + let l:additional_args = readfile(l:path) + catch + continue " All we can really do is skip this argument + endtry + + let l:file_lines = [] + + for l:line in l:additional_args + let l:file_lines += ale#c#ShellSplit(l:line) + endfor + + " @file arguments can include other @file arguments, so we must + " recurse. + let l:out_lines += ale#c#ExpandAtArgs(a:path_prefix, l:file_lines) + else + " This is not an @file argument, so don't touch it. + let l:out_lines += [l:option] + endif + endfor + + return l:out_lines +endfunction + function! ale#c#ParseCFlags(path_prefix, cflag_line) abort let l:cflags_list = [] - let l:split_lines = ale#c#ShellSplit(a:cflag_line) + let l:raw_split_lines = ale#c#ShellSplit(a:cflag_line) + " Expand @file arguments now before parsing + let l:split_lines = ale#c#ExpandAtArgs(a:path_prefix, l:raw_split_lines) let l:option_index = 0 while l:option_index < len(l:split_lines) -- cgit v1.2.3 From 5a4fad6172472fd2cc316ff72b827ed726e69a5b Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 14 Aug 2020 01:55:48 +0100 Subject: Fix #2899 - Handle tsserver default import completion --- autoload/ale/completion.vim | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index bffcdc8a..a273d4e1 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -418,12 +418,22 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort for l:suggestion in a:response.body let l:displayParts = [] + let l:local_name = v:null for l:action in get(l:suggestion, 'codeActions', []) call add(l:displayParts, l:action.description . ' ') endfor for l:part in l:suggestion.displayParts + " Stop on stop on line breaks for the menu. + if get(l:part, 'kind') is# 'lineBreak' + break + endif + + if get(l:part, 'kind') is# 'localName' + let l:local_name = l:part.text + endif + call add(l:displayParts, l:part.text) endfor @@ -436,7 +446,13 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort " See :help complete-items let l:result = { - \ 'word': l:suggestion.name, + \ 'word': ( + \ l:suggestion.name is# 'default' + \ && l:suggestion.kind is# 'alias' + \ && !empty(l:local_name) + \ ? l:local_name + \ : l:suggestion.name + \ ), \ 'kind': ale#completion#GetCompletionSymbols(l:suggestion.kind), \ 'icase': 1, \ 'menu': join(l:displayParts, ''), -- cgit v1.2.3 From d5c1d842307a4c916905d5160544cfe152a16ee0 Mon Sep 17 00:00:00 2001 From: blyoa Date: Mon, 17 Aug 2020 18:14:38 +0900 Subject: Add remark-lint for a markdown fixer (#2836) --- autoload/ale/fix/registry.vim | 5 +++++ autoload/ale/fixers/remark_lint.vim | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 autoload/ale/fixers/remark_lint.vim (limited to 'autoload') diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 02b02699..94476ca9 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -365,6 +365,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['nix'], \ 'description': 'A formatter for Nix code', \ }, +\ 'remark-lint': { +\ 'function': 'ale#fixers#remark_lint#Fix', +\ 'suggested_filetypes': ['markdown'], +\ 'description': 'Fix markdown files with remark-lint', +\ }, \ 'html-beautify': { \ 'function': 'ale#fixers#html_beautify#Fix', \ 'suggested_filetypes': ['html', 'htmldjango'], diff --git a/autoload/ale/fixers/remark_lint.vim b/autoload/ale/fixers/remark_lint.vim new file mode 100644 index 00000000..3ce442f3 --- /dev/null +++ b/autoload/ale/fixers/remark_lint.vim @@ -0,0 +1,24 @@ +" Author: blyoa +" Description: Fixing files with remark-lint. + +call ale#Set('markdown_remark_lint_executable', 'remark') +call ale#Set('markdown_remark_lint_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('markdown_remark_lint_options', '') + +function! ale#fixers#remark_lint#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'markdown_remark_lint', [ + \ 'node_modules/remark-cli/cli.js', + \ 'node_modules/.bin/remark', + \]) +endfunction + +function! ale#fixers#remark_lint#Fix(buffer) abort + let l:executable = ale#fixers#remark_lint#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'markdown_remark_lint_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . (!empty(l:options) ? ' ' . l:options : ''), + \} +endfunction + -- cgit v1.2.3 From 4df352eee585cf50edb208aea713fe1a67ac4094 Mon Sep 17 00:00:00 2001 From: w0rp Date: Tue, 18 Aug 2020 01:48:07 +0100 Subject: Fix #3294 - Fix hover off by 1, handle LSP server crashes --- autoload/ale/hover.vim | 5 ++++- autoload/ale/lsp.vim | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim index 168ff424..38b4b866 100644 --- a/autoload/ale/hover.vim +++ b/autoload/ale/hover.vim @@ -264,7 +264,10 @@ function! s:OnReady(line, column, opt, linter, lsp_details) abort " hover position probably won't make sense. call ale#lsp#NotifyForChanges(l:id, l:buffer) - let l:column = min([a:column, len(getbufline(l:buffer, a:line)[0])]) + let l:column = max([ + \ min([a:column, len(getbufline(l:buffer, a:line)[0])]), + \ 1, + \]) let l:message = ale#lsp#message#Hover(l:buffer, a:line, l:column) endif diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index ae8fd51d..7d99e9d2 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -64,6 +64,9 @@ endfunction " Used only in tests. function! ale#lsp#GetConnections() abort + " This command will throw from the sandbox. + let &l:equalprg=&l:equalprg + return s:connections endfunction @@ -449,6 +452,7 @@ function! ale#lsp#StartProgram(conn_id, executable, command) abort endif if l:started && !l:conn.is_tsserver + let l:conn.initialized = 0 call s:SendInitMessage(l:conn) endif -- cgit v1.2.3 From 5eda1df0a96e691ebf24e5d8a3585c2feb2a48a3 Mon Sep 17 00:00:00 2001 From: w0rp Date: Tue, 18 Aug 2020 23:03:43 +0100 Subject: Remove features deprecated in previous versions --- autoload/ale/assert.vim | 2 +- autoload/ale/definition.vim | 8 -- autoload/ale/engine.vim | 130 +------------------------------ autoload/ale/fix.vim | 25 ++---- autoload/ale/linter.vim | 183 +++++++------------------------------------- autoload/ale/lsp_linter.vim | 2 +- autoload/ale/test.vim | 4 +- 7 files changed, 38 insertions(+), 316 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/assert.vim b/autoload/ale/assert.vim index 291edcee..934fcaa8 100644 --- a/autoload/ale/assert.vim +++ b/autoload/ale/assert.vim @@ -130,7 +130,7 @@ endfunction function! ale#assert#LSPLanguage(expected_language) abort let l:buffer = bufnr('') let l:linter = s:GetLinter() - let l:language = ale#util#GetFunction(l:linter.language_callback)(l:buffer) + let l:language = ale#linter#GetLanguage(l:buffer, l:linter) AssertEqual a:expected_language, l:language endfunction diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim index ffcd9d10..0c1fb7cf 100644 --- a/autoload/ale/definition.vim +++ b/autoload/ale/definition.vim @@ -135,10 +135,6 @@ function! s:GoToLSPDefinition(linter, options, capability) abort endfunction function! ale#definition#GoTo(options) abort - if !get(g:, 'ale_ignore_2_7_warnings') && has_key(a:options, 'deprecated_command') - execute 'echom '':' . a:options.deprecated_command . ' is deprecated. Use `let g:ale_ignore_2_7_warnings = 1` to disable this message.''' - endif - for l:linter in ale#linter#Get(&filetype) if !empty(l:linter.lsp) call s:GoToLSPDefinition(l:linter, a:options, 'definition') @@ -147,10 +143,6 @@ function! ale#definition#GoTo(options) abort endfunction function! ale#definition#GoToType(options) abort - if !get(g:, 'ale_ignore_2_7_warnings') && has_key(a:options, 'deprecated_command') - execute 'echom '':' . a:options.deprecated_command . ' is deprecated. Use `let g:ale_ignore_2_7_warnings = 1` to disable this message.''' - endif - for l:linter in ale#linter#Get(&filetype) if !empty(l:linter.lsp) " TODO: handle typeDefinition for tsserver if supported by the diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 491d3c2e..cfc1e5d7 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -104,42 +104,6 @@ function! ale#engine#IsCheckingBuffer(buffer) abort \ || !empty(get(l:info, 'active_other_sources_list', [])) endfunction -" Register a temporary file to be managed with the ALE engine for -" a current job run. -function! ale#engine#ManageFile(buffer, filename) abort - if !get(g:, 'ale_ignore_2_4_warnings') - execute 'echom ''ale#engine#ManageFile is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.''' - endif - - call ale#command#ManageFile(a:buffer, a:filename) -endfunction - -" Same as the above, but manage an entire directory. -function! ale#engine#ManageDirectory(buffer, directory) abort - if !get(g:, 'ale_ignore_2_4_warnings') - execute 'echom ''ale#engine#ManageDirectory is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.''' - endif - - call ale#command#ManageDirectory(a:buffer, a:directory) -endfunction - -function! ale#engine#CreateFile(buffer) abort - if !get(g:, 'ale_ignore_2_4_warnings') - execute 'echom ''ale#engine#CreateFile is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.''' - endif - - return ale#command#CreateFile(a:buffer) -endfunction - -" Create a new temporary directory and manage it in one go. -function! ale#engine#CreateDirectory(buffer) abort - if !get(g:, 'ale_ignore_2_4_warnings') - execute 'echom ''ale#engine#CreateDirectory is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.''' - endif - - return ale#command#CreateDirectory(a:buffer) -endfunction - function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort let l:info = get(g:ale_buffer_info, a:buffer, {}) @@ -192,7 +156,6 @@ function! s:HandleExit(job_info, buffer, output, data) abort let l:linter = a:job_info.linter let l:executable = a:job_info.executable - let l:next_chain_index = a:job_info.next_chain_index " Remove this job from the list. call ale#engine#MarkLinterInactive(l:buffer_info, l:linter.name) @@ -207,20 +170,6 @@ function! s:HandleExit(job_info, buffer, output, data) abort call remove(a:output, -1) endif - if l:next_chain_index < len(get(l:linter, 'command_chain', [])) - let [l:command, l:options] = ale#engine#ProcessChain( - \ a:buffer, - \ l:executable, - \ l:linter, - \ l:next_chain_index, - \ a:output, - \) - - call s:RunJob(l:command, l:options) - - return - endif - try let l:loclist = ale#util#GetFunction(l:linter.callback)(a:buffer, a:output) " Handle the function being unknown, or being deleted. @@ -454,20 +403,18 @@ function! s:RunJob(command, options) abort let l:buffer = a:options.buffer let l:linter = a:options.linter let l:output_stream = a:options.output_stream - let l:next_chain_index = a:options.next_chain_index let l:read_buffer = a:options.read_buffer let l:info = g:ale_buffer_info[l:buffer] let l:Callback = function('s:HandleExit', [{ \ 'linter': l:linter, \ 'executable': l:executable, - \ 'next_chain_index': l:next_chain_index, \}]) let l:result = ale#command#Run(l:buffer, l:command, l:Callback, { \ 'output_stream': l:output_stream, \ 'executable': l:executable, \ 'read_buffer': l:read_buffer, - \ 'log_output': l:next_chain_index >= len(get(l:linter, 'command_chain', [])), + \ 'log_output': 1, \}) " Only proceed if the job is being run. @@ -482,68 +429,6 @@ function! s:RunJob(command, options) abort return 1 endfunction -" Determine which commands to run for a link in a command chain, or -" just a regular command. -function! ale#engine#ProcessChain(buffer, executable, linter, chain_index, input) abort - let l:output_stream = get(a:linter, 'output_stream', 'stdout') - let l:read_buffer = a:linter.read_buffer - let l:chain_index = a:chain_index - let l:input = a:input - - while l:chain_index < len(a:linter.command_chain) - " Run a chain of commands, one asynchronous command after the other, - " so that many programs can be run in a sequence. - let l:chain_item = a:linter.command_chain[l:chain_index] - - if l:chain_index == 0 - " The first callback in the chain takes only a buffer number. - let l:command = ale#util#GetFunction(l:chain_item.callback)( - \ a:buffer - \) - else - " The second callback in the chain takes some input too. - let l:command = ale#util#GetFunction(l:chain_item.callback)( - \ a:buffer, - \ l:input - \) - endif - - " If we have a command to run, execute that. - if !empty(l:command) - " The chain item can override the output_stream option. - if has_key(l:chain_item, 'output_stream') - let l:output_stream = l:chain_item.output_stream - endif - - " The chain item can override the read_buffer option. - if has_key(l:chain_item, 'read_buffer') - let l:read_buffer = l:chain_item.read_buffer - elseif l:chain_index != len(a:linter.command_chain) - 1 - " Don't read the buffer for commands besides the last one - " in the chain by default. - let l:read_buffer = 0 - endif - - break - endif - - " Command chain items can return an empty string to indicate that - " a command should be skipped, so we should try the next item - " with no input. - let l:input = [] - let l:chain_index += 1 - endwhile - - return [l:command, { - \ 'executable': a:executable, - \ 'buffer': a:buffer, - \ 'linter': a:linter, - \ 'output_stream': l:output_stream, - \ 'next_chain_index': l:chain_index + 1, - \ 'read_buffer': l:read_buffer, - \}] -endfunction - function! s:StopCurrentJobs(buffer, clear_lint_file_jobs) abort let l:info = get(g:ale_buffer_info, a:buffer, {}) call ale#command#StopJobs(a:buffer, 'linter') @@ -622,25 +507,12 @@ function! s:RunIfExecutable(buffer, linter, executable) abort let l:job_type = a:linter.lint_file ? 'file_linter' : 'linter' call setbufvar(a:buffer, 'ale_job_type', l:job_type) - if has_key(a:linter, 'command_chain') - let [l:command, l:options] = ale#engine#ProcessChain( - \ a:buffer, - \ a:executable, - \ a:linter, - \ 0, - \ [] - \) - - return s:RunJob(l:command, l:options) - endif - let l:command = ale#linter#GetCommand(a:buffer, a:linter) let l:options = { \ 'executable': a:executable, \ 'buffer': a:buffer, \ 'linter': a:linter, \ 'output_stream': get(a:linter, 'output_stream', 'stdout'), - \ 'next_chain_index': 1, \ 'read_buffer': a:linter.read_buffer, \} diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index 69817b36..b2a39444 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -90,7 +90,6 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort let l:output = a:job_output endif - let l:ChainCallback = get(a:job_info, 'chain_with', v:null) let l:ProcessWith = get(a:job_info, 'process_with', v:null) " Post-process the output with a function if we have one. @@ -102,27 +101,18 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort " otherwise skip this job and use the input from before. " " We'll use the input from before for chained commands. - if l:ChainCallback is v:null && !empty(split(join(l:output))) + if !empty(split(join(l:output))) let l:input = l:output else let l:input = a:job_info.input endif - if l:ChainCallback isnot v:null && !get(g:, 'ale_ignore_2_4_warnings') - execute 'echom ''chain_with is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.''' - endif - - let l:next_index = l:ChainCallback is v:null - \ ? a:job_info.callback_index + 1 - \ : a:job_info.callback_index - call s:RunFixer({ \ 'buffer': a:buffer, \ 'input': l:input, \ 'output': l:output, \ 'callback_list': a:job_info.callback_list, - \ 'callback_index': l:next_index, - \ 'chain_callback': l:ChainCallback, + \ 'callback_index': a:job_info.callback_index + 1, \}) endfunction @@ -152,17 +142,14 @@ function! s:RunJob(result, options) abort endif let l:command = get(a:result, 'command', '') - let l:ChainWith = get(a:result, 'chain_with', v:null) if empty(l:command) - " If the command is empty, skip to the next item, or call the - " chain_with function. + " If the command is empty, skip to the next item. call s:RunFixer({ \ 'buffer': l:buffer, \ 'input': l:input, - \ 'callback_index': a:options.callback_index + (l:ChainWith is v:null), + \ 'callback_index': a:options.callback_index, \ 'callback_list': a:options.callback_list, - \ 'chain_callback': l:ChainWith, \ 'output': [], \}) @@ -170,8 +157,7 @@ function! s:RunJob(result, options) abort endif let l:read_temporary_file = get(a:result, 'read_temporary_file', 0) - " Default to piping the buffer for the last fixer in the chain. - let l:read_buffer = get(a:result, 'read_buffer', l:ChainWith is v:null) + let l:read_buffer = get(a:result, 'read_buffer', 1) let l:output_stream = get(a:result, 'output_stream', 'stdout') if l:read_temporary_file @@ -180,7 +166,6 @@ function! s:RunJob(result, options) abort let l:Callback = function('s:HandleExit', [{ \ 'input': l:input, - \ 'chain_with': l:ChainWith, \ 'callback_index': a:options.callback_index, \ 'callback_list': a:options.callback_list, \ 'process_with': get(a:result, 'process_with', v:null), diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 0e935149..fecfeed6 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -77,10 +77,6 @@ function! s:IsBoolean(value) abort return type(a:value) is v:t_number && (a:value == 0 || a:value == 1) endfunction -function! s:LanguageGetter(buffer) dict abort - return l:self.language -endfunction - function! ale#linter#PreProcess(filetype, linter) abort if type(a:linter) isnot v:t_dict throw 'The linter object must be a Dictionary' @@ -114,14 +110,7 @@ function! ale#linter#PreProcess(filetype, linter) abort if !l:needs_executable if has_key(a:linter, 'executable') - \|| has_key(a:linter, 'executable_callback') - throw '`executable` and `executable_callback` cannot be used when lsp == ''socket''' - endif - elseif has_key(a:linter, 'executable_callback') - let l:obj.executable_callback = a:linter.executable_callback - - if !s:IsCallback(l:obj.executable_callback) - throw '`executable_callback` must be a callback if defined' + throw '`executable` cannot be used when lsp == ''socket''' endif elseif has_key(a:linter, 'executable') let l:obj.executable = a:linter.executable @@ -131,54 +120,12 @@ function! ale#linter#PreProcess(filetype, linter) abort throw '`executable` must be a String or Function if defined' endif else - throw 'Either `executable` or `executable_callback` must be defined' + throw '`executable` must be defined' endif if !l:needs_command if has_key(a:linter, 'command') - \|| has_key(a:linter, 'command_callback') - \|| has_key(a:linter, 'command_chain') - throw '`command` and `command_callback` and `command_chain` cannot be used when lsp == ''socket''' - endif - elseif has_key(a:linter, 'command_chain') - let l:obj.command_chain = a:linter.command_chain - - if type(l:obj.command_chain) isnot v:t_list - throw '`command_chain` must be a List' - endif - - if empty(l:obj.command_chain) - throw '`command_chain` must contain at least one item' - endif - - let l:link_index = 0 - - for l:link in l:obj.command_chain - let l:err_prefix = 'The `command_chain` item ' . l:link_index . ' ' - - if !s:IsCallback(get(l:link, 'callback')) - throw l:err_prefix . 'must define a `callback` function' - endif - - if has_key(l:link, 'output_stream') - if type(l:link.output_stream) isnot v:t_string - \|| index(['stdout', 'stderr', 'both'], l:link.output_stream) < 0 - throw l:err_prefix . '`output_stream` flag must be ' - \ . "'stdout', 'stderr', or 'both'" - endif - endif - - if has_key(l:link, 'read_buffer') && !s:IsBoolean(l:link.read_buffer) - throw l:err_prefix . 'value for `read_buffer` must be `0` or `1`' - endif - - let l:link_index += 1 - endfor - elseif has_key(a:linter, 'command_callback') - let l:obj.command_callback = a:linter.command_callback - - if !s:IsCallback(l:obj.command_callback) - throw '`command_callback` must be a callback if defined' + throw '`command` cannot be used when lsp == ''socket''' endif elseif has_key(a:linter, 'command') let l:obj.command = a:linter.command @@ -188,22 +135,12 @@ function! ale#linter#PreProcess(filetype, linter) abort throw '`command` must be a String or Function if defined' endif else - throw 'Either `command`, `executable_callback`, `command_chain` ' - \ . 'must be defined' - endif - - if ( - \ has_key(a:linter, 'command') - \ + has_key(a:linter, 'command_chain') - \ + has_key(a:linter, 'command_callback') - \) > 1 - throw 'Only one of `command`, `command_callback`, or `command_chain` ' - \ . 'should be set' + throw '`command` must be defined' endif if !l:needs_address - if has_key(a:linter, 'address') || has_key(a:linter, 'address_callback') - throw '`address` or `address_callback` cannot be used when lsp != ''socket''' + if has_key(a:linter, 'address') + throw '`address` cannot be used when lsp != ''socket''' endif elseif has_key(a:linter, 'address') if type(a:linter.address) isnot v:t_string @@ -212,41 +149,17 @@ function! ale#linter#PreProcess(filetype, linter) abort endif let l:obj.address = a:linter.address - elseif has_key(a:linter, 'address_callback') - let l:obj.address_callback = a:linter.address_callback - - if !s:IsCallback(l:obj.address_callback) - throw '`address_callback` must be a callback if defined' - endif else - throw '`address` or `address_callback` must be defined for getting the LSP address' + throw '`address` must be defined for getting the LSP address' endif if l:needs_lsp_details - if has_key(a:linter, 'language_callback') - if has_key(a:linter, 'language') - throw 'Only one of `language` or `language_callback` ' - \ . 'should be set' - endif - - let l:obj.language_callback = get(a:linter, 'language_callback') + " Default to using the filetype as the language. + let l:obj.language = get(a:linter, 'language', a:filetype) - if !s:IsCallback(l:obj.language_callback) - throw '`language_callback` must be a callback for LSP linters' - endif - else - " Default to using the filetype as the language. - let l:Language = get(a:linter, 'language', a:filetype) - - if type(l:Language) is v:t_string - " Make 'language_callback' return the 'language' value. - let l:obj.language = l:Language - let l:obj.language_callback = function('s:LanguageGetter') - elseif type(l:Language) is v:t_func - let l:obj.language_callback = l:Language - else - throw '`language` must be a String or Funcref' - endif + if type(l:obj.language) isnot v:t_string + \&& type(l:obj.language) isnot v:t_func + throw '`language` must be a String or Funcref if defined' endif if has_key(a:linter, 'project_root') @@ -254,16 +167,10 @@ function! ale#linter#PreProcess(filetype, linter) abort if type(l:obj.project_root) isnot v:t_string \&& type(l:obj.project_root) isnot v:t_func - throw '`project_root` must be a String or Function if defined' - endif - elseif has_key(a:linter, 'project_root_callback') - let l:obj.project_root_callback = a:linter.project_root_callback - - if !s:IsCallback(l:obj.project_root_callback) - throw '`project_root_callback` must be a callback if defined' + throw '`project_root` must be a String or Function' endif else - throw '`project_root` or `project_root_callback` must be defined for LSP linters' + throw '`project_root` must be defined for LSP linters' endif if has_key(a:linter, 'completion_filter') @@ -274,37 +181,16 @@ function! ale#linter#PreProcess(filetype, linter) abort endif endif - if has_key(a:linter, 'initialization_options_callback') - if has_key(a:linter, 'initialization_options') - throw 'Only one of `initialization_options` or ' - \ . '`initialization_options_callback` should be set' - endif - - let l:obj.initialization_options_callback = a:linter.initialization_options_callback - - if !s:IsCallback(l:obj.initialization_options_callback) - throw '`initialization_options_callback` must be a callback if defined' - endif - elseif has_key(a:linter, 'initialization_options') + if has_key(a:linter, 'initialization_options') let l:obj.initialization_options = a:linter.initialization_options if type(l:obj.initialization_options) isnot v:t_dict \&& type(l:obj.initialization_options) isnot v:t_func - throw '`initialization_options` must be a String or Function if defined' + throw '`initialization_options` must be a Dictionary or Function if defined' endif endif - if has_key(a:linter, 'lsp_config_callback') - if has_key(a:linter, 'lsp_config') - throw 'Only one of `lsp_config` or `lsp_config_callback` should be set' - endif - - let l:obj.lsp_config_callback = a:linter.lsp_config_callback - - if !s:IsCallback(l:obj.lsp_config_callback) - throw '`lsp_config_callback` must be a callback if defined' - endif - elseif has_key(a:linter, 'lsp_config') + if has_key(a:linter, 'lsp_config') if type(a:linter.lsp_config) isnot v:t_dict \&& type(a:linter.lsp_config) isnot v:t_func throw '`lsp_config` must be a Dictionary or Function if defined' @@ -347,14 +233,6 @@ function! ale#linter#PreProcess(filetype, linter) abort throw '`aliases` must be a List of String values' endif - for l:key in filter(keys(a:linter), 'v:val[-9:] is# ''_callback'' || v:val is# ''command_chain''') - if !get(g:, 'ale_ignore_2_4_warnings') - execute 'echom l:key . '' is deprecated. Use `let g:ale_ignore_2_4_warnings = 1` to disable this message.''' - endif - - break - endfor - return l:obj endfunction @@ -522,9 +400,7 @@ endfunction " Given a buffer and linter, get the executable String for the linter. function! ale#linter#GetExecutable(buffer, linter) abort - let l:Executable = has_key(a:linter, 'executable_callback') - \ ? function(a:linter.executable_callback) - \ : a:linter.executable + let l:Executable = a:linter.executable return type(l:Executable) is v:t_func \ ? l:Executable(a:buffer) @@ -532,24 +408,21 @@ function! ale#linter#GetExecutable(buffer, linter) abort endfunction " Given a buffer and linter, get the command String for the linter. -" The command_chain key is not supported. function! ale#linter#GetCommand(buffer, linter) abort - let l:Command = has_key(a:linter, 'command_callback') - \ ? function(a:linter.command_callback) - \ : a:linter.command + let l:Command = a:linter.command - return type(l:Command) is v:t_func - \ ? l:Command(a:buffer) - \ : l:Command + return type(l:Command) is v:t_func ? l:Command(a:buffer) : l:Command endfunction " Given a buffer and linter, get the address for connecting to the server. function! ale#linter#GetAddress(buffer, linter) abort - let l:Address = has_key(a:linter, 'address_callback') - \ ? function(a:linter.address_callback) - \ : a:linter.address + let l:Address = a:linter.address + + return type(l:Address) is v:t_func ? l:Address(a:buffer) : l:Address +endfunction + +function! ale#linter#GetLanguage(buffer, linter) abort + let l:Language = a:linter.language - return type(l:Address) is v:t_func - \ ? l:Address(a:buffer) - \ : l:Address + return type(l:Language) is v:t_func ? l:Language(a:buffer) : l:Language endfunction diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index e4148ceb..f4a42bf4 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -227,7 +227,7 @@ function! ale#lsp_linter#OnInit(linter, details, Callback) abort let l:command = a:details.command let l:config = ale#lsp_linter#GetConfig(l:buffer, a:linter) - let l:language_id = ale#util#GetFunction(a:linter.language_callback)(l:buffer) + let l:language_id = ale#linter#GetLanguage(l:buffer, a:linter) call ale#lsp#UpdateConfig(l:conn_id, l:buffer, l:config) diff --git a/autoload/ale/test.vim b/autoload/ale/test.vim index 082d91ff..6fcbf35e 100644 --- a/autoload/ale/test.vim +++ b/autoload/ale/test.vim @@ -145,8 +145,8 @@ function! ale#test#WaitForJobs(deadline) abort " end, but before handlers are run. sleep 10ms - " We must check the buffer data again to see if new jobs started - " for command_chain linters. + " We must check the buffer data again to see if new jobs started for + " linters with chained commands. let l:has_new_jobs = 0 " Check again to see if any jobs are running. -- cgit v1.2.3 From 361027eac6b8a3e9e4e88d6ec7f13e17e909f641 Mon Sep 17 00:00:00 2001 From: w0rp Date: Wed, 19 Aug 2020 01:04:08 +0100 Subject: Fix #3200 - Do not use -fstack-usage from parsed flags --- autoload/ale/c.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index be54cff1..aefe1fea 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -159,7 +159,7 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort elseif (stridx(l:option, '-W') == 0 && stridx(l:option, '-Wa,') != 0 && stridx(l:option, '-Wl,') != 0 && stridx(l:option, '-Wp,') != 0) \ || l:option is# '-w' || stridx(l:option, '-pedantic') == 0 \ || l:option is# '-ansi' || stridx(l:option, '-std=') == 0 - \ || (stridx(l:option, '-f') == 0 && stridx(l:option, '-fdump') != 0 && stridx(l:option, '-fdiagnostics') != 0 && stridx(l:option, '-fno-show-column') != 0) + \ || stridx(l:option, '-f') == 0 && l:option !~# '\v^-f(dump|diagnostics|no-show-column|stack-usage)' \ || stridx(l:option, '-O') == 0 \ || l:option is# '-C' || l:option is# '-CC' || l:option is# '-trigraphs' \ || stridx(l:option, '-nostdinc') == 0 || stridx(l:option, '-iplugindir=') == 0 -- cgit v1.2.3 From de7b0567b11555005a9b2ec051327891bcb8f2b4 Mon Sep 17 00:00:00 2001 From: w0rp Date: Wed, 19 Aug 2020 01:10:33 +0100 Subject: Try to fix tests on Windows --- autoload/ale/c.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index aefe1fea..fee993ab 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -173,7 +173,7 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort endfunction function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort - if !g:ale_c_parse_makefile + if !get(g:, 'ale_c_parse_makefile', 0) return v:null endif -- cgit v1.2.3 From 90abb7e7efbb9d7cbd3bfcb2beceed08bb901ebe Mon Sep 17 00:00:00 2001 From: w0rp Date: Wed, 19 Aug 2020 01:22:43 +0100 Subject: Try to fix Windows tests again --- autoload/ale/c.vim | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index fee993ab..0ec724b3 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -8,6 +8,14 @@ let s:sep = has('win32') ? '\' : '/' " Set just so tests can override it. let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt'] +function! s:CanParseMakefile(buffer) abort + " Something somewhere seems to delete this setting in tests, so ensure we + " always have a default value. + call ale#Set('c_parse_makefile', 0) + + return ale#Var(a:buffer, 'c_parse_makefile') +endfunction + function! ale#c#GetBuildDirectory(buffer) abort let l:build_dir = ale#Var(a:buffer, 'c_build_dir') @@ -173,7 +181,7 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort endfunction function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort - if !get(g:, 'ale_c_parse_makefile', 0) + if !s:CanParseMakefile(a:buffer) return v:null endif @@ -383,9 +391,7 @@ function! ale#c#GetCFlags(buffer, output) abort endif endif - if ale#Var(a:buffer, 'c_parse_makefile') - \&& !empty(a:output) - \&& !empty(l:cflags) + if s:CanParseMakefile(a:buffer) && !empty(a:output) && !empty(l:cflags) let l:cflags = ale#c#ParseCFlagsFromMakeOutput(a:buffer, a:output) endif @@ -397,7 +403,7 @@ function! ale#c#GetCFlags(buffer, output) abort endfunction function! ale#c#GetMakeCommand(buffer) abort - if ale#Var(a:buffer, 'c_parse_makefile') + if s:CanParseMakefile(a:buffer) let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') if !empty(l:makefile_path) -- cgit v1.2.3 From 4d42ebc160d7cccd19c37ffe2ccb97752794be37 Mon Sep 17 00:00:00 2001 From: awang Date: Wed, 19 Aug 2020 23:09:02 +0000 Subject: Keep -iframework if present in parsed C/C++ flags (#3057) * Keep -iframework if present in parsed C/C++ flags * Add test to make sure -iframework is parsed Co-authored-by: Alex Wang --- autoload/ale/c.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 0ec724b3..5cf4c690 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -132,6 +132,7 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort \ || stridx(l:option, '-iquote') == 0 \ || stridx(l:option, '-isystem') == 0 \ || stridx(l:option, '-idirafter') == 0 + \ || stridx(l:option, '-iframework') == 0 if stridx(l:option, '-I') == 0 && l:option isnot# '-I' let l:arg = join(split(l:option, '\zs')[2:], '') let l:option = '-I' -- cgit v1.2.3 From 4bece27bd4f7a5645235b308b121fdae01fb3ab1 Mon Sep 17 00:00:00 2001 From: Khaveesh N Date: Fri, 21 Aug 2020 17:19:22 +0530 Subject: refactor(Fixer): Change latexindent to read from stdin instead of temporary file This is a better strategy as it avoids creating temporary files and the delay that follows --- autoload/ale/fixers/latexindent.vim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/fixers/latexindent.vim b/autoload/ale/fixers/latexindent.vim index b0a0884a..54f1231e 100644 --- a/autoload/ale/fixers/latexindent.vim +++ b/autoload/ale/fixers/latexindent.vim @@ -10,9 +10,7 @@ function! ale#fixers#latexindent#Fix(buffer) abort return { \ 'command': ale#Escape(l:executable) - \ . ' -l -w' + \ . ' -l' \ . (empty(l:options) ? '' : ' ' . l:options) - \ . ' %t', - \ 'read_temporary_file': 1, \} endfunction -- cgit v1.2.3 From ba3dd0d02735e7b23918200ae58410b0deed2f62 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 23 Aug 2020 19:55:42 +0100 Subject: Close #2556 - Support filename mapping ALE now supports mapping files between different systems for running linters and fixers with Docker, in virtual machines, in servers, etc. --- autoload/ale.vim | 20 ++++++++++++++++++++ autoload/ale/command.vim | 26 ++++++++++++++++++++++--- autoload/ale/engine.vim | 20 +++++++++++++++++--- autoload/ale/filename_mapping.vim | 22 +++++++++++++++++++++ autoload/ale/fix.vim | 40 ++++++++++++++++++++------------------- autoload/ale/fix/registry.vim | 4 ++-- autoload/ale/lsp_linter.vim | 9 ++++++++- autoload/ale/path.vim | 2 +- 8 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 autoload/ale/filename_mapping.vim (limited to 'autoload') diff --git a/autoload/ale.vim b/autoload/ale.vim index 6251b47b..b75c9fc9 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -266,3 +266,23 @@ function! ale#GetLocItemMessage(item, format_string) abort return l:msg endfunction + +" Given a buffer and a linter or fixer name, return an Array of two-item +" Arrays describing how to map filenames to and from the local to foreign file +" systems. +function! ale#GetFilenameMappings(buffer, name) abort + let l:linter_mappings = ale#Var(a:buffer, 'filename_mappings') + + if type(l:linter_mappings) is v:t_list + return l:linter_mappings + endif + + let l:name = a:name + + if !has_key(l:linter_mappings, l:name) + " Use * as a default setting for all tools. + let l:name = '*' + endif + + return get(l:linter_mappings, l:name, []) +endfunction diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim index 1bbc4f4c..ec264a36 100644 --- a/autoload/ale/command.vim +++ b/autoload/ale/command.vim @@ -133,11 +133,30 @@ function! ale#command#EscapeCommandPart(command_part) abort return substitute(a:command_part, '%', '%%', 'g') endfunction +" Format a filename, converting it with filename mappings, if non-empty, +" and escaping it for putting into a command string. +function! s:FormatFilename(filename, mappings) abort + let l:filename = a:filename + + if !empty(a:mappings) + let l:filename = ale#filename_mapping#Map(l:filename, a:mappings) + endif + + return ale#Escape(l:filename) +endfunction + " Given a command string, replace every... " %s -> with the current filename " %t -> with the name of an unused file in a temporary directory " %% -> with a literal % -function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_needed, input) abort +function! ale#command#FormatCommand( +\ buffer, +\ executable, +\ command, +\ pipe_file_if_needed, +\ input, +\ filename_mappings, +\) abort let l:temporary_file = '' let l:command = a:command @@ -154,14 +173,14 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne " file. if l:command =~# '%s' let l:filename = fnamemodify(bufname(a:buffer), ':p') - let l:command = substitute(l:command, '%s', '\=ale#Escape(l:filename)', 'g') + let l:command = substitute(l:command, '%s', '\=s:FormatFilename(l:filename, a:filename_mappings)', 'g') endif if a:input isnot v:false && l:command =~# '%t' " Create a temporary filename, / " The file itself will not be created by this function. let l:temporary_file = s:TemporaryFilename(a:buffer) - let l:command = substitute(l:command, '%t', '\=ale#Escape(l:temporary_file)', 'g') + let l:command = substitute(l:command, '%t', '\=s:FormatFilename(l:temporary_file, a:filename_mappings)', 'g') endif " Finish formatting so %% becomes %. @@ -265,6 +284,7 @@ function! ale#command#Run(buffer, command, Callback, ...) abort \ a:command, \ get(l:options, 'read_buffer', 0), \ get(l:options, 'input', v:null), + \ get(l:options, 'filename_mappings', []), \) let l:command = ale#job#PrepareCommand(a:buffer, l:command) let l:job_options = { diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index cfc1e5d7..3f4f9338 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -256,6 +256,13 @@ function! s:RemapItemTypes(type_map, loclist) abort endfunction function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) abort + let l:mappings = ale#GetFilenameMappings(a:buffer, a:linter_name) + + if !empty(l:mappings) + " We need to apply reverse filename mapping here. + let l:mappings = ale#filename_mapping#Invert(l:mappings) + endif + let l:bufnr_map = {} let l:new_loclist = [] @@ -296,13 +303,19 @@ function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) let l:item.code = l:old_item.code endif - if has_key(l:old_item, 'filename') - \&& !ale#path#IsTempName(l:old_item.filename) + let l:old_name = get(l:old_item, 'filename', '') + + " Map parsed from output to local filesystem files. + if !empty(l:old_name) && !empty(l:mappings) + let l:old_name = ale#filename_mapping#Map(l:old_name, l:mappings) + endif + + if !empty(l:old_name) && !ale#path#IsTempName(l:old_name) " Use the filename given. " Temporary files are assumed to be for this buffer, " and the filename is not included then, because it looks bad " in the loclist window. - let l:filename = l:old_item.filename + let l:filename = l:old_name let l:item.filename = l:filename if has_key(l:old_item, 'bufnr') @@ -415,6 +428,7 @@ function! s:RunJob(command, options) abort \ 'executable': l:executable, \ 'read_buffer': l:read_buffer, \ 'log_output': 1, + \ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:linter.name), \}) " Only proceed if the job is being run. diff --git a/autoload/ale/filename_mapping.vim b/autoload/ale/filename_mapping.vim new file mode 100644 index 00000000..76d47acc --- /dev/null +++ b/autoload/ale/filename_mapping.vim @@ -0,0 +1,22 @@ +" Author: w0rp +" Description: Logic for handling mappings between files + +" Invert filesystem mappings so they can be mapped in reverse. +function! ale#filename_mapping#Invert(filename_mappings) abort + return map(copy(a:filename_mappings), '[v:val[1], v:val[0]]') +endfunction + +" Given a filename and some filename_mappings, map a filename. +function! ale#filename_mapping#Map(filename, filename_mappings) abort + let l:simplified_filename = ale#path#Simplify(a:filename) + + for [l:mapping_from, l:mapping_to] in a:filename_mappings + let l:mapping_from = ale#path#Simplify(l:mapping_from) + + if l:simplified_filename[:len(l:mapping_from) - 1] is# l:mapping_from + return l:mapping_to . l:simplified_filename[len(l:mapping_from):] + endif + endfor + + return a:filename +endfunction diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index b2a39444..a53f8626 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -1,4 +1,8 @@ +" Author: w0rp +" Description: Functions for fixing code with programs, or other means. + call ale#Set('fix_on_save_ignore', {}) +call ale#Set('filename_mappings', {}) " Apply fixes queued up for buffers which may be hidden. " Vim doesn't let you modify hidden buffers. @@ -110,7 +114,6 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort call s:RunFixer({ \ 'buffer': a:buffer, \ 'input': l:input, - \ 'output': l:output, \ 'callback_list': a:job_info.callback_list, \ 'callback_index': a:job_info.callback_index + 1, \}) @@ -125,6 +128,7 @@ function! s:RunJob(result, options) abort let l:buffer = a:options.buffer let l:input = a:options.input + let l:fixer_name = a:options.fixer_name if a:result is 0 || type(a:result) is v:t_list if type(a:result) is v:t_list @@ -150,7 +154,6 @@ function! s:RunJob(result, options) abort \ 'input': l:input, \ 'callback_index': a:options.callback_index, \ 'callback_list': a:options.callback_list, - \ 'output': [], \}) return @@ -177,6 +180,7 @@ function! s:RunJob(result, options) abort \ 'read_buffer': l:read_buffer, \ 'input': l:input, \ 'log_output': 0, + \ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:fixer_name), \}) if empty(l:run_result) @@ -200,32 +204,22 @@ function! s:RunFixer(options) abort return endif - let l:ChainCallback = get(a:options, 'chain_callback', v:null) - - let l:Function = l:ChainCallback isnot v:null - \ ? ale#util#GetFunction(l:ChainCallback) - \ : a:options.callback_list[l:index] + let [l:fixer_name, l:Function] = a:options.callback_list[l:index] " Record new jobs started as fixer jobs. call setbufvar(l:buffer, 'ale_job_type', 'fixer') - if l:ChainCallback isnot v:null - " Chained commands accept (buffer, output, [input]) - let l:result = ale#util#FunctionArgCount(l:Function) == 2 - \ ? call(l:Function, [l:buffer, a:options.output]) - \ : call(l:Function, [l:buffer, a:options.output, copy(l:input)]) - else - " Regular fixer commands accept (buffer, [input]) - let l:result = ale#util#FunctionArgCount(l:Function) == 1 - \ ? call(l:Function, [l:buffer]) - \ : call(l:Function, [l:buffer, copy(l:input)]) - endif + " Regular fixer commands accept (buffer, [input]) + let l:result = ale#util#FunctionArgCount(l:Function) == 1 + \ ? call(l:Function, [l:buffer]) + \ : call(l:Function, [l:buffer, copy(l:input)]) call s:RunJob(l:result, { \ 'buffer': l:buffer, \ 'input': l:input, \ 'callback_list': a:options.callback_list, \ 'callback_index': l:index, + \ 'fixer_name': l:fixer_name, \}) endfunction @@ -293,16 +287,24 @@ function! s:GetCallbacks(buffer, fixing_flag, fixers) abort " Variables with capital characters are needed, or Vim will complain about " funcref variables. for l:Item in l:callback_list + " Try to capture the names of registered fixer names, so we can use + " them for filename mapping or other purposes later. + let l:fixer_name = v:null + if type(l:Item) is v:t_string let l:Func = ale#fix#registry#GetFunc(l:Item) if !empty(l:Func) + let l:fixer_name = l:Item let l:Item = l:Func endif endif try - call add(l:corrected_list, ale#util#GetFunction(l:Item)) + call add(l:corrected_list, [ + \ l:fixer_name, + \ ale#util#GetFunction(l:Item) + \]) catch /E475/ " Rethrow exceptions for failing to get a function so we can print " a friendly message about it. diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 94476ca9..2a38945c 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -160,11 +160,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['php'], \ 'description': 'Fix PHP files with php-cs-fixer.', \ }, -\ 'astyle': { +\ 'astyle': { \ 'function': 'ale#fixers#astyle#Fix', \ 'suggested_filetypes': ['c', 'cpp'], \ 'description': 'Fix C/C++ with astyle.', -\ }, +\ }, \ 'clangtidy': { \ 'function': 'ale#fixers#clangtidy#Fix', \ 'suggested_filetypes': ['c', 'cpp', 'objc'], diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index f4a42bf4..db640654 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -265,7 +265,14 @@ function! s:StartLSP(options, address, executable, command) abort call ale#lsp#MarkConnectionAsTsserver(l:conn_id) endif - let l:command = ale#command#FormatCommand(l:buffer, a:executable, a:command, 0, v:false)[1] + let l:command = ale#command#FormatCommand( + \ l:buffer, + \ a:executable, + \ a:command, + \ 0, + \ v:false, + \ [], + \)[1] let l:command = ale#job#PrepareCommand(l:buffer, l:command) let l:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command) endif diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim index 30550503..f18a9733 100644 --- a/autoload/ale/path.vim +++ b/autoload/ale/path.vim @@ -95,7 +95,7 @@ function! ale#path#IsAbsolute(filename) abort return a:filename[:0] is# '/' || a:filename[1:2] is# ':\' endfunction -let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h')) +let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h:h')) " Given a filename, return 1 if the file represents some temporary file " created by Vim. -- cgit v1.2.3 From 3e2abe3f25493af63af91a6013447e378e09f6ec Mon Sep 17 00:00:00 2001 From: w0rp Date: Mon, 24 Aug 2020 09:33:07 +0100 Subject: #2556 - Support modifiers for formatted filenames --- autoload/ale/command.vim | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim index ec264a36..8f497169 100644 --- a/autoload/ale/command.vim +++ b/autoload/ale/command.vim @@ -135,13 +135,19 @@ endfunction " Format a filename, converting it with filename mappings, if non-empty, " and escaping it for putting into a command string. -function! s:FormatFilename(filename, mappings) abort +" +" The filename can be modified. +function! s:FormatFilename(filename, mappings, modifiers) abort let l:filename = a:filename if !empty(a:mappings) let l:filename = ale#filename_mapping#Map(l:filename, a:mappings) endif + if !empty(a:modifiers) + let l:filename = fnamemodify(l:filename, a:modifiers) + endif + return ale#Escape(l:filename) endfunction @@ -155,7 +161,7 @@ function! ale#command#FormatCommand( \ command, \ pipe_file_if_needed, \ input, -\ filename_mappings, +\ mappings, \) abort let l:temporary_file = '' let l:command = a:command @@ -173,14 +179,24 @@ function! ale#command#FormatCommand( " file. if l:command =~# '%s' let l:filename = fnamemodify(bufname(a:buffer), ':p') - let l:command = substitute(l:command, '%s', '\=s:FormatFilename(l:filename, a:filename_mappings)', 'g') + let l:command = substitute( + \ l:command, + \ '\v\%s(%(:h|:t|:r|:e)*)', + \ '\=s:FormatFilename(l:filename, a:mappings, submatch(1))', + \ 'g' + \) endif if a:input isnot v:false && l:command =~# '%t' " Create a temporary filename, / " The file itself will not be created by this function. let l:temporary_file = s:TemporaryFilename(a:buffer) - let l:command = substitute(l:command, '%t', '\=s:FormatFilename(l:temporary_file, a:filename_mappings)', 'g') + let l:command = substitute( + \ l:command, + \ '\v\%t(%(:h|:t|:r|:e)*)', + \ '\=s:FormatFilename(l:temporary_file, a:mappings, submatch(1))', + \ 'g' + \) endif " Finish formatting so %% becomes %. -- cgit v1.2.3 From 447aea4af045b80d63fafe4e65791aa6b7d5b21b Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Tue, 25 Aug 2020 08:57:35 -0400 Subject: Add dhall-format as a Fixer https://github.com/dhall-lang/dhall-lang --- autoload/ale/fix/registry.vim | 5 +++++ autoload/ale/fixers/dhall.vim | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 autoload/ale/fixers/dhall.vim (limited to 'autoload') diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 2a38945c..d71668f2 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -375,6 +375,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['html', 'htmldjango'], \ 'description': 'Fix HTML files with html-beautify.', \ }, +\ 'dhall': { +\ 'function': 'ale#fixers#dhall#Fix', +\ 'suggested_filetypes': ['dhall'], +\ 'description': 'Fix Dhall files with dhall-format.', +\ }, \} " Reset the function registry to the default entries. diff --git a/autoload/ale/fixers/dhall.vim b/autoload/ale/fixers/dhall.vim new file mode 100644 index 00000000..18f6006c --- /dev/null +++ b/autoload/ale/fixers/dhall.vim @@ -0,0 +1,23 @@ +" Author: Pat Brisbin +" Description: Integration of dhall-format with ALE. + +call ale#Set('dhall_format_executable', 'dhall') + +function! ale#fixers#dhall#GetExecutable(buffer) abort + let l:executable = ale#Var(a:buffer, 'dhall_format_executable') + + " Dhall is written in Haskell and commonly installed with Stack + return ale#handlers#haskell_stack#EscapeExecutable(l:executable, 'dhall') +endfunction + +function! ale#fixers#dhall#Fix(buffer) abort + let l:executable = ale#fixers#dhall#GetExecutable(a:buffer) + + return { + \ 'command': l:executable + \ . ' format' + \ . ' --inplace' + \ . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction -- cgit v1.2.3 From 396fba7cca5bb6cd5774173241e3e606045e1c46 Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 08:44:43 +0100 Subject: Fix #3312 - Fix a false positive for auto imports ALE was incorrectly detecting completion results from servers such as rust-analyzer as wanting to add import lines when additionalTextEdits was present, but empty. Now ALE only filters out completion results if the autoimport setting is off, and one of the additionalTextEdits starts on some line other than the current line. If any additionalTextEdits happen to be identical to the change from completion anyway, ALE will skip them. --- autoload/ale/completion.vim | 60 ++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 17 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index a273d4e1..87efd191 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -496,6 +496,18 @@ function! ale#completion#NullFilter(buffer, item) abort return 1 endfunction +" Check if additional text edits make changes starting on lines other than the +" one you're asking for completions on. +function! s:TextEditsChangeOtherLines(line, text_edit_list) abort + for l:edit in a:text_edit_list + if l:edit.range.start.line + 1 isnot a:line + return 1 + endif + endfor + + return 0 +endfunction + function! ale#completion#ParseLSPCompletions(response) abort let l:buffer = bufnr('') let l:info = get(b:, 'ale_completion_info', {}) @@ -540,7 +552,9 @@ function! ale#completion#ParseLSPCompletions(response) abort " Don't use LSP items with additional text edits when autoimport for " completions is turned off. - if has_key(l:item, 'additionalTextEdits') && !g:ale_completion_autoimport + if has_key(l:item, 'additionalTextEdits') + \&& !g:ale_completion_autoimport + \&& s:TextEditsChangeOtherLines(l:info.line, l:item.additionalTextEdits) continue endif @@ -562,31 +576,41 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:text_changes = [] for l:edit in l:item.additionalTextEdits - let l:range = l:edit.range + " Don't apply additional text edits that are identical to the + " word we're going to insert anyway. + if l:edit.newText is# l:word + \&& l:edit.range.start.line + 1 is l:info.line + \&& l:edit.range.end.line + 1 is l:info.line + \&& l:edit.range.start.character is l:edit.range.end.character + continue + endif + call add(l:text_changes, { \ 'start': { - \ 'line': l:range.start.line + 1, - \ 'offset': l:range.start.character + 1, + \ 'line': l:edit.range.start.line + 1, + \ 'offset': l:edit.range.start.character + 1, \ }, \ 'end': { - \ 'line': l:range.end.line + 1, - \ 'offset': l:range.end.character + 1, + \ 'line': l:edit.range.end.line + 1, + \ 'offset': l:edit.range.end.character + 1, \ }, \ 'newText': l:edit.newText, \}) endfor - let l:changes = [{ - \ 'fileName': expand('#' . l:buffer . ':p'), - \ 'textChanges': l:text_changes, - \}] - \ - let l:result.user_data = json_encode({ - \ 'codeActions': [{ - \ 'description': 'completion', - \ 'changes': l:changes, - \ }], - \ }) + if !empty(l:text_changes) + let l:result.user_data = json_encode({ + \ 'codeActions': [{ + \ 'description': 'completion', + \ 'changes': [ + \ { + \ 'fileName': expand('#' . l:buffer . ':p'), + \ 'textChanges': l:text_changes, + \ } + \ ], + \ }], + \}) + endif endif call add(l:results, l:result) @@ -900,6 +924,8 @@ function! ale#completion#Done() abort endfunction augroup ALECompletionActions + autocmd! + autocmd CompleteDone * call ale#completion#HandleUserData(v:completed_item) augroup END -- cgit v1.2.3 From a955f5dfa8811a44c9e871b06b55c0bcad13f0d9 Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 08:57:12 +0100 Subject: #3312 - Just check if additionalTextEdits is non-empty --- autoload/ale/completion.vim | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 87efd191..f6a0c350 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -496,18 +496,6 @@ function! ale#completion#NullFilter(buffer, item) abort return 1 endfunction -" Check if additional text edits make changes starting on lines other than the -" one you're asking for completions on. -function! s:TextEditsChangeOtherLines(line, text_edit_list) abort - for l:edit in a:text_edit_list - if l:edit.range.start.line + 1 isnot a:line - return 1 - endif - endfor - - return 0 -endfunction - function! ale#completion#ParseLSPCompletions(response) abort let l:buffer = bufnr('') let l:info = get(b:, 'ale_completion_info', {}) @@ -552,9 +540,8 @@ function! ale#completion#ParseLSPCompletions(response) abort " Don't use LSP items with additional text edits when autoimport for " completions is turned off. - if has_key(l:item, 'additionalTextEdits') + if !empty(get(l:item, 'additionalTextEdits')) \&& !g:ale_completion_autoimport - \&& s:TextEditsChangeOtherLines(l:info.line, l:item.additionalTextEdits) continue endif @@ -576,15 +563,6 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:text_changes = [] for l:edit in l:item.additionalTextEdits - " Don't apply additional text edits that are identical to the - " word we're going to insert anyway. - if l:edit.newText is# l:word - \&& l:edit.range.start.line + 1 is l:info.line - \&& l:edit.range.end.line + 1 is l:info.line - \&& l:edit.range.start.character is l:edit.range.end.character - continue - endif - call add(l:text_changes, { \ 'start': { \ 'line': l:edit.range.start.line + 1, -- cgit v1.2.3 From f5aa0e84577e583349b8849beae9348a0c017720 Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 11:44:35 +0100 Subject: Fix #3307 - Handle compile_commands paths better ALE now converts paths from compile_commands.json files into absolute paths and prefers matching against absolute file and directory names for determining which flags to use for files. As a result, parsing compile_commands.json to determine flags should work for a lot more C and C++ projects. --- autoload/ale/c.vim | 71 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 16 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 5cf4c690..e6fcb8e6 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -84,10 +84,10 @@ function! ale#c#ExpandAtArgs(path_prefix, raw_split_lines) abort let l:path = join(split(l:option, '\zs')[1:], '') " Make path absolute - if stridx(l:path, s:sep) != 0 && stridx(l:path, '/') != 0 + if !ale#path#IsAbsolute(l:path) let l:rel_path = substitute(l:path, '"', '', 'g') let l:rel_path = substitute(l:rel_path, '''', '', 'g') - let l:path = a:path_prefix . s:sep . l:rel_path + let l:path = ale#path#GetAbsPath(a:path_prefix, l:rel_path) endif " Read the file and add all the arguments @@ -142,10 +142,12 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort endif " Fix relative paths if needed - if stridx(l:arg, s:sep) != 0 && stridx(l:arg, '/') != 0 + if !ale#path#IsAbsolute(l:arg) let l:rel_path = substitute(l:arg, '"', '', 'g') let l:rel_path = substitute(l:rel_path, '''', '', 'g') - let l:arg = ale#Escape(a:path_prefix . s:sep . l:rel_path) + let l:arg = ale#Escape( + \ ale#path#GetAbsPath(a:path_prefix, l:rel_path) + \) endif call add(l:cflags_list, l:option) @@ -268,6 +270,10 @@ if !exists('s:compile_commands_cache') let s:compile_commands_cache = {} endif +function! ale#c#ResetCompileCommandsCache() abort + let s:compile_commands_cache = {} +endfunction + function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort let l:empty = [{}, {}] @@ -298,9 +304,20 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort let l:dir_lookup = {} for l:entry in (type(l:raw_data) is v:t_list ? l:raw_data : []) + let l:filename = ale#path#GetAbsPath(l:entry.directory, l:entry.file) + + " Store a key for lookups by the absolute path to the filename. + let l:file_lookup[l:filename] = get(l:file_lookup, l:filename, []) + [l:entry] + + " Store a key for fuzzy lookups by the absolute path to the directory. + let l:dirname = fnamemodify(l:filename, ':h') + let l:dir_lookup[l:dirname] = get(l:dir_lookup, l:dirname, []) + [l:entry] + + " Store a key for fuzzy lookups by just the basename of the file. let l:basename = tolower(fnamemodify(l:entry.file, ':t')) let l:file_lookup[l:basename] = get(l:file_lookup, l:basename, []) + [l:entry] + " Store a key for fuzzy lookups by just the basename of the directory. let l:dirbasename = tolower(fnamemodify(l:entry.directory, ':p:h:t')) let l:dir_lookup[l:dirbasename] = get(l:dir_lookup, l:dirbasename, []) + [l:entry] endfor @@ -326,17 +343,41 @@ function! ale#c#GetCompileCommand(json_item) abort endfunction function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort + let l:buffer_filename = ale#path#Simplify(expand('#' . a:buffer . ':p')) + let l:basename = tolower(fnamemodify(l:buffer_filename, ':t')) + " Look for any file in the same directory if we can't find an exact match. + let l:dir = fnamemodify(l:buffer_filename, ':h') + " Search for an exact file match first. - let l:basename = tolower(expand('#' . a:buffer . ':t')) - let l:file_list = get(a:file_lookup, l:basename, []) + let l:file_list = get(a:file_lookup, l:buffer_filename, []) + " Try the absolute path to the directory second. + let l:dir_list = get(a:dir_lookup, l:dir, []) + + if empty(l:file_list) && empty(l:dir_list) + " If we can't find matches with the path to the file, try a + " case-insensitive match for any similarly-named file. + let l:file_list = get(a:file_lookup, l:basename, []) + + " If we can't find matches with the path to the directory, try a + " case-insensitive match for anything in similarly-named directory. + let l:dir_list = get(a:dir_lookup, tolower(fnamemodify(l:dir, ':t')), []) + endif + " A source file matching the header filename. let l:source_file = '' if empty(l:file_list) && l:basename =~? '\.h$\|\.hpp$' for l:suffix in ['.c', '.cpp'] - let l:key = fnamemodify(l:basename, ':r') . l:suffix + " Try to find a source file by an absolute path first. + let l:key = fnamemodify(l:buffer_filename, ':r') . l:suffix let l:file_list = get(a:file_lookup, l:key, []) + if empty(l:file_list) + " Look fuzzy matches on the basename second. + let l:key = fnamemodify(l:basename, ':r') . l:suffix + let l:file_list = get(a:file_lookup, l:key, []) + endif + if !empty(l:file_list) let l:source_file = l:key break @@ -345,27 +386,25 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort endif for l:item in l:file_list + let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file) + " Load the flags for this file, or for a source file matching the " header file. if ( - \ bufnr(l:item.file) is a:buffer + \ bufnr(l:filename) is a:buffer \ || ( \ !empty(l:source_file) - \ && l:item.file[-len(l:source_file):] is? l:source_file + \ && l:filename[-len(l:source_file):] is? l:source_file \ ) \) return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item)) endif endfor - " Look for any file in the same directory if we can't find an exact match. - let l:dir = ale#path#Simplify(expand('#' . a:buffer . ':p:h')) - - let l:dirbasename = tolower(expand('#' . a:buffer . ':p:h:t')) - let l:dir_list = get(a:dir_lookup, l:dirbasename, []) - for l:item in l:dir_list - if ale#path#Simplify(fnamemodify(l:item.file, ':h')) is? l:dir + let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file) + + if ale#path#Simplify(fnamemodify(l:filename, ':h')) is? l:dir return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item)) endif endfor -- cgit v1.2.3 From 66ff00c4204095e65d8547c86e94325b205365ea Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 12:41:07 +0100 Subject: Fix #3316 - Repeat -relative for ALERepeatSelection --- autoload/ale/preview.vim | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/preview.vim b/autoload/ale/preview.vim index faf45cb0..8b94aa7a 100644 --- a/autoload/ale/preview.vim +++ b/autoload/ale/preview.vim @@ -1,14 +1,22 @@ " Author: w0rp " Description: Preview windows for showing whatever information in. -if !has_key(s:, 'last_selection_list') - let s:last_selection_list = [] +if !has_key(s:, 'last__list') + let s:last_list = [] endif -if !has_key(s:, 'last_selection_open_in') - let s:last_selection_open_in = 'current-buffer' +if !has_key(s:, 'last_options') + let s:last_options = {} endif +function! ale#preview#SetLastSelection(item_list, options) abort + let s:last_list = a:item_list + let s:last_options = { + \ 'open_in': get(a:options, 'open_in', 'current-buffer'), + \ 'use_relative_paths': get(a:options, 'use_relative_paths', 0), + \} +endfunction + " Open a preview window and show some lines in it. " A second argument can be passed as a Dictionary with options. They are... " @@ -81,19 +89,14 @@ function! ale#preview#ShowSelection(item_list, ...) abort let b:ale_preview_item_list = a:item_list let b:ale_preview_item_open_in = get(l:options, 'open_in', 'current-buffer') - " Remove the last preview - let s:last_selection_list = b:ale_preview_item_list - let s:last_selection_open_in = b:ale_preview_item_open_in + " Remember preview state, so we can repeat it later. + call ale#preview#SetLastSelection(a:item_list, l:options) endfunction function! ale#preview#RepeatSelection() abort - if empty(s:last_selection_list) - return + if !empty(s:last_list) + call ale#preview#ShowSelection(s:last_list, s:last_options) endif - - call ale#preview#ShowSelection(s:last_selection_list, { - \ 'open_in': s:last_selection_open_in, - \}) endfunction function! s:Open(open_in) abort -- cgit v1.2.3 From 17605777d6cbe4a9eba5d31c799e73a6672f59cf Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 13:05:50 +0100 Subject: Fix #3317 - Parse -include from C flags --- autoload/ale/c.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index e6fcb8e6..6f18ce4c 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -133,6 +133,7 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort \ || stridx(l:option, '-isystem') == 0 \ || stridx(l:option, '-idirafter') == 0 \ || stridx(l:option, '-iframework') == 0 + \ || stridx(l:option, '-include') == 0 if stridx(l:option, '-I') == 0 && l:option isnot# '-I' let l:arg = join(split(l:option, '\zs')[2:], '') let l:option = '-I' -- cgit v1.2.3 From 719f3c62b0e6a5500deaee456f71596f44d7e779 Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 13:57:14 +0100 Subject: #3266 - Catch echo visual selection errors --- autoload/ale/cursor.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'autoload') diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim index 8c331c5c..9ca6fb15 100644 --- a/autoload/ale/cursor.vim +++ b/autoload/ale/cursor.vim @@ -39,6 +39,8 @@ function! ale#cursor#TruncatedEcho(original_message) abort endif exec 'echomsg l:message' + catch /E481/ + " Do nothing if running from a visual selection. endtry " Reset the cursor position if we moved off the end of the line. -- cgit v1.2.3 From af177d7825a3e78cc2022710d61eff8a685832ec Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 19:33:43 +0100 Subject: #3318 Refactor C flag parsing to set up for quoting arguments --- autoload/ale/c.vim | 64 +++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 30 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 6f18ce4c..eac9879c 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -8,6 +8,11 @@ let s:sep = has('win32') ? '\' : '/' " Set just so tests can override it. let g:__ale_c_project_filenames = ['.git/HEAD', 'configure', 'Makefile', 'CMakeLists.txt'] +let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [ +\ 'build', +\ 'bin', +\]) + function! s:CanParseMakefile(buffer) abort " Something somewhere seems to delete this setting in tests, so ensure we " always have a default value. @@ -115,16 +120,14 @@ function! ale#c#ExpandAtArgs(path_prefix, raw_split_lines) abort return l:out_lines endfunction -function! ale#c#ParseCFlags(path_prefix, cflag_line) abort - let l:cflags_list = [] - - let l:raw_split_lines = ale#c#ShellSplit(a:cflag_line) +function! ale#c#ParseCFlags(path_prefix, should_quote, raw_arguments) abort " Expand @file arguments now before parsing - let l:split_lines = ale#c#ExpandAtArgs(a:path_prefix, l:raw_split_lines) + let l:arguments = ale#c#ExpandAtArgs(a:path_prefix, a:raw_arguments) + let l:arguments_to_use = [] let l:option_index = 0 - while l:option_index < len(l:split_lines) - let l:option = l:split_lines[l:option_index] + while l:option_index < len(l:arguments) + let l:option = l:arguments[l:option_index] let l:option_index = l:option_index + 1 " Include options, that may need relative path fix @@ -138,7 +141,7 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort let l:arg = join(split(l:option, '\zs')[2:], '') let l:option = '-I' else - let l:arg = l:split_lines[l:option_index] + let l:arg = l:arguments[l:option_index] let l:option_index = l:option_index + 1 endif @@ -151,21 +154,21 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort \) endif - call add(l:cflags_list, l:option) - call add(l:cflags_list, l:arg) + call add(l:arguments_to_use, l:option) + call add(l:arguments_to_use, l:arg) " Options with arg that can be grouped with the option or separate elseif stridx(l:option, '-D') == 0 || stridx(l:option, '-B') == 0 - call add(l:cflags_list, l:option) + call add(l:arguments_to_use, l:option) if l:option is# '-D' || l:option is# '-B' - call add(l:cflags_list, l:split_lines[l:option_index]) + call add(l:arguments_to_use, l:arguments[l:option_index]) let l:option_index = l:option_index + 1 endif " Options that have an argument (always separate) elseif l:option is# '-iprefix' || stridx(l:option, '-iwithprefix') == 0 \ || l:option is# '-isysroot' || l:option is# '-imultilib' - call add(l:cflags_list, l:option) - call add(l:cflags_list, l:split_lines[l:option_index]) + call add(l:arguments_to_use, l:option) + call add(l:arguments_to_use, l:arguments[l:option_index]) let l:option_index = l:option_index + 1 " Options without argument elseif (stridx(l:option, '-W') == 0 && stridx(l:option, '-Wa,') != 0 && stridx(l:option, '-Wl,') != 0 && stridx(l:option, '-Wp,') != 0) @@ -177,11 +180,11 @@ function! ale#c#ParseCFlags(path_prefix, cflag_line) abort \ || stridx(l:option, '-nostdinc') == 0 || stridx(l:option, '-iplugindir=') == 0 \ || stridx(l:option, '--sysroot=') == 0 || l:option is# '--no-sysroot-suffix' \ || stridx(l:option, '-m') == 0 - call add(l:cflags_list, l:option) + call add(l:arguments_to_use, l:option) endif endwhile - return join(l:cflags_list, ' ') + return join(l:arguments_to_use, ' ') endfunction function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort @@ -203,7 +206,7 @@ function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') let l:makefile_dir = fnamemodify(l:makefile_path, ':p:h') - return ale#c#ParseCFlags(l:makefile_dir, l:cflag_line) + return ale#c#ParseCFlags(l:makefile_dir, 0, ale#c#ShellSplit(l:cflag_line)) endfunction " Given a buffer number, find the project directory containing @@ -333,14 +336,16 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort return l:empty endfunction -function! ale#c#GetCompileCommand(json_item) abort - if has_key(a:json_item, 'command') - return a:json_item.command - elseif has_key(a:json_item, 'arguments') - return join(a:json_item.arguments, ' ') +" Get [should_quote, arguments] from either 'command' or 'arguments' +" 'arguments' should be quoted later, the split 'command' strings should not. +function! s:GetArguments(json_item) abort + if has_key(a:json_item, 'arguments') + return [1, a:json_item.arguments] + elseif has_key(a:json_item, 'command') + return [0, ale#c#ShellSplit(a:json_item.command)] endif - return '' + return [0, []] endfunction function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort @@ -398,7 +403,9 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort \ && l:filename[-len(l:source_file):] is? l:source_file \ ) \) - return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item)) + let [l:should_quote, l:args] = s:GetArguments(l:item) + + return ale#c#ParseCFlags(l:item.directory, l:should_quote, l:args) endif endfor @@ -406,7 +413,9 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file) if ale#path#Simplify(fnamemodify(l:filename, ':h')) is? l:dir - return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item)) + let [l:should_quote, l:args] = s:GetArguments(l:item) + + return ale#c#ParseCFlags(l:item.directory, l:should_quote, l:args) endif endfor @@ -517,8 +526,3 @@ function! ale#c#IncludeOptions(include_paths) abort return join(l:option_list) endfunction - -let g:ale_c_build_dir_names = get(g:, 'ale_c_build_dir_names', [ -\ 'build', -\ 'bin', -\]) -- cgit v1.2.3 From 6d843715f3dfddf869dfec5b1031a93fea87db18 Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 20:18:13 +0100 Subject: Fix C flag parsing and tests on Windows --- autoload/ale/c.vim | 29 ++++++++++++++++++++++++++++- autoload/ale/path.vim | 8 ++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index eac9879c..4c7ee5bc 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -356,9 +356,27 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort " Search for an exact file match first. let l:file_list = get(a:file_lookup, l:buffer_filename, []) + + " We may have to look for /foo/bar instead of C:\foo\bar + if empty(l:file_list) && has('win32') + let l:file_list = get( + \ a:file_lookup, + \ ale#path#RemoveDriveLetter(l:buffer_filename), + \ [] + \) + endif + " Try the absolute path to the directory second. let l:dir_list = get(a:dir_lookup, l:dir, []) + if empty(l:dir_list) && has('win32') + let l:dir_list = get( + \ a:dir_lookup, + \ ale#path#RemoveDriveLetter(l:dir), + \ [] + \) + endif + if empty(l:file_list) && empty(l:dir_list) " If we can't find matches with the path to the file, try a " case-insensitive match for any similarly-named file. @@ -378,6 +396,14 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort let l:key = fnamemodify(l:buffer_filename, ':r') . l:suffix let l:file_list = get(a:file_lookup, l:key, []) + if empty(l:file_list) && has('win32') + let l:file_list = get( + \ a:file_lookup, + \ ale#path#RemoveDriveLetter(l:key), + \ [] + \) + endif + if empty(l:file_list) " Look fuzzy matches on the basename second. let l:key = fnamemodify(l:basename, ':r') . l:suffix @@ -412,7 +438,8 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort for l:item in l:dir_list let l:filename = ale#path#GetAbsPath(l:item.directory, l:item.file) - if ale#path#Simplify(fnamemodify(l:filename, ':h')) is? l:dir + if ale#path#RemoveDriveLetter(fnamemodify(l:filename, ':h')) + \ is? ale#path#RemoveDriveLetter(l:dir) let [l:should_quote, l:args] = s:GetArguments(l:item) return ale#c#ParseCFlags(l:item.directory, l:should_quote, l:args) diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim index f18a9733..4e59ce2f 100644 --- a/autoload/ale/path.vim +++ b/autoload/ale/path.vim @@ -24,6 +24,14 @@ function! ale#path#Simplify(path) abort return substitute(simplify(l:win_path), '^\\\+', '\', 'g') " no-custom-checks endfunction +" Simplify a path without a Windows drive letter. +" This function can be used for checking if paths are equal. +function! ale#path#RemoveDriveLetter(path) abort + return has('win32') && a:path[1:2] is# ':\' + \ ? ale#path#Simplify(a:path[2:]) + \ : ale#path#Simplify(a:path) +endfunction + " Given a buffer and a filename, find the nearest file by searching upwards " through the paths relative to the given buffer. function! ale#path#FindNearestFile(buffer, filename) abort -- cgit v1.2.3 From 7545b18ba181be61bdeee09efa292e9e82b6e863 Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 21:17:24 +0100 Subject: Fix #3318 - Escape macros when parsing C flags --- autoload/ale/c.vim | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 4c7ee5bc..64668dd5 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -120,10 +120,23 @@ function! ale#c#ExpandAtArgs(path_prefix, raw_split_lines) abort return l:out_lines endfunction +" Quote C/C++ a compiler argument, if needed. +" +" Quoting arguments might cause issues with some systems/compilers, so we only +" quote them if we need to. +function! ale#c#QuoteArg(arg) abort + if a:arg !~# '\v[#$&*()\\|[\]{};''"<>/?! ^%]' + return a:arg + endif + + return ale#Escape(a:arg) +endfunction + function! ale#c#ParseCFlags(path_prefix, should_quote, raw_arguments) abort " Expand @file arguments now before parsing let l:arguments = ale#c#ExpandAtArgs(a:path_prefix, a:raw_arguments) - let l:arguments_to_use = [] + " A list of [already_quoted, argument] + let l:items = [] let l:option_index = 0 while l:option_index < len(l:arguments) @@ -149,26 +162,25 @@ function! ale#c#ParseCFlags(path_prefix, should_quote, raw_arguments) abort if !ale#path#IsAbsolute(l:arg) let l:rel_path = substitute(l:arg, '"', '', 'g') let l:rel_path = substitute(l:rel_path, '''', '', 'g') - let l:arg = ale#Escape( - \ ale#path#GetAbsPath(a:path_prefix, l:rel_path) - \) + let l:arg = ale#path#GetAbsPath(a:path_prefix, l:rel_path) endif - call add(l:arguments_to_use, l:option) - call add(l:arguments_to_use, l:arg) + call add(l:items, [1, l:option]) + call add(l:items, [1, ale#Escape(l:arg)]) " Options with arg that can be grouped with the option or separate elseif stridx(l:option, '-D') == 0 || stridx(l:option, '-B') == 0 - call add(l:arguments_to_use, l:option) - if l:option is# '-D' || l:option is# '-B' - call add(l:arguments_to_use, l:arguments[l:option_index]) + call add(l:items, [1, l:option]) + call add(l:items, [0, l:arguments[l:option_index]]) let l:option_index = l:option_index + 1 + else + call add(l:items, [0, l:option]) endif " Options that have an argument (always separate) elseif l:option is# '-iprefix' || stridx(l:option, '-iwithprefix') == 0 \ || l:option is# '-isysroot' || l:option is# '-imultilib' - call add(l:arguments_to_use, l:option) - call add(l:arguments_to_use, l:arguments[l:option_index]) + call add(l:items, [0, l:option]) + call add(l:items, [0, l:arguments[l:option_index]]) let l:option_index = l:option_index + 1 " Options without argument elseif (stridx(l:option, '-W') == 0 && stridx(l:option, '-Wa,') != 0 && stridx(l:option, '-Wl,') != 0 && stridx(l:option, '-Wp,') != 0) @@ -180,11 +192,19 @@ function! ale#c#ParseCFlags(path_prefix, should_quote, raw_arguments) abort \ || stridx(l:option, '-nostdinc') == 0 || stridx(l:option, '-iplugindir=') == 0 \ || stridx(l:option, '--sysroot=') == 0 || l:option is# '--no-sysroot-suffix' \ || stridx(l:option, '-m') == 0 - call add(l:arguments_to_use, l:option) + call add(l:items, [0, l:option]) endif endwhile - return join(l:arguments_to_use, ' ') + if a:should_quote + " Quote C arguments that haven't already been quoted above. + " If and only if we've been asked to quote them. + call map(l:items, 'v:val[0] ? v:val[1] : ale#c#QuoteArg(v:val[1])') + else + call map(l:items, 'v:val[1]') + endif + + return join(l:items, ' ') endfunction function! ale#c#ParseCFlagsFromMakeOutput(buffer, make_output) abort -- cgit v1.2.3 From ecd7abecc08ead5d8f055b26498e1c4a2a2c3065 Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 27 Aug 2020 21:29:13 +0100 Subject: Fix #3319 - Force modifications to buffers --- autoload/ale/util.vim | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'autoload') diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim index 1f396377..05f11993 100644 --- a/autoload/ale/util.vim +++ b/autoload/ale/util.vim @@ -505,6 +505,13 @@ function! ale#util#SetBufferContents(buffer, lines) abort \ : a:lines let l:first_line_to_remove = len(l:new_lines) + 1 + " We'll temporarily make a buffer modifiable, to force edits. + let l:modifiable = getbufvar(a:buffer, '&modifiable') + + if !l:modifiable + call setbufvar(a:buffer, '&modifiable', 1) + endif + " Use a Vim API for setting lines in other buffers, if available. if l:has_bufline_api call setbufline(a:buffer, 1, l:new_lines) @@ -523,5 +530,9 @@ function! ale#util#SetBufferContents(buffer, lines) abort call setline(1, l:new_lines) endif + if !l:modifiable + call setbufvar(a:buffer, '&modifiable', 0) + endif + return l:new_lines endfunction -- cgit v1.2.3 From 80bd2e18d65e335492cc4f6ba3fe15cc98740ef3 Mon Sep 17 00:00:00 2001 From: Sorin Iclanzan Date: Fri, 28 Aug 2020 03:14:50 -0400 Subject: Set prettier working directory to where .prettierignore is (#3101) Prettier does not use `.prettierignore` unless the current directory is the root where the `.prettierignore` file resides. * Update Prettier tests * Look for prettierignore to determine project root --- autoload/ale/fixers/prettier.vim | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/fixers/prettier.vim b/autoload/ale/fixers/prettier.vim index 23120777..e0f4972e 100644 --- a/autoload/ale/fixers/prettier.vim +++ b/autoload/ale/fixers/prettier.vim @@ -34,6 +34,21 @@ function! ale#fixers#prettier#ProcessPrettierDOutput(buffer, output) abort return a:output endfunction +function! ale#fixers#prettier#GetProjectRoot(buffer) abort + let l:config = ale#path#FindNearestFile(a:buffer, '.prettierignore') + + if !empty(l:config) + return fnamemodify(l:config, ':h') + endif + + " Fall back to the directory of the buffer + return fnamemodify(bufname(a:buffer), ':p:h') +endfunction + +function! ale#fixers#prettier#CdProjectRoot(buffer) abort + return ale#path#CdString(ale#fixers#prettier#GetProjectRoot(a:buffer)) +endfunction + function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort let l:executable = ale#fixers#prettier#GetExecutable(a:buffer) let l:options = ale#Var(a:buffer, 'javascript_prettier_options') @@ -97,7 +112,7 @@ function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort " 1.4.0 is the first version with --stdin-filepath if ale#semver#GTE(a:version, [1, 4, 0]) return { - \ 'command': ale#path#BufferCdString(a:buffer) + \ 'command': ale#fixers#prettier#CdProjectRoot(a:buffer) \ . ale#Escape(l:executable) \ . (!empty(l:options) ? ' ' . l:options : '') \ . ' --stdin-filepath %s --stdin', -- cgit v1.2.3 From 369e3876f00d497ee9f7d2f6a3936837f6bcdcb7 Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 28 Aug 2020 08:23:02 +0100 Subject: #3324 - Enable rls by default --- autoload/ale/linter.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index fecfeed6..8d89cd5c 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -32,7 +32,7 @@ let s:default_ale_linter_aliases = { " " No linters are used for plaintext files by default. " -" Only cargo is enabled for Rust by default. +" Only cargo and rls are enabled for Rust by default. " rpmlint is disabled by default because it can result in code execution. " hhast is disabled by default because it executes code in the project root. " @@ -46,7 +46,7 @@ let s:default_ale_linters = { \ 'perl': ['perlcritic'], \ 'perl6': [], \ 'python': ['flake8', 'mypy', 'pylint', 'pyright'], -\ 'rust': ['cargo'], +\ 'rust': ['cargo', 'rls'], \ 'spec': [], \ 'text': [], \ 'vue': ['eslint', 'vls'], -- cgit v1.2.3 From 68741204059d56ffcb4229ec433edf5f9fc4e8c7 Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 28 Aug 2020 09:33:09 +0100 Subject: Fix #3323 - Set default for g:ale_filename_mappings --- autoload/ale/engine.vim | 1 + autoload/ale/fix.vim | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 3f4f9338..b00ec453 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -4,6 +4,7 @@ " Remapping of linter problems. let g:ale_type_map = get(g:, 'ale_type_map', {}) +let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {}) if !has_key(s:, 'executable_cache_map') let s:executable_cache_map = {} diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index a53f8626..3ad0a433 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -1,8 +1,8 @@ " Author: w0rp " Description: Functions for fixing code with programs, or other means. -call ale#Set('fix_on_save_ignore', {}) -call ale#Set('filename_mappings', {}) +let g:ale_fix_on_save_ignore = get(g:, 'ale_fix_on_save_ignore', {}) +let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {}) " Apply fixes queued up for buffers which may be hidden. " Vim doesn't let you modify hidden buffers. -- cgit v1.2.3 From b8c0ac2e6126d2245f4281c286387b4dd1847178 Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 28 Aug 2020 09:52:36 +0100 Subject: Close #3309 - Add b:ale_lint_delay --- autoload/ale/events.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/events.vim b/autoload/ale/events.vim index 731e36f2..3568c117 100644 --- a/autoload/ale/events.vim +++ b/autoload/ale/events.vim @@ -105,11 +105,11 @@ function! ale#events#Init() abort if g:ale_enabled if l:text_changed is? 'always' || l:text_changed is# '1' - autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay) + autocmd TextChanged,TextChangedI * call ale#Queue(ale#Var(str2nr(expand('')), 'lint_delay')) elseif l:text_changed is? 'normal' - autocmd TextChanged * call ale#Queue(g:ale_lint_delay) + autocmd TextChanged * call ale#Queue(ale#Var(str2nr(expand('')), 'lint_delay')) elseif l:text_changed is? 'insert' - autocmd TextChangedI * call ale#Queue(g:ale_lint_delay) + autocmd TextChangedI * call ale#Queue(ale#Var(str2nr(expand('')), 'lint_delay')) endif if g:ale_lint_on_enter -- cgit v1.2.3 From 34e409ea21baa017776b84ec0620eea9f6ec429c Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 28 Aug 2020 14:02:05 +0100 Subject: Close #3285 - lint_file is now dynamic `lint_file` can now be computed dynamically with a callback function, which can return a deferred result, as per `ale#command#Run`. This allows linters to dynamically switch between checking files on disk, or checking code on the fly. Some tests have been fixed on Windows. --- autoload/ale/engine.vim | 111 +++++++++++++++++++++++++++++++++++++++--------- autoload/ale/linter.vim | 10 ++--- 2 files changed, 95 insertions(+), 26 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index b00ec453..ae0354b8 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -417,7 +417,7 @@ function! s:RunJob(command, options) abort let l:buffer = a:options.buffer let l:linter = a:options.linter let l:output_stream = a:options.output_stream - let l:read_buffer = a:options.read_buffer + let l:read_buffer = a:options.read_buffer && !a:options.lint_file let l:info = g:ale_buffer_info[l:buffer] let l:Callback = function('s:HandleExit', [{ @@ -508,10 +508,15 @@ function! s:AddProblemsFromOtherBuffers(buffer, linters) abort endif endfunction -function! s:RunIfExecutable(buffer, linter, executable) abort +function! s:RunIfExecutable(buffer, linter, lint_file, executable) abort if ale#command#IsDeferred(a:executable) let a:executable.result_callback = { - \ executable -> s:RunIfExecutable(a:buffer, a:linter, executable) + \ executable -> s:RunIfExecutable( + \ a:buffer, + \ a:linter, + \ a:lint_file, + \ executable + \ ) \} return 1 @@ -519,7 +524,7 @@ function! s:RunIfExecutable(buffer, linter, executable) abort if ale#engine#IsExecutable(a:buffer, a:executable) " Use different job types for file or linter jobs. - let l:job_type = a:linter.lint_file ? 'file_linter' : 'linter' + let l:job_type = a:lint_file ? 'file_linter' : 'linter' call setbufvar(a:buffer, 'ale_job_type', l:job_type) let l:command = ale#linter#GetCommand(a:buffer, a:linter) @@ -529,6 +534,7 @@ function! s:RunIfExecutable(buffer, linter, executable) abort \ 'linter': a:linter, \ 'output_stream': get(a:linter, 'output_stream', 'stdout'), \ 'read_buffer': a:linter.read_buffer, + \ 'lint_file': a:lint_file, \} return s:RunJob(l:command, l:options) @@ -540,33 +546,62 @@ endfunction " Run a linter for a buffer. " " Returns 1 if the linter was successfully run. -function! s:RunLinter(buffer, linter) abort +function! s:RunLinter(buffer, linter, lint_file) abort if !empty(a:linter.lsp) return ale#lsp_linter#CheckWithLSP(a:buffer, a:linter) else let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) - return s:RunIfExecutable(a:buffer, a:linter, l:executable) + return s:RunIfExecutable(a:buffer, a:linter, a:lint_file, l:executable) endif return 0 endfunction -function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort - " Initialise the buffer information if needed. - let l:new_buffer = ale#engine#InitBufferInfo(a:buffer) - call s:StopCurrentJobs(a:buffer, a:should_lint_file) - call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters) +function! s:GetLintFileValues(slots, Callback) abort + let l:deferred_list = [] + let l:new_slots = [] - " 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:lint_file, l:linter] in a:slots + while ale#command#IsDeferred(l:lint_file) && has_key(l:lint_file, 'value') + " If we've already computed the return value, use it. + let l:lint_file = l:lint_file.value + endwhile - silent doautocmd User ALELintPre + if ale#command#IsDeferred(l:lint_file) + " If we are going to return the result later, wait for it. + call add(l:deferred_list, l:lint_file) + else + " If we have the value now, coerce it to 0 or 1. + let l:lint_file = l:lint_file is 1 + endif - for l:linter in a:linters + call add(l:new_slots, [l:lint_file, l:linter]) + endfor + + if !empty(l:deferred_list) + for l:deferred in l:deferred_list + let l:deferred.result_callback = + \ {-> s:GetLintFileValues(l:new_slots, a:Callback)} + endfor + else + call a:Callback(l:new_slots) + endif +endfunction + +function! s:RunLinters( +\ buffer, +\ slots, +\ should_lint_file, +\ new_buffer, +\ can_clear_results +\) abort + let l:can_clear_results = a:can_clear_results + + for [l:lint_file, l:linter] in a:slots " 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 !l:lint_file || a:should_lint_file + if s:RunLinter(a:buffer, l:linter, l:lint_file) " If a single linter ran, we shouldn't clear everything. let l:can_clear_results = 0 endif @@ -581,11 +616,49 @@ function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort " disabled, or ALE itself is disabled. if l:can_clear_results call ale#engine#SetResults(a:buffer, []) - elseif l:new_buffer - call s:AddProblemsFromOtherBuffers(a:buffer, a:linters) + elseif a:new_buffer + call s:AddProblemsFromOtherBuffers( + \ a:buffer, + \ map(copy(a:slots), 'v:val[1]') + \) endif endfunction +function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort + " Initialise the buffer information if needed. + let l:new_buffer = ale#engine#InitBufferInfo(a:buffer) + call s:StopCurrentJobs(a:buffer, a:should_lint_file) + call s:RemoveProblemsForDisabledLinters(a:buffer, a:linters) + + " We can only clear the results if we aren't checking the buffer. + let l:can_clear_results = !ale#engine#IsCheckingBuffer(a:buffer) + + silent doautocmd User ALELintPre + + " Handle `lint_file` callbacks first. + let l:linter_slots = [] + + for l:linter in a:linters + let l:LintFile = l:linter.lint_file + + if type(l:LintFile) is v:t_func + let l:LintFile = l:LintFile(a:buffer) + endif + + call add(l:linter_slots, [l:LintFile, l:linter]) + endfor + + call s:GetLintFileValues(l:linter_slots, { + \ new_slots -> s:RunLinters( + \ a:buffer, + \ new_slots, + \ a:should_lint_file, + \ l:new_buffer, + \ l:can_clear_results, + \ ) + \}) +endfunction + " Clean up a buffer. " " This function will stop all current jobs for the buffer, diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 8d89cd5c..b483fc19 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -211,21 +211,17 @@ function! ale#linter#PreProcess(filetype, linter) abort " file on disk. let l:obj.lint_file = get(a:linter, 'lint_file', 0) - if !s:IsBoolean(l:obj.lint_file) - throw '`lint_file` must be `0` or `1`' + if !s:IsBoolean(l:obj.lint_file) && type(l:obj.lint_file) isnot v:t_func + throw '`lint_file` must be `0`, `1`, or a Function' endif " An option indicating that the buffer should be read. - let l:obj.read_buffer = get(a:linter, 'read_buffer', !l:obj.lint_file) + let l:obj.read_buffer = get(a:linter, 'read_buffer', 1) if !s:IsBoolean(l:obj.read_buffer) throw '`read_buffer` must be `0` or `1`' endif - if l:obj.lint_file && l:obj.read_buffer - 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) isnot v:t_list -- cgit v1.2.3 From 3d5a2690ce707cbc9e71e8ed4d5d1955e4b26d65 Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 28 Aug 2020 17:46:43 +0100 Subject: #3325 - ale#path#BufferCdString now generates %s:h --- autoload/ale/path.vim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim index 4e59ce2f..fed95ccd 100644 --- a/autoload/ale/path.vim +++ b/autoload/ale/path.vim @@ -82,15 +82,19 @@ endfunction function! ale#path#CdString(directory) abort if has('win32') return 'cd /d ' . ale#Escape(a:directory) . ' && ' - else - return 'cd ' . ale#Escape(a:directory) . ' && ' endif + + return 'cd ' . ale#Escape(a:directory) . ' && ' endfunction " Output 'cd && ' " This function can be used changing the directory for a linter command. function! ale#path#BufferCdString(buffer) abort - return ale#path#CdString(fnamemodify(bufname(a:buffer), ':p:h')) + if has('win32') + return 'cd /d %s:h && ' + endif + + return 'cd %s:h && ' endfunction " Return 1 if a path is an absolute path. -- cgit v1.2.3 From 7d4ce4e6aa960a6052a16d90322566d6f4fddb7c Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 28 Aug 2020 19:50:36 +0100 Subject: Close #3325 - Apply new formatting where possible --- autoload/ale/c.vim | 6 +++--- autoload/ale/fixers/ocamlformat.vim | 3 +-- autoload/ale/fixers/rubocop.vim | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index 64668dd5..d0a4fa06 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -501,10 +501,10 @@ endfunction function! ale#c#GetMakeCommand(buffer) abort if s:CanParseMakefile(a:buffer) - let l:makefile_path = ale#path#FindNearestFile(a:buffer, 'Makefile') + let l:path = ale#path#FindNearestFile(a:buffer, 'Makefile') - if !empty(l:makefile_path) - return 'cd '. fnamemodify(l:makefile_path, ':p:h') . ' && make -n' + if !empty(l:path) + return ale#path#CdString(fnamemodify(l:path, ':h')) . 'make -n' endif endif diff --git a/autoload/ale/fixers/ocamlformat.vim b/autoload/ale/fixers/ocamlformat.vim index 9b7c3e12..b12d2eb9 100644 --- a/autoload/ale/fixers/ocamlformat.vim +++ b/autoload/ale/fixers/ocamlformat.vim @@ -5,14 +5,13 @@ call ale#Set('ocaml_ocamlformat_executable', 'ocamlformat') call ale#Set('ocaml_ocamlformat_options', '') function! ale#fixers#ocamlformat#Fix(buffer) abort - let l:filename = expand('#' . a:buffer . ':p') let l:executable = ale#Var(a:buffer, 'ocaml_ocamlformat_executable') let l:options = ale#Var(a:buffer, 'ocaml_ocamlformat_options') return { \ 'command': ale#Escape(l:executable) \ . (empty(l:options) ? '' : ' ' . l:options) - \ . ' --name=' . ale#Escape(l:filename) + \ . ' --name=%s' \ . ' -' \} endfunction diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim index d9615256..cdfb014a 100644 --- a/autoload/ale/fixers/rubocop.vim +++ b/autoload/ale/fixers/rubocop.vim @@ -29,8 +29,7 @@ function! ale#fixers#rubocop#GetCommand(buffer) abort \ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '') \ . (!empty(l:options) ? ' ' . l:options : '') \ . (l:auto_correct_all ? ' --auto-correct-all' : ' --auto-correct') - \ . ' --force-exclusion --stdin ' - \ . ale#Escape(expand('#' . a:buffer . ':p')) + \ . ' --force-exclusion --stdin %s' endfunction function! ale#fixers#rubocop#Fix(buffer) abort -- cgit v1.2.3 From dd9ad9b5be84bea7105a91bb7e15c308cfe2c57f Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 29 Aug 2020 12:33:17 +0100 Subject: #3319 - Try to modify buffers later for ALEFix --- autoload/ale/fix.vim | 31 +++++++++++++++++++------------ autoload/ale/util.vim | 11 ----------- 2 files changed, 19 insertions(+), 23 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index 3ad0a433..b04ab18f 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -15,22 +15,29 @@ function! ale#fix#ApplyQueuedFixes(buffer) abort call remove(g:ale_fix_buffer_data, a:buffer) - if l:data.changes_made - let l:new_lines = ale#util#SetBufferContents(a:buffer, l:data.output) - - if l:data.should_save - if a:buffer is bufnr('') - if empty(&buftype) - noautocmd :w! + try + if l:data.changes_made + let l:new_lines = ale#util#SetBufferContents(a:buffer, l:data.output) + + if l:data.should_save + if a:buffer is bufnr('') + if empty(&buftype) + noautocmd :w! + else + set nomodified + endif else - set nomodified + call writefile(l:new_lines, expand('#' . a:buffer . ':p')) " no-custom-checks + call setbufvar(a:buffer, '&modified', 0) endif - else - call writefile(l:new_lines, expand('#' . a:buffer . ':p')) " no-custom-checks - call setbufvar(a:buffer, '&modified', 0) endif endif - endif + catch /E21/ + " If we cannot modify the buffer now, try again later. + let g:ale_fix_buffer_data[a:buffer] = l:data + + return + endtry if l:data.should_save let l:should_lint = ale#Var(a:buffer, 'fix_on_save') diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim index 05f11993..1f396377 100644 --- a/autoload/ale/util.vim +++ b/autoload/ale/util.vim @@ -505,13 +505,6 @@ function! ale#util#SetBufferContents(buffer, lines) abort \ : a:lines let l:first_line_to_remove = len(l:new_lines) + 1 - " We'll temporarily make a buffer modifiable, to force edits. - let l:modifiable = getbufvar(a:buffer, '&modifiable') - - if !l:modifiable - call setbufvar(a:buffer, '&modifiable', 1) - endif - " Use a Vim API for setting lines in other buffers, if available. if l:has_bufline_api call setbufline(a:buffer, 1, l:new_lines) @@ -530,9 +523,5 @@ function! ale#util#SetBufferContents(buffer, lines) abort call setline(1, l:new_lines) endif - if !l:modifiable - call setbufvar(a:buffer, '&modifiable', 0) - endif - return l:new_lines endfunction -- cgit v1.2.3 From d9a7d6bc231b699fbd103e3af6edbcdaa7ed6f91 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sat, 29 Aug 2020 15:03:26 +0300 Subject: Improved macro handling in gcc --- autoload/ale/handlers/gcc.vim | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim index ec16b977..0b37c98a 100644 --- a/autoload/ale/handlers/gcc.vim +++ b/autoload/ale/handlers/gcc.vim @@ -10,7 +10,7 @@ let s:pragma_error = '#pragma once in main file' " :8:5: warning: conversion lacks type at end of format [-Wformat=] " :10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’) " -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004] -let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$' +let s:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+)?:?(\d+)?:? ([^:]+): (.+)$' let s:inline_pattern = '\v inlined from .* at \:(\d+):(\d+):$' function! s:IsHeaderFile(filename) abort @@ -117,6 +117,23 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort if !empty(l:output) if !has_key(l:output[-1], 'detail') let l:output[-1].detail = l:output[-1].text + + " handle macro expansion errors/notes + if l:match[5] =~? '^in expansion of macro ‘\w*\w’$' + " if the macro expansion is in the file we're in, add + " the lnum and col keys to the previous error + if l:match[1] is# '' + \ && !has_key(l:output[-1], 'col') + let l:output[-1].lnum = str2nr(l:match[2]) + let l:output[-1].col = str2nr(l:match[3]) + else + " the error is not in the current file, and since + " macro expansion errors don't show the full path to + " the error from the current file, we have to just + " give out a generic error message + let l:output[-1].text = 'Error found in macro expansion. See :ALEDetail' + endif + endif endif let l:output[-1].detail = l:output[-1].detail . "\n" -- cgit v1.2.3 From bc3a843e102d4490b2476b53531807ab624417e7 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 29 Aug 2020 14:23:51 +0100 Subject: Add a missing `augroup END` line --- autoload/ale/fix.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'autoload') diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index b04ab18f..8b841b13 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -383,3 +383,4 @@ endfunction augroup ALEBufferFixGroup autocmd! autocmd BufEnter * call ale#fix#ApplyQueuedFixes(str2nr(expand(''))) +augroup END -- cgit v1.2.3 From 7e0cdb53ecf9c94bb8777a57de8bf2aacca46b5d Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 29 Aug 2020 16:05:49 +0100 Subject: Fix #3247 - Use --always-make for make -n by default --- autoload/ale/c.vim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim index d0a4fa06..cff53125 100644 --- a/autoload/ale/c.vim +++ b/autoload/ale/c.vim @@ -2,7 +2,9 @@ " Description: Functions for integrating with C-family linters. call ale#Set('c_parse_makefile', 0) +call ale#Set('c_always_make', has('unix') && !has('macunix')) call ale#Set('c_parse_compile_commands', 1) + let s:sep = has('win32') ? '\' : '/' " Set just so tests can override it. @@ -504,7 +506,10 @@ function! ale#c#GetMakeCommand(buffer) abort let l:path = ale#path#FindNearestFile(a:buffer, 'Makefile') if !empty(l:path) - return ale#path#CdString(fnamemodify(l:path, ':h')) . 'make -n' + let l:always_make = ale#Var(a:buffer, 'c_always_make') + + return ale#path#CdString(fnamemodify(l:path, ':h')) + \ . 'make -n' . (l:always_make ? ' --always-make' : '') endif endif -- cgit v1.2.3 From 6e2e51b154d526c1ab0b506bb2110b45421b7a06 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 29 Aug 2020 17:27:53 +0100 Subject: Fix #2971 - Disable automatic completion while 'paste' is active --- autoload/ale/completion.vim | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index f6a0c350..5035bbf0 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -324,6 +324,12 @@ function! ale#completion#AutomaticOmniFunc(findstart, base) abort endif endfunction +function! s:OpenCompletionMenu(...) abort + if !&l:paste + call ale#util#FeedKeys("\(ale_show_completion_menu)") + endif +endfunction + function! ale#completion#Show(result) abort if ale#util#Mode() isnot# 'i' return @@ -344,10 +350,7 @@ function! ale#completion#Show(result) abort let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '') if l:source is# 'ale-automatic' || l:source is# 'ale-manual' - call timer_start( - \ 0, - \ {-> ale#util#FeedKeys("\(ale_show_completion_menu)")} - \) + call timer_start(0, function('s:OpenCompletionMenu')) endif if l:source is# 'ale-callback' -- cgit v1.2.3 From 25b572b3bf3cc0229ed836eb3fa80c4c74f2c247 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 29 Aug 2020 17:36:25 +0100 Subject: Close #3205 - Avoid inserting completions by default --- autoload/ale/completion.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 5035bbf0..c2cfd74a 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -5,7 +5,7 @@ scriptencoding utf-8 " The omnicompletion menu is shown through a special Plug mapping which is " only valid in Insert mode. This way, feedkeys() won't send these keys if you " quit Insert mode quickly enough. -inoremap (ale_show_completion_menu) +inoremap (ale_show_completion_menu) " If we hit the key sequence in normal mode, then we won't show the menu, so " we should restore the old settings right away. nnoremap (ale_show_completion_menu) :call ale#completion#RestoreCompletionOptions() -- cgit v1.2.3 From 844febb9fbfb66bb13dd652d958495e47f0bd408 Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 4 Sep 2020 09:37:33 +0100 Subject: Fix #3322 - Apply rename changes correctly --- autoload/ale/code_action.vim | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'autoload') diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim index 60c3bbef..8c7263f3 100644 --- a/autoload/ale/code_action.vim +++ b/autoload/ale/code_action.vim @@ -24,6 +24,42 @@ function! ale#code_action#HandleCodeAction(code_action, should_save) abort endfor endfunction +function! s:ChangeCmp(left, right) abort + if a:left.start.line < a:right.start.line + return -1 + endif + + if a:left.start.line > a:right.start.line + return 1 + endif + + if a:left.start.offset < a:right.start.offset + return -1 + endif + + if a:left.start.offset > a:right.start.offset + return 1 + endif + + if a:left.end.line < a:right.end.line + return -1 + endif + + if a:left.end.line > a:right.end.line + return 1 + endif + + if a:left.end.offset < a:right.end.offset + return -1 + endif + + if a:left.end.offset > a:right.end.offset + return 1 + endif + + return 0 +endfunction + function! ale#code_action#ApplyChanges(filename, changes, should_save) abort let l:current_buffer = bufnr('') " The buffer is used to determine the fileformat, if available. @@ -48,7 +84,8 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort let l:column_offset = 0 let l:last_end_line = 0 - for l:code_edit in a:changes + " Changes have to be sorted so we apply them from top-to-bottom. + for l:code_edit in sort(copy(a:changes), function('s:ChangeCmp')) if l:code_edit.start.line isnot l:last_end_line let l:column_offset = 0 endif -- cgit v1.2.3 From 5bc49d204782617e01da65d83c0d9700a1b0a72f Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 5 Sep 2020 21:46:39 +0100 Subject: Fix #3183 - Escape filename characters from LSP/tsserver --- autoload/ale/lsp_linter.vim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index db640654..dcd76e8f 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -34,7 +34,11 @@ endfunction function! s:HandleLSPDiagnostics(conn_id, response) abort let l:linter_name = s:lsp_linter_map[a:conn_id] let l:filename = ale#path#FromURI(a:response.params.uri) - let l:buffer = bufnr('^' . l:filename . '$') + let l:escaped_name = escape( + \ fnameescape(l:filename), + \ has('win32') ? '^' : '^,}]' + \) + let l:buffer = bufnr('^' . l:escaped_name . '$') let l:info = get(g:ale_buffer_info, l:buffer, {}) if empty(l:info) @@ -52,7 +56,11 @@ endfunction function! s:HandleTSServerDiagnostics(response, error_type) abort let l:linter_name = 'tsserver' - let l:buffer = bufnr('^' . a:response.body.file . '$') + let l:escaped_name = escape( + \ fnameescape(a:response.body.file), + \ has('win32') ? '^' : '^,}]' + \) + let l:buffer = bufnr('^' . l:escaped_name . '$') let l:info = get(g:ale_buffer_info, l:buffer, {}) if empty(l:info) -- cgit v1.2.3 From c36053d4cc28fbc5fc0ee70ccb2c9a1b349eaaa3 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 6 Sep 2020 22:37:37 +0100 Subject: Close #3268 - Implement :ALEImport A new command, `:ALEImport`, has been added, which lets you import words at your cursor if a completion provider can provide a completion for that word which includes some additional text changes. --- autoload/ale/completion.vim | 185 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 149 insertions(+), 36 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index c2cfd74a..96415202 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -188,7 +188,13 @@ function! ale#completion#GetTriggerCharacter(filetype, prefix) abort return '' endfunction -function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort +function! ale#completion#Filter( +\ buffer, +\ filetype, +\ suggestions, +\ prefix, +\ exact_prefix_match, +\) abort let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words') if empty(a:prefix) @@ -215,10 +221,17 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort " Dictionaries is accepted here. let l:word = type(l:item) is v:t_string ? l:item : l:item.word - " Add suggestions if the suggestion starts with a - " case-insensitive match for the prefix. - if l:word[: len(a:prefix) - 1] is? a:prefix - call add(l:filtered_suggestions, l:item) + if a:exact_prefix_match + " Add suggestions if the word is an exact match. + if l:word is# a:prefix + call add(l:filtered_suggestions, l:item) + endif + else + " Add suggestions if the suggestion starts with a + " case-insensitive match for the prefix. + if l:word[: len(a:prefix) - 1] is? a:prefix + call add(l:filtered_suggestions, l:item) + endif endif endfor endif @@ -241,21 +254,17 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort return l:filtered_suggestions endfunction -function! s:ReplaceCompletionOptions() abort - let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '') - - if l:source is# 'ale-automatic' || l:source is# 'ale-manual' - " Remember the old omnifunc value, if there is one. - " If we don't store an old one, we'll just never reset the option. - " This will stop some random exceptions from appearing. - if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc) - let b:ale_old_omnifunc = &l:omnifunc - endif - - let &l:omnifunc = 'ale#completion#AutomaticOmniFunc' +function! s:ReplaceCompletionOptions(source) abort + " Remember the old omnifunc value, if there is one. + " If we don't store an old one, we'll just never reset the option. + " This will stop some random exceptions from appearing. + if !exists('b:ale_old_omnifunc') && !empty(&l:omnifunc) + let b:ale_old_omnifunc = &l:omnifunc endif - if l:source is# 'ale-automatic' + let &l:omnifunc = 'ale#completion#AutomaticOmniFunc' + + if a:source is# 'ale-automatic' if !exists('b:ale_old_completeopt') let b:ale_old_completeopt = &l:completeopt endif @@ -318,7 +327,11 @@ function! ale#completion#AutomaticOmniFunc(findstart, base) abort else let l:result = ale#completion#GetCompletionResult() - call s:ReplaceCompletionOptions() + let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '') + + if l:source is# 'ale-automatic' || l:source is# 'ale-manual' + call s:ReplaceCompletionOptions(l:source) + endif return l:result isnot v:null ? l:result : [] endif @@ -331,31 +344,53 @@ function! s:OpenCompletionMenu(...) abort endfunction function! ale#completion#Show(result) abort - if ale#util#Mode() isnot# 'i' + let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '') + + if ale#util#Mode() isnot# 'i' && l:source isnot# 'ale-import' return endif - " Set the list in the buffer, temporarily replace omnifunc with our - " function, and then start omni-completion. + " Set the list in the buffer. let b:ale_completion_result = a:result " Don't try to open the completion menu if there's nothing to show. if empty(b:ale_completion_result) + if l:source is# 'ale-import' + " If we ran completion from :ALEImport, + " tell the user that nothing is going to happen. + call s:message('No possible imports found.') + endif + return endif " Replace completion options shortly before opening the menu. - call s:ReplaceCompletionOptions() - - let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '') - if l:source is# 'ale-automatic' || l:source is# 'ale-manual' + call s:ReplaceCompletionOptions(l:source) + call timer_start(0, function('s:OpenCompletionMenu')) endif if l:source is# 'ale-callback' call b:CompleteCallback(b:ale_completion_result) endif + + if l:source is# 'ale-import' + call ale#completion#HandleUserData(b:ale_completion_result[0]) + + let l:text_changed = '' . g:ale_lint_on_text_changed + + " Check the buffer again right away, if linting is enabled. + if g:ale_enabled + \&& ( + \ l:text_changed is# '1' + \ || l:text_changed is# 'always' + \ || l:text_changed is# 'normal' + \ || l:text_changed is# 'insert' + \) + call ale#Queue(0, '') + endif + endif endfunction function! ale#completion#GetAllTriggers() abort @@ -386,7 +421,10 @@ endfunction function! s:CompletionStillValid(request_id) abort let [l:line, l:column] = getpos('.')[1:2] - return ale#util#Mode() is# 'i' + return ( + \ ale#util#Mode() is# 'i' + \ || b:ale_completion_info.source is# 'ale-import' + \) \&& has_key(b:, 'ale_completion_info') \&& b:ale_completion_info.request_id == a:request_id \&& b:ale_completion_info.line == l:line @@ -394,6 +432,7 @@ function! s:CompletionStillValid(request_id) abort \ b:ale_completion_info.column == l:column \ || b:ale_completion_info.source is# 'ale-omnifunc' \ || b:ale_completion_info.source is# 'ale-callback' + \ || b:ale_completion_info.source is# 'ale-import' \) endfunction @@ -418,6 +457,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort let l:buffer = bufnr('') let l:results = [] let l:names_with_details = [] + let l:info = get(b:, 'ale_completion_info', {}) for l:suggestion in a:response.body let l:displayParts = [] @@ -459,7 +499,8 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort \ 'kind': ale#completion#GetCompletionSymbols(l:suggestion.kind), \ 'icase': 1, \ 'menu': join(l:displayParts, ''), - \ 'dup': g:ale_completion_autoimport, + \ 'dup': get(l:info, 'additional_edits_only', 0) + \ || g:ale_completion_autoimport, \ 'info': join(l:documentationParts, ''), \} @@ -469,7 +510,12 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort \ }) endif - call add(l:results, l:result) + " Include this item if we'll accept any items, + " or if we only want items with additional edits, and this has them. + if !get(l:info, 'additional_edits_only', 0) + \|| has_key(l:result, 'user_data') + call add(l:results, l:result) + endif endfor let l:names = getbufvar(l:buffer, 'ale_tsserver_completion_names', []) @@ -544,7 +590,10 @@ function! ale#completion#ParseLSPCompletions(response) abort " Don't use LSP items with additional text edits when autoimport for " completions is turned off. if !empty(get(l:item, 'additionalTextEdits')) - \&& !g:ale_completion_autoimport + \&& !( + \ get(l:info, 'additional_edits_only', 0) + \ || g:ale_completion_autoimport + \) continue endif @@ -594,11 +643,22 @@ function! ale#completion#ParseLSPCompletions(response) abort endif endif - call add(l:results, l:result) + " Include this item if we'll accept any items, + " or if we only want items with additional edits, and this has them. + if !get(l:info, 'additional_edits_only', 0) + \|| has_key(l:result, 'user_data') + call add(l:results, l:result) + endif endfor if has_key(l:info, 'prefix') - let l:results = ale#completion#Filter(l:buffer, &filetype, l:results, l:info.prefix) + let l:results = ale#completion#Filter( + \ l:buffer, + \ &filetype, + \ l:results, + \ l:info.prefix, + \ get(l:info, 'additional_edits_only', 0), + \) endif return l:results[: g:ale_completion_max_suggestions - 1] @@ -622,13 +682,18 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort \ &filetype, \ ale#completion#ParseTSServerCompletions(a:response), \ b:ale_completion_info.prefix, + \ get(b:ale_completion_info, 'additional_edits_only', 0), \)[: g:ale_completion_max_suggestions - 1] " We need to remember some names for tsserver, as it doesn't send " details back for everything we send. call setbufvar(l:buffer, 'ale_tsserver_completion_names', l:names) - if !empty(l:names) + if empty(l:names) + " Response with no results now and skip making a redundant request + " for nothing. + call ale#completion#Show([]) + else let l:identifiers = [] for l:name in l:names @@ -702,7 +767,8 @@ function! s:OnReady(linter, lsp_details) abort \ b:ale_completion_info.line, \ b:ale_completion_info.column, \ b:ale_completion_info.prefix, - \ g:ale_completion_autoimport, + \ get(b:ale_completion_info, 'additional_edits_only', 0) + \ || g:ale_completion_autoimport, \) else " Send a message saying the buffer has changed first, otherwise @@ -761,9 +827,19 @@ function! ale#completion#GetCompletions(...) abort let b:CompleteCallback = l:CompleteCallback endif - let [l:line, l:column] = getpos('.')[1:2] + if has_key(l:options, 'line') && has_key(l:options, 'column') + " Use a provided line and column, if given. + let l:line = l:options.line + let l:column = l:options.column + else + let [l:line, l:column] = getpos('.')[1:2] + endif - let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column) + if has_key(l:options, 'prefix') + let l:prefix = l:options.prefix + else + let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column) + endif if l:source is# 'ale-automatic' && empty(l:prefix) return 0 @@ -782,6 +858,11 @@ function! ale#completion#GetCompletions(...) abort \} unlet! b:ale_completion_result + if has_key(l:options, 'additional_edits_only') + let b:ale_completion_info.additional_edits_only = + \ l:options.additional_edits_only + endif + let l:buffer = bufnr('') let l:Callback = function('s:OnReady') @@ -798,6 +879,37 @@ function! ale#completion#GetCompletions(...) abort return l:started endfunction +function! s:message(message) abort + call ale#util#Execute('echom ' . string(a:message)) +endfunction + +" This function implements the :ALEImport command. +function! ale#completion#Import() abort + let l:word = expand('') + + if empty(l:word) + call s:message('Nothing to complete at cursor!') + + return + endif + + let [l:line, l:column] = getpos('.')[1:2] + let l:column = searchpos('\V' . escape(l:word, '/\'), 'bn', l:line)[1] + + if l:column isnot 0 + let l:started = ale#completion#GetCompletions('ale-import', { + \ 'line': l:line, + \ 'column': l:column, + \ 'prefix': l:word, + \ 'additional_edits_only': 1, + \}) + + if !l:started + call s:message('No completion providers are available.') + endif + endif +endfunction + function! ale#completion#OmniFunc(findstart, base) abort if a:findstart let l:started = ale#completion#GetCompletions('ale-omnifunc') @@ -876,6 +988,7 @@ function! ale#completion#HandleUserData(completed_item) abort if l:source isnot# 'ale-automatic' \&& l:source isnot# 'ale-manual' \&& l:source isnot# 'ale-callback' + \&& l:source isnot# 'ale-import' return endif -- cgit v1.2.3 From b4b75126f9eae30da8f5e0cb9ec100feb38c1cb6 Mon Sep 17 00:00:00 2001 From: w0rp Date: Mon, 7 Sep 2020 10:00:36 +0100 Subject: Fix a completion error --- autoload/ale/completion.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 96415202..bdade95c 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -421,11 +421,11 @@ endfunction function! s:CompletionStillValid(request_id) abort let [l:line, l:column] = getpos('.')[1:2] - return ( + return has_key(b:, 'ale_completion_info') + \&& ( \ ale#util#Mode() is# 'i' \ || b:ale_completion_info.source is# 'ale-import' \) - \&& has_key(b:, 'ale_completion_info') \&& b:ale_completion_info.request_id == a:request_id \&& b:ale_completion_info.line == l:line \&& ( -- cgit v1.2.3