dense-analysis / dense-analysis/ale
Vim hangs when searching for linters (ALE should allow the user to provide stop directories for file searches)
- Dominant language
- Vim Script
- Stars
- 14k
- Forks
- 1.5k
- Avg merge
- 17h 49m
- Merged PRs (30d)
- 1
Description
## Information
**VIM version**
VIM - Vi IMproved 9.1 (2024 Jan 02, compiled Jan 01 1980 00:00:00)
Included patches: 1-1918
Operating System: Nix on RHEL
## What went wrong
On my system, `/home` is an NFS automounter, and enumerating its contents takes an extremely long time:
```
$ time ls /home/$USER/node_modules/.bin/vim-language-server
ls: cannot access '/home/$USER/node_modules/.bin/vim-language-server': No such file or directory
real 0m0.009s
user 0m0.001s
sys 0m0.001s
$ time ls /home/node_modules/.bin/vim-language-server
ls: cannot access '/home/node_modules/.bin/vim-language-server': No such file or directory
real 0m25.432s
user 0m0.001s
sys 0m0.001s
```
Because ALE does an upward search for linters from the buffer file path without using any stop directories, it tries to look for linters in `/home`, which causes (on my system) a 25.4-second hang every time I try to load or save a file, if ALE cannot find the linter for that file type.
The solution would be to allow the user to specify stop directories (in my case, `$HOME`) that would be used for all upward `finddir` and `findfile` calls.
## Reproducing the bug
1. Install ALE.
2. Do not install `vim-language-server`.
3. Run `strace -etrace=file -ostrace.log -tt vim ~/.vimrc`.
4. Run `grep -A1 -F /home/node_modules/.bin/vim-language-server strace.log`.
```
16:23:12.662908 newfstatat(AT_FDCWD, "/home/node_modules/.bin/vim-language-server", 0x7ffffffd0ee0, 0) = -1 ENOENT (No such file or directory)
16:23:38.077781 newfstatat(AT_FDCWD, "/", {st_mode=S_IFDIR|0700, st_size=4096, ...}, 0) = 0
```
On most systems this `stat()` syscall would be fast, but as you can see, on my system it takes 25.4 seconds. This bug would be fixed if ALE can be configured to stop looking for linters at `$HOME`, so this slow `stat()` syscall can be avoided.
### :ALEInfo
Expand
```
Current Filetype: vim
Available Linters: ['ale_custom_linting_rules', 'vimls', 'vint']
Enabled Linters: ['ale_custom_linting_rules', 'vimls', 'vint']
Ignored Linters: []
Suggested Fixers:
'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.
Linter Variables:
" Press Space to read :help for a setting
let g:ale_vim_vimls_config = {}
let g:ale_vim_vimls_executable = 'vim-language-server'
let g:ale_vim_vimls_use_global = 0
let g:ale_vim_vint_executable = 'vint'
let g:ale_vim_vint_show_style_issues = 1
Global Variables:
" Press Space to read :help for a setting
let g:ale_cache_executable_check_failures = v:null
let g:ale_change_sign_column_color = v:false
let g:ale_command_wrapper = v:null
let g:ale_completion_delay = v:null
let g:ale_completion_enabled = v:false
let g:ale_completion_max_suggestions = v:null
let g:ale_disable_lsp = 'auto'
let g:ale_echo_cursor = v:true
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 = v:false
let g:ale_fixers = {}
let g:ale_history_enabled = v:true
let g:ale_info_default_mode = 'preview'
let g:ale_history_log_output = v:true
let g:ale_keep_list_window_open = 0
let g:ale_lint_delay = 200
let g:ale_lint_on_enter = v:true
let g:ale_lint_on_filetype_changed = v:true
let g:ale_lint_on_insert_leave = v:true
let g:ale_lint_on_save = v:true
let g:ale_lint_on_text_changed = 'normal'
let g:ale_linter_aliases = {}
let g:ale_linters = {}
let g:ale_linters_explicit = v:false
let g:ale_linters_ignore = {}
let g:ale_list_vertical = v:false
let g:ale_list_window_size = 10
let g:ale_loclist_msg_format = '%code: %%s'
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 = v:false
let g:ale_pattern_options = v:null
let g:ale_pattern_options_enabled = v:null
let g:ale_root = {}
let g:ale_set_balloons = v:false
let g:ale_set_highlights = v:true
let g:ale_set_loclist = v:true
let g:ale_set_quickfix = v:false
let g:ale_set_signs = v:true
let g:ale_sign_column_always = v:false
let g:ale_sign_error = 'E'
let g:ale_sign_info = 'I'
let g:ale_sign_offset = 1000000
let g:ale_sign_style_error = 'E'
let g:ale_sign_style_warning = 'W'
let g:ale_sign_warning = 'W'
let g:ale_sign_highlight_linenrs = v:false
let g:ale_type_map = {}
let g:ale_use_neovim_diagnostics_api = v:false
let g:ale_use_global_executables = v:null
let g:ale_virtualtext_cursor = 'all'
let g:ale_warn_about_trailing_blank_lines = v:true
let g:ale_warn_about_trailing_whitespace = v:true
Command History:
(executable check - failure) vim-language-server
(executable check - failure) vint
```
Contributor guide
Research direction
Start by tracing ALE's upward finddir and findfile calls used during linter executable checks, then reproduce the delay with the provided Vim and strace commands. The change is done when users can configure stop directories such as $HOME and searches avoid those paths without breaking linter discovery.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- vim
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100