From 810068af5e6a50e87ce3f692b66f5d4a4e051f38 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sat, 3 Oct 2020 14:31:37 +0200 Subject: ftplugin/typescript.vim: Make `gf` open `index.ts` if path is directory In TypeScript and Node.js, you can import a file `./directory/index.ts` by importing `./directory`. I wanted `gf` and friends to open the `index` file if the import refers to a directory. By default, `gf` opens the directory using Netrw. At first tried to do this by setting 'includeexpr' as follows: setlocal includeexpr=FindFile(v:fname) function! s:FindFile(fname) echom 'FindFile: ' . a:fname if filereadable(a:fname) return a:fname endif return a:fname . '/index' endfunction This gave me the following error: E120: Using not in a script context: FindFile so I tried removing ``: setlocal includeexpr=FindFile(v:fname) function! FindFile(fname) echom 'FindFile: ' . a:fname if filereadable(a:fname) return a:fname endif return a:fname . '/index.ts' endfunction The problem was, my 'includeexpr' function wasn't getting executed every time I used `gf`, only some times. And it never ran when I used `gf` on a directory. After asking on Freenode#vim, 'crose' made the following suggestion: augroup typescript_maybe_append_filename au! au BufEnter * if expand('')->isdirectory() \ | call s:maybe_append_filename() \ | endif augroup END fu s:maybe_append_filename() abort if getbufvar('#', '&ft', '') == 'typescript' exe 'e ' .. expand('%:p') .. 'index.ts' endif endfu This works, but it adds the Netrw directory buffer to the jumplist, and feels more like a mitigation than a solution. I looked at the following language ftplugins for inspiration: * .../vim/8.2.1650/share/vim/vim82/ftplugin/ruby.vim * https://github.com/moll/vim-node/blob/master/autoload/node.vim These prompted me to override `gf` with a custom mapping since it looked like 'includeexpr' wasn't going to be the right tool for this. The `s:FindFile()` function has similar logic, but is now written as a map rhs function, taking into account the different ways of invoking `gf`. When defined as a script-local function, I at first kept getting this error using `gf` on a directory: E127: Cannot redefine function 111_FindFile: It is in use Thanks to Ingo Karkat (https://stackoverflow.com/users/813602/ingo-karkat) and this Stack Overflow answer which describes how to keep the function in the same file (so I didn't have to move it to an autoload function) and still prevent it from being redefined: https://stackoverflow.com/questions/22633115/why-do-i-get-e127-from-this-vimscript/22633702#22633702 I needed to prepend `` to the `` characters in the map function calls because these would actually run in the command line, deleting the preceding word, and messing up the mapping. In order to pass those `` characters in the string arguments, I needed to escape them with ``, even though it resulted in non-obvious looking code. --- ftplugin/typescript.vim | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'ftplugin') diff --git a/ftplugin/typescript.vim b/ftplugin/typescript.vim index c68494a..94b9c9e 100644 --- a/ftplugin/typescript.vim +++ b/ftplugin/typescript.vim @@ -1,3 +1,41 @@ " TypeScript vim settings setlocal commentstring=//\ %s + +setlocal suffixesadd+=.ts + +let b:undo_ftplugin = '' + + +nnoremap gf + \ :call FindFile(v:count1, expand(''), 'find', 'gf') +nnoremap f + \ :call FindFile(v:count1, expand(''), 'sfind', 'f') +nnoremap + \ :call FindFile(v:count1, expand(''), 'sfind', '') +nnoremap gf + \ :call FindFile(v:count1, expand(''), 'tabfind', 'gf') + +let b:undo_ftplugin .= 'nunmap gf' +let b:undo_ftplugin .= '| nunmap f' +let b:undo_ftplugin .= '| nunmap ' +let b:undo_ftplugin .= '| nunmap gf' + + +if exists('*s:FindFile') + finish +endif + +" If `fname` is a directory, open fname/index.ts using `command`. Otherwise, +" run `map` with `count`. +function! s:FindFile(count, fname, command, map) + let relative_file = expand('%:h') . '/' . a:fname + + if isdirectory(relative_file) + execute a:command . ' ' . relative_file . '/index.ts' + + return + endif + + execute 'normal! ' . a:count . a:map +endfunction -- cgit v1.2.3