tpope / vim-rails

rails.vim: Ruby on Rails power tools
http://www.vim.org/scripts/script.php?script_id=1567
4.1k stars 383 forks source link

`gf` finding files with nested constant #551

Closed jweir closed 4 years ago

jweir commented 4 years ago

Could gf be less explicit in finding files?

For example if file foo.rb is defined as

module Foo
  VALUE=1
  module Bar; end
end

gf on Foo::Bar will report file not found. What, would be nice is if gf would walk up the directory tree and find foo.rb

This also applies to gf over module/classes with a constant

gf on Foo::VALUE will also report file not found.

tpope commented 4 years ago

The answer is yes, with a couple of asterisks. The way gf works is we build a relative filename, the same filename that you would pass to require to explicitly load the library, and feed it to :find, which makes it absolute by looking through 'path', or gives an error if it can't be found. Without changing that, our only real option is to preemptively hunt for the file, and if we can't find it, remove one level of filename and try again. Here's how I did basically that for Lua:

https://github.com/tpope/vim-apathy/blob/ca90185a497088bbb62d933bb500cb2911c7be64/after/ftplugin/lua_apathy.vim#L25-L33

A downside of this approach is that we're now doing two searches through the load path on every gf, more if the file can't be found. This is a bigger deal for the long load paths of a Rails app than it is for Lua. For one additional search, I am seeing an additional overhead of about 40ms on the random Rails app I loaded.

My other concern is that it takes what was previously a simple deterministic process of translating a class name to a file name and throws in a giant dependency on the state of the current file system, making it much harder to reason about. This isn't unprecedented - we're already doing it for asset and view extensions - but now we're taking it a one step further and changing the base filename.

Between these two, I'm on the fence. But as a sponsor (thanks!), you've won me over. I'm only going to go one level deep: Foo::Bar::VALUE will only get walked back to foo/bar.rb. I think that strikes a reasonable balance.