dense-analysis / ale

Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
BSD 2-Clause "Simplified" License
13.56k stars 1.44k forks source link

ALE linting on text changed with g:ale_lint_on_text_changed=0 #3555

Closed willeccles closed 3 years ago

willeccles commented 3 years ago

Information

VIM version

NVIM v0.5.0-dev+nightly-1-g1f8de50-dirty Build type: Release

Operating System: I have experienced this on Linux (both Debian and Void) running in WSL and natively on macOS (all using exactly identical configs).

What went wrong

My configuration looks like this:

let g:ale_completion_enabled=1
let g:ale_pattern_options={ '\.h$': { ..... } } "custom linters for header files
let g:ale_c_cc_options='-Wall -pedantic -std=c11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700'
let g:ale_hover_cursor=0
let g:ale_lint_on_text_changed=0
let g:ale_lint_on_insert_leave=0
let g:ale_lint_on_save=1

Reproducing the bug

When I write text in insert mode, ALE immediately updates all the time, which is causing noticeable latency when typing. However, this does not always happen. It seems to happen only sometimes and not others, and I can't find any patterns here.

Of course, I can get around this by disabling ALE and only invoking it manually, but this is quite painful compared to just having ALE lint when I write the buffer.

:ALEInfo

 ALEInfo
 Current Filetype: c
Available Linters: ['cc', 'ccls', 'clangd', 'clangtidy', 'cppcheck', 'cquery', 'flawfinder']
   Linter Aliases:
'cc' -> ['gcc', 'clang']
  Enabled Linters: ['cc', 'ccls', 'clangd', 'clangtidy', 'cppcheck', 'cquery', 'flawfinder']
  Ignored Linters: []
 Suggested Fixers:
  'astyle' - Fix C/C++ with astyle.
  'clang-format' - Fix C/C++ and cuda files with clang-format.
  'clangtidy' - Fix C/C++ and ObjectiveC files with clang-tidy.
  'remove_trailing_lines' - Remove all blank lines at the end of a file.
  'trim_whitespace' - Remove all trailing whitespace characters at the end of every line.
  'uncrustify' - Fix C, C++, C#, ObjectiveC, ObjectiveC++, D, Java, Pawn, and VALA files with uncrustify.
 Linter Variables:

let g:ale_c_always_make = 1
let g:ale_c_build_dir = ''
let g:ale_c_build_dir_names = ['build', 'bin']
let g:ale_c_cc_executable = '<auto>'
let g:ale_c_cc_options = '-Wall -pedantic -std=c11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700'
let g:ale_c_ccls_executable = 'ccls'
let g:ale_c_ccls_init_options = {}
let g:ale_c_clangd_executable = 'clangd'
let g:ale_c_clangd_options = ''
let g:ale_c_clangtidy_checks = []
let g:ale_c_clangtidy_executable = 'clang-tidy'
let g:ale_c_clangtidy_extra_options = ''
let g:ale_c_clangtidy_options = ''
let g:ale_c_cppcheck_executable = 'cppcheck'
let g:ale_c_cppcheck_options = '--enable=style'
let g:ale_c_cquery_cache_directory = '/home/cactus/.cache/cquery'
let g:ale_c_cquery_executable = 'cquery'
let g:ale_c_flawfinder_error_severity = 6
let g:ale_c_flawfinder_executable = 'flawfinder'
let g:ale_c_flawfinder_minlevel = 1
let g:ale_c_flawfinder_options = ''
let g:ale_c_cquery_cache_directory = '/home/cactus/.cache/cquery'
let g:ale_c_cquery_executable = 'cquery'
let g:ale_c_flawfinder_error_severity = 6
let g:ale_c_flawfinder_executable = 'flawfinder'
let g:ale_c_flawfinder_minlevel = 1
let g:ale_c_flawfinder_options = ''
let g:ale_c_parse_compile_commands = 1
let g:ale_c_parse_makefile = 0
 Global Variables:
let g:ale_cache_executable_check_failures = v:null
let g:ale_change_sign_column_color = 0
let g:ale_command_wrapper = ''
let g:ale_completion_delay = 100
let g:ale_completion_enabled = 1
let g:ale_completion_max_suggestions = 50
let g:ale_disable_lsp = 0
let g:ale_echo_cursor = 1
let g:ale_echo_msg_error_str = 'Error'
let g:ale_echo_msg_format = '%code: %%s'
let g:ale_echo_msg_info_str = 'Info'
let g:ale_echo_msg_warning_str = 'Warning'
let g:ale_enabled = 1
let g:ale_fix_on_save = 0
let g:ale_fixers = {}
let g:ale_history_enabled = 1
let g:ale_history_log_output = 1
let g:ale_keep_list_window_open = 0
let g:ale_lint_delay = 200
let g:ale_lint_on_enter = 1
let g:ale_lint_on_filetype_changed = 1
let g:ale_lint_on_insert_leave = 0
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 0
let g:ale_linter_aliases = {}
let g:ale_linters = {}
let g:ale_linters_explicit = 0
let g:ale_linters_ignore = {}
let g:ale_list_vertical = 0
let g:ale_list_window_size = 10
let g:ale_loclist_msg_format = '%code: %%s'
let g:ale_lsp_root = {}
let g:ale_max_buffer_history_size = 20
let g:ale_max_signs = -1
let g:ale_maximum_file_size = v:null
let g:ale_open_list = 0
let g:ale_pattern_options = {'\.h$': {'ale_linters': ['ccls', 'clang', 'clangcheck', 'clangd', 'clangtidy', 'clazy', 'cppcheck', 'cpplint', 'cquery', 'flawfinder', 'gcc']}}
let g:ale_pattern_options_enabled = v:null
let g:ale_set_balloons = 0
let g:ale_sign_error = '>>'
let g:ale_sign_info = '--'
let g:ale_sign_offset = 1000000
let g:ale_sign_style_error = '>>'
let g:ale_sign_style_warning = '--'
let g:ale_sign_warning = '--'
let g:ale_sign_highlight_linenrs = 0
let g:ale_statusline_format = v:null
let g:ale_type_map = {}
let g:ale_use_global_executables = v:null
let g:ale_virtualtext_cursor = 0
let g:ale_warn_about_trailing_blank_lines = 1
let g:ale_warn_about_trailing_whitespace = 1
  Command History:

(executable check - success) clang
(finished - exit code 0) ['/bin/zsh', '-c', '''clang'' -S -x c -o /dev/null -iquote ''/redacted/path/to/src'' -Wall -pedantic -std=c11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 - < ''/tmp/nvimJjKnxH/2/main.c''']

<<<NO OUTPUT RETURNED>>>

(executable check - failure) ccls
(executable check - success) clangd
(executable check - success) clang-tidy
(finished - exit code 0) ['/bin/zsh', '-c', '''clang-tidy'' ''/redacted/path/to/src/main.c''']

<<<NO OUTPUT RETURNED>>>

(executable check - failure) cppcheck
(executable check - failure) cquery
(executable check - failure) flawfinder
(started) ['/bin/zsh', '-c', '''clangd''']
(executable check - failure) ccls
(executable check - failure) cquery
(executable check - failure) ccls
(executable check - failure) cquery
(executable check - failure) ccls
(executable check - failure) cquery
w0rp commented 3 years ago

It's not possible to fix this without changing the LSP protocol. See here: https://github.com/microsoft/language-server-protocol/issues/737

The issue is that language servers send diagnostics to editors at any time and there's no real way to control when they arrive, as opposed to running a command on a file to check it, or the protocol TypeScript uses with tsserver. Often using completion is the thing that triggers diagnostics being sent again. The protocol needs to be changed to offer an optional mode for servers where they don't send diagnostics unless the client requests them.

willeccles commented 3 years ago

This doesn't make sense to me. I can have completion enabled and linting disabled and it (completion) works fine and does not cause linting on text changed. Why would it not be possible to use completion and only lint when the file is saved? It should work the same as when I disable linting but leave completion enabled.

w0rp commented 3 years ago

The message for diagnostics from a language server isn't sent in response to any exact request, it is sent as a notification from the server to client, so you can't control when diagnostics will be received. They could come in response to asking for completion results because you told the server the document was changed, they could come completely at random. You could theoretically stash the list of diagnostics away and try and pass them on to be displayed only after some event has been triggered which a user has said they want results for, but it would be a complicated mess I don't want to devote any energy too.

The solution is to amend the Language Server Protocol so getting diagnostics can be controlled by the client.

willeccles commented 3 years ago

Hm, I see. I quite dislike how that protocol works; it's very counter-intuitive. On a related note, how is it that disabling linting makes (neo)vim noticeably more responsive and less laggy if it's still receiving the same amount of data (and presumably parsing it)?

In the meantime, I think I will just add a hook for buffer writes that calls ALELint or whatever the command is. This is a decent middle-ground I think.

w0rp commented 3 years ago

https://github.com/microsoft/vscode/issues/117042

The good news is that what I suggested is now a proposal under review, so it's probably only a matter of time until Language Server Protocol supports client control over diagnostics and this issue will slowly be resolved as more servers support it.

Rican7 commented 3 years ago

Hmm, it looks like they closed microsoft/vscode#117042. So it doesn't seem like they consider this pull model something that they're willing to implement?