dense-analysis / dense-analysis/ale

Vim hangs when searching for linters (ALE should allow the user to provide stop directories for file searches)

Open
#5,089 3 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.