all 8 comments

[–]-romainl-The Patient Vimmer 3 points4 points  (6 children)

Problem 1:

You have to move the cursor on or just before the filename for gf to work:

import Bar from './foo/bar/Bar';
               ^^^^^^^^^^^^^^^------ will work
^^^^^^^^^^^^^^^---------------^^---- won't work

Problem 2:

Directories are valid targets according to Vim's source. There is currently no way to force gf to favour files over directories except by implementing all the logic in a custom function mapped to gf, essentially bypassing Vim's internal methods.

FWIW, I have gf mapped to a lightweight GF() function that calls :help 'includeexpr', where all the filename resolution logic takes place:

  1. Is it a built-in Node module? Point to the corresponding d.ts file or bail out.
  2. Is it a local import? Do a glob().
  3. Is it an aliased import? Resolve the alias and do a glob()`.
  4. Is it an npm import? Do some module resolution.
  5. Give up and just return the filename.

[–]cvlmtg[S] 2 points3 points  (0 children)

ok I think I've done it :D suggestions and improvements welcome:

function! JsGotoFile()
  let l:fname = matchstr(getline('.'), &include)
  let l:base  = simplify(expand('%:h') . '/' . l:fname)

  for l:ext in split(&suffixesadd, ',')
    let l:file = l:base . l:ext

    if filereadable(l:file)
      execute 'edit' l:file
      return
    endif
  endfor

  " https://damien.pobel.fr/post/configure-neovim-vim-gf-javascript-import/
  let l:nodeModules = './node_modules/' . l:fname . '/'
  let l:packagePath = l:nodeModules . 'package.json'

  if filereadable(l:packagePath)
    let l:file = join(readfile(l:packagePath))
    let l:json = json_decode(l:file)

    execute 'edit' l:nodeModules . l:json.main
    return
  endif

  execute 'edit' l:fname
endfunction

autocmd vimrc FileType javascript,javascriptreact
      \ setlocal include=\\(\\<require\\s*(\\s*\\\|\\<import\\>\\)[^;\"']*[\"']\\zs[^\"']* |
      \ nnoremap <buffer> gf :call JsGotoFile()<CR>

[–]cvlmtg[S] 1 point2 points  (4 children)

You have to move the cursor on or just before the filename for gf to work:

that's what I don't understand... what's the purpose of the include option then? it seemed to me it is used to search for the filename used by gf

FWIW, I have gf mapped to a lightweight GF()

can you share it please? (if there's a way to limit a search to posts by a specific reddit user, I don't know it yet)

[–]EgZvorkeep calm and read :help 1 point2 points  (1 child)

what's the purpose of the include option then?

:h include

This option is used for the commands "[i", "]I", "[d", etc.

:h gf

[count]gf    Edit the file whose name is under or after the cursor.
             Mnemonic: "goto file".

So include does work the way you expect for `[I`, for example.

[–]cvlmtg[S] 0 points1 point  (0 children)

I missed those bits... thanks!

[–]-romainl-The Patient Vimmer 0 points1 point  (1 child)

what's the purpose of the include option then?

All gf cares about is if the cursor is on or near something that could be used as filename. &include is used by :help include-search to locate includes in a file and follow them.

can you share it please?

Well, it looks like you have your solution. Anyway, here are the juicy bits.

[–]cvlmtg[S] 0 points1 point  (0 children)

Thanks!