summaryrefslogtreecommitdiff
path: root/autoload
diff options
context:
space:
mode:
Diffstat (limited to 'autoload')
-rw-r--r--autoload/ale/c.vim25
-rw-r--r--autoload/ale/completion.vim101
-rw-r--r--autoload/ale/definition.vim19
-rw-r--r--autoload/ale/engine.vim6
-rw-r--r--autoload/ale/fix.vim28
-rw-r--r--autoload/ale/fix/registry.vim5
-rw-r--r--autoload/ale/fixers/styler.vim16
-rw-r--r--autoload/ale/highlight.vim140
-rw-r--r--autoload/ale/lsp/response.vim2
-rw-r--r--autoload/ale/lsp_linter.vim15
-rw-r--r--autoload/ale/sign.vim2
-rw-r--r--autoload/ale/util.vim2
-rw-r--r--autoload/ale/virtualtext.vim2
13 files changed, 295 insertions, 68 deletions
diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
index eeb512ba..a9289e22 100644
--- a/autoload/ale/c.vim
+++ b/autoload/ale/c.vim
@@ -219,9 +219,32 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort
" 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, [])
+ " 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
+ let l:file_list = get(a:file_lookup, l:key, [])
+
+ if !empty(l:file_list)
+ let l:source_file = l:key
+ break
+ endif
+ endfor
+ endif
for l:item in l:file_list
- if bufnr(l:item.file) is a:buffer && has_key(l:item, 'command')
+ " Load the flags for this file, or for a source file matching the
+ " header file.
+ if has_key(l:item, 'command')
+ \&& (
+ \ bufnr(l:item.file) is a:buffer
+ \ || (
+ \ !empty(l:source_file)
+ \ && l:item.file[-len(l:source_file):] is? l:source_file
+ \ )
+ \)
return ale#c#ParseCFlags(l:item.directory, l:item.command)
endif
endfor
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
index 682f4c43..1d42c489 100644
--- a/autoload/ale/completion.vim
+++ b/autoload/ale/completion.vim
@@ -159,18 +159,20 @@ function! ale#completion#Filter(buffer, filetype, suggestions, prefix) abort
endfunction
function! s:ReplaceCompletionOptions() 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
-
- let &l:omnifunc = 'ale#completion#OmniFunc'
+ 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:info = get(b:, 'ale_completion_info', {})
+ let &l:omnifunc = 'ale#completion#OmniFunc'
+ endif
- if !get(l:info, 'manual')
+ if l:source is# 'ale-automatic'
if !exists('b:ale_old_completeopt')
let b:ale_old_completeopt = &l:completeopt
endif
@@ -199,31 +201,49 @@ function! ale#completion#RestoreCompletionOptions() abort
endif
endfunction
-function! ale#completion#OmniFunc(findstart, base) abort
- if a:findstart
- let l:line = b:ale_completion_info.line
- let l:column = b:ale_completion_info.column
- let l:regex = s:GetFiletypeValue(s:omni_start_map, &filetype)
- let l:up_to_column = getline(l:line)[: l:column - 2]
- let l:match = matchstr(l:up_to_column, l:regex)
+function! ale#completion#GetCompletionPosition() abort
+ if !exists('b:ale_completion_info')
+ return 0
+ endif
- return l:column - len(l:match) - 1
- else
- " Parse a new response if there is one.
- if exists('b:ale_completion_response')
- \&& exists('b:ale_completion_parser')
- let l:response = b:ale_completion_response
- let l:parser = b:ale_completion_parser
+ let l:line = b:ale_completion_info.line
+ let l:column = b:ale_completion_info.column
+ let l:regex = s:GetFiletypeValue(s:omni_start_map, &filetype)
+ let l:up_to_column = getline(l:line)[: l:column - 2]
+ let l:match = matchstr(l:up_to_column, l:regex)
+
+ return l:column - len(l:match) - 1
+endfunction
- unlet b:ale_completion_response
- unlet b:ale_completion_parser
+function! ale#completion#GetCompletionResult() abort
+ " Parse a new response if there is one.
+ if exists('b:ale_completion_response')
+ \&& exists('b:ale_completion_parser')
+ let l:response = b:ale_completion_response
+ let l:parser = b:ale_completion_parser
- let b:ale_completion_result = function(l:parser)(l:response)
- endif
+ unlet b:ale_completion_response
+ unlet b:ale_completion_parser
+
+ let b:ale_completion_result = function(l:parser)(l:response)
+ endif
+
+ if exists('b:ale_completion_result')
+ return b:ale_completion_result
+ endif
+
+ return v:null
+endfunction
+
+function! ale#completion#OmniFunc(findstart, base) abort
+ if a:findstart
+ return ale#completion#GetCompletionPosition()
+ else
+ let l:result = ale#completion#GetCompletionResult()
call s:ReplaceCompletionOptions()
- return get(b:, 'ale_completion_result', [])
+ return l:result isnot v:null ? l:result : []
endif
endfunction
@@ -239,7 +259,14 @@ function! ale#completion#Show(response, completion_parser) abort
" Replace completion options shortly before opening the menu.
call s:ReplaceCompletionOptions()
- call timer_start(0, {-> ale#util#FeedKeys("\<Plug>(ale_show_completion_menu)")})
+ 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("\<Plug>(ale_show_completion_menu)")}
+ \)
+ endif
endfunction
function! s:CompletionStillValid(request_id) abort
@@ -249,7 +276,10 @@ function! s:CompletionStillValid(request_id) abort
\&& has_key(b:, 'ale_completion_info')
\&& b:ale_completion_info.request_id == a:request_id
\&& b:ale_completion_info.line == l:line
- \&& b:ale_completion_info.column == l:column
+ \&& (
+ \ b:ale_completion_info.column == l:column
+ \ || b:ale_completion_info.source is# 'deoplete'
+ \)
endfunction
function! ale#completion#ParseTSServerCompletions(response) abort
@@ -519,12 +549,12 @@ endfunction
" This function can be used to manually trigger autocomplete, even when
" g:ale_completion_enabled is set to false
-function! ale#completion#GetCompletions(manual) abort
+function! ale#completion#GetCompletions(source) abort
let [l:line, l:column] = getpos('.')[1:2]
let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)
- if !a:manual && empty(l:prefix)
+ if a:source is# 'ale-automatic' && empty(l:prefix)
return
endif
@@ -537,8 +567,9 @@ function! ale#completion#GetCompletions(manual) abort
\ 'prefix': l:prefix,
\ 'conn_id': 0,
\ 'request_id': 0,
- \ 'manual': a:manual,
+ \ 'source': a:source,
\}
+ unlet! b:ale_completion_result
let l:buffer = bufnr('')
let l:Callback = function('s:OnReady')
@@ -562,7 +593,7 @@ function! s:TimerHandler(...) abort
" When running the timer callback, we have to be sure that the cursor
" hasn't moved from where it was when we requested completions by typing.
if s:timer_pos == [l:line, l:column] && ale#util#Mode() is# 'i'
- call ale#completion#GetCompletions(0)
+ call ale#completion#GetCompletions('ale-automatic')
endif
endfunction
diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim
index 521f0b45..3915cac1 100644
--- a/autoload/ale/definition.vim
+++ b/autoload/ale/definition.vim
@@ -3,6 +3,9 @@
let s:go_to_definition_map = {}
+" Enable automatic updates of the tagstack
+let g:ale_update_tagstack = get(g:, 'ale_update_tagstack', 1)
+
" Used to get the definition map in tests.
function! ale#definition#GetMap() abort
return deepcopy(s:go_to_definition_map)
@@ -17,6 +20,20 @@ function! ale#definition#ClearLSPData() abort
let s:go_to_definition_map = {}
endfunction
+function! ale#definition#UpdateTagStack() abort
+ let l:should_update_tagstack = exists('*gettagstack') && exists('*settagstack') && g:ale_update_tagstack
+
+ if l:should_update_tagstack
+ " Grab the old location (to jump back to) and the word under the
+ " cursor (as a label for the tagstack)
+ let l:old_location = [bufnr('%'), line('.'), col('.'), 0]
+ let l:tagname = expand('<cword>')
+ let l:winid = win_getid()
+ call settagstack(l:winid, {'items': [{'from': l:old_location, 'tagname': l:tagname}]}, 'a')
+ call settagstack(l:winid, {'curidx': len(gettagstack(l:winid)['items']) + 1})
+ endif
+endfunction
+
function! ale#definition#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') is# 'definition'
\&& has_key(s:go_to_definition_map, a:response.request_seq)
@@ -27,6 +44,7 @@ function! ale#definition#HandleTSServerResponse(conn_id, response) abort
let l:line = a:response.body[0].start.line
let l:column = a:response.body[0].start.offset
+ call ale#definition#UpdateTagStack()
call ale#util#Open(l:filename, l:line, l:column, l:options)
endif
endif
@@ -51,6 +69,7 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort
let l:line = l:item.range.start.line + 1
let l:column = l:item.range.start.character + 1
+ call ale#definition#UpdateTagStack()
call ale#util#Open(l:filename, l:line, l:column, l:options)
break
endfor
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 6b312b4f..7db808d6 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -39,8 +39,8 @@ function! ale#engine#MarkLinterActive(info, linter) abort
endif
endfunction
-function! ale#engine#MarkLinterInactive(info, linter) abort
- call filter(a:info.active_linter_list, 'v:val.name isnot# a:linter.name')
+function! ale#engine#MarkLinterInactive(info, linter_name) abort
+ call filter(a:info.active_linter_list, 'v:val.name isnot# a:linter_name')
endfunction
function! ale#engine#ResetExecutableCache() abort
@@ -195,7 +195,7 @@ function! s:HandleExit(job_info, buffer, output, data) abort
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)
+ call ale#engine#MarkLinterInactive(l:buffer_info, l:linter.name)
" Stop here if we land in the handle for a job completing if we're in
" a sandbox.
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index c0f05611..92ae3e14 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -1,3 +1,5 @@
+call ale#Set('fix_on_save_ignore', {})
+
" Apply fixes queued up for buffers which may be hidden.
" Vim doesn't let you modify hidden buffers.
function! ale#fix#ApplyQueuedFixes() abort
@@ -265,7 +267,21 @@ function! s:AddSubCallbacks(full_list, callbacks) abort
return 1
endfunction
-function! s:GetCallbacks(buffer, fixers) abort
+function! s:IgnoreFixers(callback_list, filetype, config) abort
+ if type(a:config) is v:t_list
+ let l:ignore_list = a:config
+ else
+ let l:ignore_list = []
+
+ for l:part in split(a:filetype , '\.')
+ call extend(l:ignore_list, get(a:config, l:part, []))
+ endfor
+ endif
+
+ call filter(a:callback_list, 'index(l:ignore_list, v:val) < 0')
+endfunction
+
+function! s:GetCallbacks(buffer, fixing_flag, fixers) abort
if len(a:fixers)
let l:callback_list = a:fixers
elseif type(get(b:, 'ale_fixers')) is v:t_list
@@ -290,8 +306,12 @@ function! s:GetCallbacks(buffer, fixers) abort
endif
endif
- if empty(l:callback_list)
- return []
+ if a:fixing_flag is# 'save_file'
+ let l:config = ale#Var(a:buffer, 'fix_on_save_ignore')
+
+ if !empty(l:config)
+ call s:IgnoreFixers(l:callback_list, &filetype, l:config)
+ endif
endif
let l:corrected_list = []
@@ -339,7 +359,7 @@ function! ale#fix#Fix(buffer, fixing_flag, ...) abort
endif
try
- let l:callback_list = s:GetCallbacks(a:buffer, a:000)
+ let l:callback_list = s:GetCallbacks(a:buffer, a:fixing_flag, a:000)
catch /E700\|BADNAME/
let l:function_name = join(split(split(v:exception, ':')[3]))
let l:echo_message = printf(
diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim
index 430dc7db..4d28272a 100644
--- a/autoload/ale/fix/registry.vim
+++ b/autoload/ale/fix/registry.vim
@@ -290,6 +290,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['kt'],
\ 'description': 'Fix Kotlin files with ktlint.',
\ },
+\ 'styler': {
+\ 'function': 'ale#fixers#styler#Fix',
+\ 'suggested_filetypes': ['r'],
+\ 'description': 'Fix R files with styler.',
+\ },
\ 'latexindent': {
\ 'function': 'ale#fixers#latexindent#Fix',
\ 'suggested_filetypes': ['tex'],
diff --git a/autoload/ale/fixers/styler.vim b/autoload/ale/fixers/styler.vim
new file mode 100644
index 00000000..7ff3275c
--- /dev/null
+++ b/autoload/ale/fixers/styler.vim
@@ -0,0 +1,16 @@
+" Author: tvatter <thibault.vatter@gmail.com>
+" Description: Fixing R files with styler.
+
+call ale#Set('r_styler_executable', 'Rscript')
+call ale#Set('r_styler_options', 'tidyverse_style')
+
+function! ale#fixers#styler#Fix(buffer) abort
+ return {
+ \ 'command': 'Rscript --vanilla -e '
+ \ . '"suppressPackageStartupMessages(library(styler));'
+ \ . 'style_file(commandArgs(TRUE), style = '
+ \ . ale#Var(a:buffer, 'r_styler_options') . ')"'
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/highlight.vim b/autoload/ale/highlight.vim
index ae1f3e7d..3e09f26d 100644
--- a/autoload/ale/highlight.vim
+++ b/autoload/ale/highlight.vim
@@ -26,6 +26,41 @@ endif
let s:MAX_POS_VALUES = 8
let s:MAX_COL_SIZE = 1073741824 " pow(2, 30)
+" Check if we have neovim's buffer highlight API
+"
+" Below we define some functions' implementation conditionally if this API
+" exists or not.
+"
+" The API itself is more ergonomic and neovim performs highlights positions
+" rebases during edits so we see less stalled highlights.
+let s:nvim_api = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace')
+
+function! ale#highlight#HasNeovimApi() abort
+ return s:nvim_api
+endfunction
+
+function! ale#highlight#nvim_buf_clear_namespace(...) abort
+ return call('nvim_buf_clear_namespace', a:000)
+endfunction
+
+function! ale#highlight#nvim_buf_add_highlight(...) abort
+ return call('nvim_buf_add_highlight', a:000)
+endfunction
+
+function! s:ale_nvim_highlight_id(bufnr) abort
+ let l:id = getbufvar(a:bufnr, 'ale_nvim_highlight_id', -1)
+
+ if l:id is -1
+ " NOTE: This will highlight nothing but will allocate new id
+ let l:id = ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, 0, '', 0, 0, -1
+ \)
+ call setbufvar(a:bufnr, 'ale_nvim_highlight_id', l:id)
+ endif
+
+ return l:id
+endfunction
+
function! ale#highlight#CreatePositions(line, col, end_line, end_col) abort
if a:line >= a:end_line
" For single lines, just return the one position.
@@ -49,12 +84,90 @@ endfunction
" Given a loclist for current items to highlight, remove all highlights
" except these which have matching loclist item entries.
+
function! ale#highlight#RemoveHighlights() abort
- for l:match in getmatches()
- if l:match.group =~# '^ALE'
- call matchdelete(l:match.id)
+ if ale#highlight#HasNeovimApi()
+ if get(b:, 'ale_nvim_highlight_id', 0)
+ let l:bufnr = bufnr('%')
+ " NOTE: 0, -1 means from 0 line till the end of buffer
+ call ale#highlight#nvim_buf_clear_namespace(
+ \ l:bufnr,
+ \ b:ale_nvim_highlight_id,
+ \ 0, -1
+ \)
endif
- endfor
+ else
+ for l:match in getmatches()
+ if l:match.group =~# '^ALE'
+ call matchdelete(l:match.id)
+ endif
+ endfor
+ endif
+endfunction
+
+function! s:highlight_line(bufnr, lnum, group) abort
+ if ale#highlight#HasNeovimApi()
+ let l:highlight_id = s:ale_nvim_highlight_id(a:bufnr)
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ a:lnum, 0, -1
+ \)
+ else
+ call matchaddpos(a:group, [a:lnum])
+ endif
+endfunction
+
+function! s:highlight_range(bufnr, range, group) abort
+ if ale#highlight#HasNeovimApi()
+ let l:highlight_id = s:ale_nvim_highlight_id(a:bufnr)
+ " NOTE: lines and columns indicies are 0-based in nvim_buf_* API.
+ let l:lnum = a:range.lnum - 1
+ let l:end_lnum = a:range.end_lnum - 1
+ let l:col = a:range.col - 1
+ let l:end_col = a:range.end_col
+
+ if l:lnum >= l:end_lnum
+ " For single lines, just return the one position.
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:lnum, l:col, l:end_col
+ \)
+ else
+ " highlight first line from start till the line end
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:lnum, l:col, -1
+ \)
+
+ " highlight all lines between the first and last entirely
+ let l:cur = l:lnum + 1
+
+ while l:cur < l:end_lnum
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:cur, 0, -1
+ \ )
+ let l:cur += 1
+ endwhile
+
+ call ale#highlight#nvim_buf_add_highlight(
+ \ a:bufnr, l:highlight_id, a:group,
+ \ l:end_lnum, 0, l:end_col
+ \)
+ endif
+ else
+ " Set all of the positions, which are chunked into Lists which
+ " are as large as will be accepted by matchaddpos.
+ call map(
+ \ ale#highlight#CreatePositions(
+ \ a:range.lnum,
+ \ a:range.col,
+ \ a:range.end_lnum,
+ \ a:range.end_col
+ \ ),
+ \ 'matchaddpos(a:group, v:val)'
+ \)
+ endif
endfunction
function! ale#highlight#UpdateHighlights() abort
@@ -79,17 +192,14 @@ function! ale#highlight#UpdateHighlights() abort
let l:group = 'ALEError'
endif
- let l:line = l:item.lnum
- let l:col = l:item.col
- let l:end_line = get(l:item, 'end_lnum', l:line)
- let l:end_col = get(l:item, 'end_col', l:col)
+ let l:range = {
+ \ 'lnum': l:item.lnum,
+ \ 'col': l:item.col,
+ \ 'end_lnum': get(l:item, 'end_lnum', l:item.lnum),
+ \ 'end_col': get(l:item, 'end_col', l:item.col)
+ \}
- " Set all of the positions, which are chunked into Lists which
- " are as large as will be accepted by matchaddpos.
- call map(
- \ ale#highlight#CreatePositions(l:line, l:col, l:end_line, l:end_col),
- \ 'matchaddpos(l:group, v:val)'
- \)
+ call s:highlight_range(l:item.bufnr, l:range, l:group)
endfor
" If highlights are enabled and signs are not enabled, we should still
@@ -111,7 +221,7 @@ function! ale#highlight#UpdateHighlights() abort
endif
if l:available_groups[l:group]
- call matchaddpos(l:group, [l:item.lnum])
+ call s:highlight_line(l:item.bufnr, l:item.lnum, l:group)
endif
endfor
endif
diff --git a/autoload/ale/lsp/response.vim b/autoload/ale/lsp/response.vim
index b91d875b..9ce05260 100644
--- a/autoload/ale/lsp/response.vim
+++ b/autoload/ale/lsp/response.vim
@@ -28,7 +28,7 @@ function! ale#lsp#response#ReadDiagnostics(response) abort
for l:diagnostic in a:response.params.diagnostics
let l:severity = get(l:diagnostic, 'severity', 0)
let l:loclist_item = {
- \ 'text': l:diagnostic.message,
+ \ 'text': substitute(l:diagnostic.message, '\(\r\n\|\n\|\r\)', ' ', 'g'),
\ 'type': 'E',
\ 'lnum': l:diagnostic.range.start.line + 1,
\ 'col': l:diagnostic.range.start.character + 1,
diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim
index d544916a..3a596d62 100644
--- a/autoload/ale/lsp_linter.vim
+++ b/autoload/ale/lsp_linter.vim
@@ -27,12 +27,13 @@ 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:info = get(g:ale_buffer_info, l:buffer, {})
- if s:ShouldIgnore(l:buffer, l:linter_name)
+ if empty(l:info)
return
endif
- if l:buffer <= 0
+ if s:ShouldIgnore(l:buffer, l:linter_name)
return
endif
@@ -50,6 +51,8 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort
return
endif
+ call ale#engine#MarkLinterInactive(l:info, l:linter_name)
+
if s:ShouldIgnore(l:buffer, l:linter_name)
return
endif
@@ -376,6 +379,10 @@ function! s:CheckWithLSP(linter, details) abort
if a:linter.lsp is# 'tsserver'
let l:message = ale#lsp#tsserver_message#Geterr(l:buffer)
let l:notified = ale#lsp#Send(l:id, l:message) != 0
+
+ if l:notified
+ call ale#engine#MarkLinterActive(l:info, a:linter)
+ endif
else
let l:notified = ale#lsp#NotifyForChanges(l:id, l:buffer)
endif
@@ -386,10 +393,6 @@ function! s:CheckWithLSP(linter, details) abort
let l:save_message = ale#lsp#message#DidSave(l:buffer)
let l:notified = ale#lsp#Send(l:id, l:save_message) != 0
endif
-
- if l:notified
- call ale#engine#MarkLinterActive(l:info, a:linter)
- endif
endfunction
function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim
index 933fc055..7395b0e2 100644
--- a/autoload/ale/sign.vim
+++ b/autoload/ale/sign.vim
@@ -66,7 +66,7 @@ endif
" Spaces and backslashes need to be escaped for signs.
function! s:EscapeSignText(sign_text) abort
- return substitute(a:sign_text, '\\\| ', '\\\0', 'g')
+ return substitute(substitute(a:sign_text, ' *$', '', ''), '\\\| ', '\\\0', 'g')
endfunction
" Signs show up on the left for error markers.
diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim
index e57307e4..e7563608 100644
--- a/autoload/ale/util.vim
+++ b/autoload/ale/util.vim
@@ -422,7 +422,7 @@ 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) " no-custom-checks
+ call writefile(l:corrected_lines, a:filename, 'S') " no-custom-checks
endfunction
if !exists('s:patial_timers')
diff --git a/autoload/ale/virtualtext.vim b/autoload/ale/virtualtext.vim
index 532427fb..d7c5360e 100644
--- a/autoload/ale/virtualtext.vim
+++ b/autoload/ale/virtualtext.vim
@@ -81,7 +81,7 @@ function! ale#virtualtext#ShowCursorWarning(...) abort
call ale#virtualtext#Clear()
if !empty(l:loc)
- let l:msg = get(l:loc, 'detail', l:loc.text)
+ let l:msg = l:loc.text
let l:hl_group = 'ALEVirtualTextInfo'
let l:type = get(l:loc, 'type', 'E')