summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ale_linters/c/flawfinder.vim3
-rw-r--r--ale_linters/cpp/flawfinder.vim2
-rw-r--r--autoload/ale/definition.vim4
-rw-r--r--autoload/ale/engine.vim13
-rw-r--r--autoload/ale/handlers/flawfinder.vim47
-rw-r--r--autoload/ale/handlers/gcc.vim2
-rw-r--r--autoload/ale/lsp.vim14
-rw-r--r--autoload/ale/lsp/reset.vim25
-rw-r--r--doc/ale-c.txt8
-rw-r--r--doc/ale.txt9
-rw-r--r--plugin/ale.vim2
-rw-r--r--test/handler/test_flawfinder_handler.vader57
-rw-r--r--test/lsp/test_reset_lsp.vader90
13 files changed, 269 insertions, 7 deletions
diff --git a/ale_linters/c/flawfinder.vim b/ale_linters/c/flawfinder.vim
index 27f269f5..df6fbebe 100644
--- a/ale_linters/c/flawfinder.vim
+++ b/ale_linters/c/flawfinder.vim
@@ -4,6 +4,7 @@
call ale#Set('c_flawfinder_executable', 'flawfinder')
call ale#Set('c_flawfinder_options', '')
call ale#Set('c_flawfinder_minlevel', 1)
+call ale#Set('c_flawfinder_error_severity', 6)
function! ale_linters#c#flawfinder#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'c_flawfinder_executable')
@@ -26,5 +27,5 @@ call ale#linter#Define('c', {
\ 'output_stream': 'stdout',
\ 'executable_callback': 'ale_linters#c#flawfinder#GetExecutable',
\ 'command_callback': 'ale_linters#c#flawfinder#GetCommand',
-\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\ 'callback': 'ale#handlers#flawfinder#HandleFlawfinderFormat',
\})
diff --git a/ale_linters/cpp/flawfinder.vim b/ale_linters/cpp/flawfinder.vim
index a19f5962..c63ecb38 100644
--- a/ale_linters/cpp/flawfinder.vim
+++ b/ale_linters/cpp/flawfinder.vim
@@ -26,5 +26,5 @@ call ale#linter#Define('cpp', {
\ 'output_stream': 'stdout',
\ 'executable_callback': 'ale_linters#cpp#flawfinder#GetExecutable',
\ 'command_callback': 'ale_linters#cpp#flawfinder#GetCommand',
-\ 'callback': 'ale#handlers#gcc#HandleGCCFormat',
+\ 'callback': 'ale#handlers#flawfinder#HandleFlawfinderFormat',
\})
diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim
index b20c01a0..521cd0b1 100644
--- a/autoload/ale/definition.vim
+++ b/autoload/ale/definition.vim
@@ -19,6 +19,10 @@ function! ale#definition#Execute(expr) abort
execute a:expr
endfunction
+function! ale#definition#ClearLSPData() abort
+ let s:go_to_definition_map = {}
+endfunction
+
function! ale#definition#Open(options, filename, line, column) abort
if a:options.open_in_tab
call ale#definition#Execute('tabedit ' . fnameescape(a:filename))
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 6ccc3a34..89169874 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -76,6 +76,11 @@ function! ale#engine#InitBufferInfo(buffer) abort
return 0
endfunction
+" Clear LSP linter data for the linting engine.
+function! ale#engine#ClearLSPData() abort
+ let s:lsp_linter_map = {}
+endfunction
+
" This function is documented and part of the public API.
"
" Return 1 if ALE is busy checking a given buffer
@@ -144,7 +149,7 @@ function! s:GatherOutput(job_id, line) abort
endif
endfunction
-function! s:HandleLoclist(linter_name, buffer, loclist) abort
+function! ale#engine#HandleLoclist(linter_name, buffer, loclist) abort
let l:info = get(g:ale_buffer_info, a:buffer, {})
if empty(l:info)
@@ -223,7 +228,7 @@ function! s:HandleExit(job_id, exit_code) abort
let l:loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output)
- call s:HandleLoclist(l:linter.name, l:buffer, l:loclist)
+ call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist)
endfunction
function! s:HandleLSPDiagnostics(conn_id, response) abort
@@ -237,7 +242,7 @@ function! s:HandleLSPDiagnostics(conn_id, response) abort
let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
- call s:HandleLoclist(l:linter_name, l:buffer, l:loclist)
+ call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist)
endfunction
function! s:HandleTSServerDiagnostics(response, error_type) abort
@@ -262,7 +267,7 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort
let l:loclist = get(l:info, 'semantic_loclist', [])
\ + get(l:info, 'syntax_loclist', [])
- call s:HandleLoclist('tsserver', l:buffer, l:loclist)
+ call ale#engine#HandleLoclist('tsserver', l:buffer, l:loclist)
endfunction
function! s:HandleLSPErrorMessage(error_message) abort
diff --git a/autoload/ale/handlers/flawfinder.vim b/autoload/ale/handlers/flawfinder.vim
new file mode 100644
index 00000000..a650d6dd
--- /dev/null
+++ b/autoload/ale/handlers/flawfinder.vim
@@ -0,0 +1,47 @@
+" Author: Christian Gibbons <cgibbons@gmu.edu>
+" Description: This file defines a handler function that should work for the
+" flawfinder format with the -CDQS flags.
+
+" Swiped this function from the GCC handler. Not sure if needed, but doesn't
+" hurt to have it.
+function! s:RemoveUnicodeQuotes(text) abort
+ let l:text = a:text
+ let l:text = substitute(l:text, '[`´‘’]', '''', 'g')
+ let l:text = substitute(l:text, '\v\\u2018([^\\]+)\\u2019', '''\1''', 'g')
+ let l:text = substitute(l:text, '[“”]', '"', 'g')
+
+ return l:text
+endfunction
+
+function! ale#handlers#flawfinder#HandleFlawfinderFormat(buffer, lines) abort
+ " Look for lines like the following.
+ "
+ " <stdin>:12:4: [2] (buffer) char:Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120). Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length.
+ " <stdin>:31:4: [1] (buffer) strncpy:Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120).
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ( \[[0-5]\] [^:]+):(.+)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ " Use severity level to determine if it should be considered a warning
+ " or error.
+ let l:severity = str2nr(matchstr(split(l:match[4])[0], '[0-5]'))
+
+ let l:item = {
+ \ 'lnum': str2nr(l:match[2]),
+ \ 'col': str2nr(l:match[3]),
+ \ 'type': (l:severity < ale#Var(a:buffer, 'c_flawfinder_error_severity'))
+ \ ? 'W' : 'E',
+ \ 'text': s:RemoveUnicodeQuotes(join(split(l:match[4])[1:]) . ': ' . l:match[5]),
+ \}
+
+ " If the filename is something like <stdin>, <nofile> or -, then
+ " this is an error for the file we checked.
+ if l:match[1] isnot# '-' && l:match[1][0] isnot# '<'
+ let l:item['filename'] = l:match[1]
+ endif
+
+ call add(l:output, l:item)
+ endfor
+
+ return l:output
+endfunction
diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim
index 7f2078a4..9ec7b110 100644
--- a/autoload/ale/handlers/gcc.vim
+++ b/autoload/ale/handlers/gcc.vim
@@ -24,7 +24,7 @@ function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort
" <stdin>:8:5: warning: conversion lacks type at end of format [-Wformat=]
" <stdin>:10:27: error: invalid operands to binary - (have ‘int’ and ‘char *’)
" -:189:7: note: $/${} is unnecessary on arithmetic variables. [SC2004]
- let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): ?(.+)$'
+ let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+):(\d+)?:? ([^:]+): (.+)$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index 126d6c18..8db9348f 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -325,6 +325,20 @@ function! ale#lsp#ConnectToAddress(address, project_root, callback) abort
return 1
endfunction
+" Stop all LSP connections, closing all jobs and channels, and removing any
+" queued messages.
+function! ale#lsp#StopAll() abort
+ for l:conn in s:connections
+ if has_key(l:conn, 'channel')
+ call ch_close(l:conn.channel)
+ else
+ call ale#job#Stop(l:conn.id)
+ endif
+ endfor
+
+ let s:connections = []
+endfunction
+
function! s:SendMessageData(conn, data) abort
if has_key(a:conn, 'executable')
call ale#job#SendRaw(a:conn.id, a:data)
diff --git a/autoload/ale/lsp/reset.vim b/autoload/ale/lsp/reset.vim
new file mode 100644
index 00000000..c206ed08
--- /dev/null
+++ b/autoload/ale/lsp/reset.vim
@@ -0,0 +1,25 @@
+" Stop all LSPs and remove all of the data for them.
+function! ale#lsp#reset#StopAllLSPs() abort
+ call ale#lsp#StopAll()
+
+ if exists('*ale#definition#ClearLSPData')
+ " Clear the mapping for connections, etc.
+ call ale#definition#ClearLSPData()
+ endif
+
+ if exists('*ale#engine#ClearLSPData')
+ " Clear the mapping for connections, etc.
+ call ale#engine#ClearLSPData()
+
+ " Remove the problems for all of the LSP linters in every buffer.
+ for l:buffer_string in keys(g:ale_buffer_info)
+ let l:buffer = str2nr(l:buffer_string)
+
+ for l:linter in ale#linter#Get(getbufvar(l:buffer, '&filetype'))
+ if !empty(l:linter.lsp)
+ call ale#engine#HandleLoclist(l:linter.name, l:buffer, [])
+ endif
+ endfor
+ endfor
+ endif
+endfunction
diff --git a/doc/ale-c.txt b/doc/ale-c.txt
index cf483fb5..08b83e80 100644
--- a/doc/ale-c.txt
+++ b/doc/ale-c.txt
@@ -169,6 +169,14 @@ g:ale_c_flawfinder_options *g:ale-c-flawfinder*
This variable can be used to pass extra options into the flawfinder command.
+g:ale_c_flawfinder_error_severity *g:ale_c_flawfinder_error_severity*
+ *b:ale_c_flawfinder_error_severity*
+ Type: |Number|
+ Default: `6`
+
+ This variable can be changed to set the minimum severity to be treated as an
+ error. This setting also applies to flawfinder for c++.
+
===============================================================================
gcc *ale-c-gcc*
diff --git a/doc/ale.txt b/doc/ale.txt
index 95c3c0e1..2e98cd71 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -1871,6 +1871,15 @@ ALEResetBuffer *ALEResetBuffer*
|ALEDisableBuffer|.
+ALEStopAllLSPs *ALEStopAllLSPs*
+
+ `ALEStopAllLSPs` will close and stop all channels and jobs for all LSP-like
+ clients, including tsserver, remove all of the data stored for them, and
+ delete all of the problems found for them, updating every linted buffer.
+
+ This command can be used when LSP clients mess up and need to be restarted.
+
+
===============================================================================
9. API *ale-api*
diff --git a/plugin/ale.vim b/plugin/ale.vim
index 69c08496..1aa35826 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -249,6 +249,8 @@ command! -bar ALEToggleBuffer :call ale#toggle#ToggleBuffer(bufnr(''))
command! -bar ALEEnableBuffer :call ale#toggle#EnableBuffer(bufnr(''))
command! -bar ALEDisableBuffer :call ale#toggle#DisableBuffer(bufnr(''))
command! -bar ALEResetBuffer :call ale#toggle#ResetBuffer(bufnr(''))
+" A command to stop all LSP-like clients, including tsserver.
+command! -bar ALEStopAllLSPs :call ale#lsp#reset#StopAllLSPs()
" A command for linting manually.
command! -bar ALELint :call ale#Queue(0, 'lint_file')
diff --git a/test/handler/test_flawfinder_handler.vader b/test/handler/test_flawfinder_handler.vader
new file mode 100644
index 00000000..708bac2a
--- /dev/null
+++ b/test/handler/test_flawfinder_handler.vader
@@ -0,0 +1,57 @@
+Before:
+ Save g:ale_c_flawfinder_error_severity
+
+ unlet! g:ale_c_flawfinder_error_severity
+ unlet! b:ale_c_flawfinder_error_severity
+
+ runtime ale_linters/c/flawfinder.vim
+
+After:
+ unlet! g:ale_c_flawfinder_error_severity
+ Restore
+
+Execute(The Flawfinder handler should ignore other lines of output):
+ AssertEqual
+ \ [],
+ \ ale#handlers#flawfinder#HandleFlawfinderFormat(347, [
+ \ 'foo',
+ \ 'bar',
+ \ 'baz',
+ \ ])
+
+Execute(The Flawfinder handler should work):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 31,
+ \ 'col': 4,
+ \ 'type': 'W',
+ \ 'text': "(buffer) strncpy: Easily used incorrectly",
+ \ },
+ \ ],
+ \ ale#handlers#flawfinder#HandleFlawfinderFormat(347, [
+ \ "<stdin>:31:4: [1] (buffer) strncpy:Easily used incorrectly",
+ \ ])
+
+Execute(The Flawfinder error severity level should be configurable):
+ let b:ale_c_flawfinder_error_severity = 2
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 12,
+ \ 'col': 4,
+ \ 'type': 'E',
+ \ 'text': "(buffer) char: Statically-sized arrays can be bad",
+ \ },
+ \ {
+ \ 'lnum': 31,
+ \ 'col': 4,
+ \ 'type': 'W',
+ \ 'text': "(buffer) strncpy: Easily used incorrectly",
+ \ },
+ \ ],
+ \ ale#handlers#flawfinder#HandleFlawfinderFormat(bufnr(''), [
+ \ "<stdin>:12:4: [2] (buffer) char:Statically-sized arrays can be bad",
+ \ "<stdin>:31:4: [1] (buffer) strncpy:Easily used incorrectly",
+ \ ])
diff --git a/test/lsp/test_reset_lsp.vader b/test/lsp/test_reset_lsp.vader
new file mode 100644
index 00000000..2bec13dc
--- /dev/null
+++ b/test/lsp/test_reset_lsp.vader
@@ -0,0 +1,90 @@
+Before:
+ Save g:ale_enabled
+ Save g:ale_set_signs
+ Save g:ale_set_quickfix
+ Save g:ale_set_loclist
+ Save g:ale_set_highlights
+ Save g:ale_echo_cursor
+
+ let g:ale_enabled = 0
+ let g:ale_set_signs = 0
+ let g:ale_set_quickfix = 0
+ let g:ale_set_loclist = 0
+ let g:ale_set_highlights = 0
+ let g:ale_echo_cursor = 0
+
+ function EmptyString() abort
+ return ''
+ endfunction
+
+ call ale#engine#InitBufferInfo(bufnr(''))
+
+ call ale#linter#Define('testft', {
+ \ 'name': 'lsplinter',
+ \ 'lsp': 'tsserver',
+ \ 'executable_callback': 'EmptyString',
+ \ 'command_callback': 'EmptyString',
+ \ 'project_root_callback': 'EmptyString',
+ \ 'language_callback': 'EmptyString',
+ \})
+
+ call ale#linter#Define('testft', {
+ \ 'name': 'otherlinter',
+ \ 'callback': 'TestCallback',
+ \ 'executable': has('win32') ? 'cmd': 'true',
+ \ 'command': 'true',
+ \ 'read_buffer': 0,
+ \})
+
+After:
+ Restore
+
+ unlet! b:ale_save_event_fired
+
+ delfunction EmptyString
+ call ale#linter#Reset()
+
+Given testft(Some file with an imaginary filetype):
+Execute(ALEStopAllLSPs should clear the loclist):
+ let g:ale_buffer_info[bufnr('')].loclist = [
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 10,
+ \ 'col': 0,
+ \ 'bufnr': bufnr(''),
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'lsplinter',
+ \ },
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 10,
+ \ 'col': 0,
+ \ 'bufnr': bufnr(''),
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'otherlinter',
+ \ },
+ \]
+ let g:ale_buffer_info[bufnr('')].active_linter_list = ['lsplinter', 'otherlinter']
+
+ ALEStopAllLSPs
+
+ " The loclist should be updated.
+ AssertEqual g:ale_buffer_info[bufnr('')].loclist, [
+ \ {
+ \ 'text': 'a',
+ \ 'lnum': 10,
+ \ 'col': 0,
+ \ 'bufnr': bufnr(''),
+ \ 'vcol': 0,
+ \ 'type': 'E',
+ \ 'nr': -1,
+ \ 'linter_name': 'otherlinter',
+ \ },
+ \]
+
+ " The LSP linter should be removed from the active linter list.
+ AssertEqual g:ale_buffer_info[bufnr('')].active_linter_list, ['otherlinter']