From 71053fa5e61b08381ef940db338ec6dcf7ab2909 Mon Sep 17 00:00:00 2001 From: xs Date: Thu, 5 Nov 2026 10:45:29 +0800 Subject: vimrc: standalone .rs files via detachedFiles, added at server start A .rs file that is a target of its nearest Cargo.toml uses that crate; any other .rs file is loaded standalone via detachedFiles with checkOnSave off (cargo check needs a Cargo.toml). --- core/vimrc | 84 +++++++++++++++++++++++--------------------------------------- 1 file changed, 31 insertions(+), 53 deletions(-) diff --git a/core/vimrc b/core/vimrc index 017e754..22b6b41 100644 --- a/core/vimrc +++ b/core/vimrc @@ -186,75 +186,53 @@ endif let g:gitgutter_sign_column_always = 0 -" rust-analyzer root: nearest Cargo.toml dir only if this file is a target of that crate -" (src/ examples/ tests/ benches/ build.rs, or its path appears in the manifest); else the -" file's own dir, so rust-analyzer loads it standalone (std resolves) instead of detached-in-a-foreign-project -" RustCrate(): crate dir if this buffer is a target of its nearest Cargo.toml, else '' +" rust-analyzer: a file that is a target of its nearest Cargo.toml (src/ examples/ tests/ +" benches/ build.rs, or its path is in the manifest) uses that crate. Any other .rs file is +" loaded standalone via detachedFiles, with checkOnSave off (cargo check needs a Cargo.toml). function! RustCrate() abort let l:p = expand('%:p') let l:d = lsp#utils#find_nearest_parent_file_directory(l:p, 'Cargo.toml') - if !empty(l:d) - let l:d = substitute(fnamemodify(l:d, ':p'), '/$', '', '') - let l:rel = stridx(l:p, l:d . '/') == 0 ? l:p[len(l:d) + 1:] : '' - let l:target = l:rel =~# '^\(src\|examples\|tests\|benches\)/' || l:rel ==# 'build.rs' - if !l:target && !empty(l:rel) - let l:target = stridx(join(readfile(l:d . '/Cargo.toml'), "\n"), '"' . l:rel . '"') >= 0 - endif - if l:target - return l:d - endif + if empty(l:d) | return '' | endif + let l:d = substitute(fnamemodify(l:d, ':p'), '/$', '', '') + let l:rel = stridx(l:p, l:d . '/') == 0 ? l:p[len(l:d) + 1:] : '' + if l:rel =~# '^\(src\|examples\|tests\|benches\)/\|^build\.rs$' + \ || (!empty(l:rel) && stridx(join(readfile(l:d . '/Cargo.toml')), '"' . l:rel . '"') >= 0) + return l:d endif return '' endfunction +function! s:RustAddDetached() abort + let l:p = expand('%:p') + if empty(l:p) || index(g:ra_init.detachedFiles, l:p) >= 0 | return 0 | endif + call add(g:ra_init.detachedFiles, l:p) + return 1 +endfunction +" Called once at server start (root_uri is read just before the init options). function! RustRoot() abort let l:c = RustCrate() - return empty(l:c) ? expand('%:p:h') : l:c + let g:ra_init.checkOnSave = empty(l:c) ? v:false : v:true + if !empty(l:c) | return l:c | endif + call s:RustAddDetached() + return expand('%:p:h') endfunction -" Standalone .rs files (no owning crate) must be listed in rust-analyzer's detachedFiles, -" else it loads nothing for them (no gd/completion). The list is shared by reference with -" the init options below (covers the first file) and pushed as config for later files. -let g:ra_detached = [] -" checkOnSave runs `cargo check` on the file, which needs nightly -Zscript for a file with -" no Cargo.toml; RustDetach turns it off when the server starts on a standalone file. let g:ra_init = { - \ 'completion': { - \ 'autoimport': { 'enable': v:true }, - \ 'postfix': { 'enable': v:true }, - \ }, - \ 'checkOnSave': v:true, - \ 'detachedFiles': g:ra_detached, - \ } -function! RustDetach() abort - if !exists('g:lsp_loaded') - return - endif - let l:p = expand('%:p') - if empty(l:p) || !empty(RustCrate()) || index(g:ra_detached, l:p) >= 0 - return - endif - call add(g:ra_detached, l:p) - if lsp#get_server_status('rust-analyzer') !~# '^\(running\|starting\)$' - let g:ra_init['checkOnSave'] = v:false - endif - if lsp#get_server_status('rust-analyzer') ==# 'running' - call lsp#update_workspace_config('rust-analyzer', {'rust-analyzer': {'detachedFiles': copy(g:ra_detached)}}) - endif -endfunction + \ 'completion': {'autoimport': {'enable': v:true}, 'postfix': {'enable': v:true}}, + \ 'checkOnSave': v:true, 'detachedFiles': []} augroup rust_detached au! - au BufEnter *.rs call RustDetach() + " standalone file opened while the server already runs: add it live (check setting untouched) + au BufEnter *.rs if exists('g:lsp_loaded') && lsp#get_server_status('rust-analyzer') ==# 'running' + \ && empty(RustCrate()) && s:RustAddDetached() + \ | call lsp#update_workspace_config('rust-analyzer', {'rust-analyzer': g:ra_init}) | endif augroup END - " rust-analyzer via the rustup proxy shim (portable across toolchains/machines) if executable(expand('~/.cargo/bin/rust-analyzer')) au User lsp_setup call lsp#register_server({ - \ 'name': 'rust-analyzer', - \ 'cmd': {server_info -> [expand('~/.cargo/bin/rust-analyzer')]}, - \ 'allowlist': ['rust'], - \ 'root_uri': {server_info -> lsp#utils#path_to_uri(RustRoot())}, - \ 'initialization_options': g:ra_init, - \ 'workspace_config': {'rust-analyzer': {'detachedFiles': g:ra_detached}}, - \ }) + \ 'name': 'rust-analyzer', 'allowlist': ['rust'], + \ 'cmd': {server_info -> [expand('~/.cargo/bin/rust-analyzer')]}, + \ 'root_uri': {server_info -> lsp#utils#path_to_uri(RustRoot())}, + \ 'initialization_options': g:ra_init, + \ 'workspace_config': {'rust-analyzer': g:ra_init}}) endif if executable(expand('~/.local/bin/ruff')) au User lsp_setup call lsp#register_server({ -- cgit