runtime(vim): add gf support for import and packadd in ftplugin Commit: https://github.com/vim/vim/commit/c0b3c191200441020e854a37beb5e814e1672475 Author: lacygoill <lacygo...@lacygoill.me> Date: Wed Aug 6 13:37:12 2025 +0200
runtime(vim): add gf support for import and packadd in ftplugin closes: https://github.com/vim/vim/issues/17881 Signed-off-by: lacygoill <lacygo...@lacygoill.me> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/runtime/autoload/vim.vim b/runtime/autoload/vim.vim new file mode 100644 index 000000000..8dc14c476 --- /dev/null +++ b/runtime/autoload/vim.vim @@ -0,0 +1,134 @@ +vim9script + +# Interface {{{1 +export def Find(editcmd: string) #{{{2 + var curline: string = getline('.') + + if curline =~ '^\s*\%(:\s*\)\=packadd!\=\s' + HandlePackaddLine(editcmd, curline) + return + endif + + if curline =~ '^\s*\%(:\s*\)\=import\s' + HandleImportLine(editcmd, curline) + return + endif + + try + execute 'normal! ' .. editcmd + catch + Error(v:exception) + endtry +enddef +#}}}1 +# Core {{{1 +def HandlePackaddLine(editcmd: string, curline: string) #{{{2 + var pat: string = '^\s*packadd!\=\s\+\zs\S\+$' + var plugin: string = curline + ->matchstr(pat) + ->substitute('^vim-\|\.vim$', '', 'g') + + if plugin == '' + try + execute 'normal! ' .. editcmd .. 'zv' + catch + Error(v:exception) + return + endtry + else + var split: string = editcmd[0] == 'g' ? 'edit' : editcmd[1] == 'g' ? 'tabedit' : 'split' + # In the past, we passed `runtime` to `getcompletion()`, instead of + # `cmdline`. But the output was tricky to use, because it contained + # paths relative to inconsistent root directories. + var files: list<string> = getcompletion($'edit **/plugin/{plugin}.vim', 'cmdline') + ->filter((_, path: string): bool => filereadable(path)) + ->map((_, fname: string) => fname->fnamemodify(':p')) + if empty(files) + echo 'Could not find any plugin file for ' .. string(plugin) + return + endif + files->Open(split) + endif +enddef + +def HandleImportLine(editcmd: string, curline: string) #{{{2 + var fname: string + var import_cmd: string = '^\s*import\s\+\%(autoload\s\+\)\=' + var import_alias: string = '\%(\s\+as\s\+\w\+\)\=$' + var import_string: string = import_cmd .. '\([''"]\)\zs.*\ze ' .. import_alias + var import_expr: string = import_cmd .. '\zs.*\ze' .. import_alias + # the script is referred to by its name in a quoted string + if curline =~ import_string + fname = curline->matchstr(import_string) + # the script is referred to by an expression + elseif curline =~ import_expr + try + sandbox fname = curline + ->matchstr(import_expr) + ->eval() + catch + Error(v:exception) + return + endtry + endif + + var filepath: string + if fname->isabsolutepath() + filepath = fname + elseif fname[0] == '.' + filepath = (expand('%:h') .. '/' .. fname)->simplify() + else + var subdir: string = curline =~ '^\s*import\s\+autoload\>' ? 'autoload' : 'import' + # Matching patterns in `'wildignore'` can be slow. + # Let's set `{nosuf}` to `true` to avoid `globpath()` to be slow. + filepath = globpath(&runtimepath, subdir .. '/' .. fname, true, true) + ->get(0, '') + endif + + if !filepath->filereadable() + printf('E447: Can''t find file "%s" in path', fname) + ->Error() + return + endif + + var how_to_split: string = { + gF: 'edit', + "\<C-W>F": 'split', + "\<C-W>gF": 'tab split', + }[editcmd] + execute how_to_split .. ' ' .. filepath +enddef + +def Open(what: any, how: string) #{{{2 + var fname: string + if what->typename() == 'list<string>' + if what->empty() + return + endif + fname = what[0] + else + if what->typename() != 'string' + return + endif + fname = what + endif + + execute $'{how} {fname}' + cursor(1, 1) + + # If there are several files to open, put them into an arglist. + if what->typename() == 'list<string>' + && what->len() > 1 + var arglist: list<string> = what + ->copy() + ->map((_, f: string) => f->fnameescape()) + execute $'arglocal {arglist->join()}' + endif +enddef +#}}}1 +# Util {{{1 +def Error(msg: string) #{{{2 + echohl ErrorMsg + echomsg msg + echohl NONE +enddef diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 0de7b20f3..7fa135edb 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1063,8 +1063,18 @@ To disable: > < VIM *ft-vim-plugin* -The Vim filetype plugin defines mappings to move to the start and end of -functions with [[ and ]]. Move around comments with ]" and [". +The Vim filetype plugin defines the following mappings: + + [[ move to the start of the previous function + ]] move to the start of the next function + ][ move to the end of the previous function + [] move to the end of the next function + ]" move to the next (legacy) comment + [" move to the previous (legacy) comment + gf edit the file under the cursor + CTRL-W gf edit the file under the cursor in a new tab + CTRL-W f edit the file under the cursor in a new window + The mappings can be disabled with: > let g:no_vim_maps = 1 diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim index 99ce0bc58..28ca9b326 100644 --- a/runtime/ftplugin/vim.vim +++ b/runtime/ftplugin/vim.vim @@ -1,11 +1,13 @@ " Vim filetype plugin " Language: Vim " Maintainer: Doug Kearns <dougkea...@gmail.com> -" Last Change: 2025 Mar 05 " Former Maintainer: Bram Moolenaar <b...@vim.org> " Contributors: Riley Bruins <ribr...@gmail.com> ('commentstring'), " @Konfekt " @tpope (s:Help()) +" @lacygoill +" Last Change: 2025 Mar 05 +" 2025 Aug 06 by Vim Project (add gf maps #17881) " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -35,6 +37,9 @@ if !exists('*VimFtpluginUndo') silent! xunmap <buffer> ]" silent! nunmap <buffer> [" silent! xunmap <buffer> [" + silent! nunmap <buffer> gf + silent! nunmap <buffer> <C-W>f + silent! nunmap <buffer> <C-W>gf endif unlet! b:match_ignorecase b:match_words b:match_skip b:did_add_maps endfunc @@ -139,6 +144,29 @@ if !exists("no_plugin_maps") && !exists("no_vim_maps") xnoremap <silent><buffer> ]" :<C-U>exe "normal! gv"<Bar>call search('\%(^\s*".* \)\@<!\%(^\s*"\)', "W")<CR> nnoremap <silent><buffer> [" :call search('\%(^\s*".* \)\%(^\s*"\)\@!', "bW")<CR> xnoremap <silent><buffer> [" :<C-U>exe "normal! gv"<Bar>call search('\%(^\s*".* \)\%(^\s*"\)\@!', "bW")<CR> + + " Purpose: Handle `:import` and `:packadd` lines in a smarter way. {{{ + " + " `:import` is followed by a filename or filepath. Find it. + " + " `:packadd` is followed by the name of a package, which we might have + " configured in scripts under `~/.vim/plugin`. Find it. + " + " --- + " + " We can't handle the `:import` lines simply by setting `'includeexpr'`, because + " the option would be ignored if: + " + " - the name of the imported script is the same as the current one + " - `'path'` includes the `.` item + " + " Indeed, in that case, Vim finds the current file, and simply reloads the + " buffer. + " }}} + " We use the `F` variants, instead of the `f` ones, because they're smarter. + nnoremap <silent><buffer> gf :<C-U>call vim#Find('gF')<CR> + nnoremap <silent><buffer> <C-W>f :<C-U>call vim#Find("\<lt>C-W>F")<CR> + nnoremap <silent><buffer> <C-W>gf :<C-U>call vim#Find("\<lt>C-W>gF")<CR> endif " Let the matchit plugin know what items can be matched. -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1ujcZr-00FsoG-Il%40256bit.org.