[Ruby] Go to definition does not work when method end in "?" and invocation is followed by !
Expected behavior
Right-clicking on a method invocation, whose name ends in ?, preceded by ! (negated) should make available the "Goto Definition" option.
Actual behavior
Right-clicking on a method invocation, whose name ends in ?, preceded by ! (negated) does not make available the "Goto Definition" option.
Steps to reproduce
- Create a new file and enter the following code:
class Test
def works
test_goto_definition?
end
def does_not_work
!test_goto_definition?
end
def test_goto_definition?
end
end
- Right-click on each invocation of the method
test_go_to_definition? - Observe where the "Goto definition" option is available.
Yes, it's because ST doesn't treat special chars such as ? as part of the word in various (many) situations, especially if they appear at the beginning or the end of a token.
So ST's goto definition functionality does.
It's a core issue, which can't be fixed by syntax definitions or symbol definitions.
Thank you for the swift response!
Ok, but I still don't understand. In my example, the first case works because it recognizes everything UP TO ? as part of the definition, correct? Also if I put a space between ! and my method name, it will still work. So what makes my non-working example special, compared to the working example? Could you elaborate a bit please?
I haven't investigated the implementation of e.g. Default/symbol.py in detail which handles goto definition etc., but if you move the caret over negated function call and hit F12 key, you'll find ST complaining Unable to find test_goto_definition in status bar. It looks for a function definition without question mark.
I can't answer the question why it works without the preceeding ! though.
P.S.: It even seems the other way round. It treats ! as part of the token to look for.
The following function is called in Default.symbol.py to find the name of the symbol to look for at a given text point.
def symbol_at_point(view, pt):
symbol = view.substr(view.expand_by_class(pt, sublime.CLASS_WORD_START | sublime.CLASS_WORD_END, "[]{}()<>:."))
locations = lookup_symbol(view.window(), symbol)
if len(locations) == 0:
symbol = view.substr(view.word(pt))
locations = lookup_symbol(view.window(), symbol)
return symbol, locations
The first line returns '!test_goto_definition?' in case of negated function calls. So ST looks for a function of that name, which it doesn't find. Seems those leading and trailing chars are stripped in any following step.
I think my previous suggestion applies here: https://github.com/sublimehq/sublime_text/issues/2960#issuecomment-526893989
How about ...
def symbol_at_point(view, pt):
separators = view.settings().get("word_separators", "[]{}()<>:.")
symbol = view.substr(view.expand_by_class(pt, sublime.CLASS_WORD_START | sublime.CLASS_WORD_END, separators))
locations = lookup_symbol(view.window(), symbol)
if len(locations) == 0:
symbol = view.substr(view.word(pt))
locations = lookup_symbol(view.window(), symbol)
return symbol, locations
... and a Ruby.sublime-settings with ...
{
// everything except: ?
"word_separators": "./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}`~",
}
Maybe also remove ! from word separators? Looks like ending some methods with ! is a convention: https://github.com/rubocop/ruby-style-guide#dangerous-method-bang
Doing so will again result in !test_goto_definition? being computed as symbol lookup candidate as before.
The only general solution is probably to expand selection by scope. It won't help in this particular situation though as function calls are not scoped by Ruby at all.
Maybe the function should ...
- try to expand by scope (e.g.: by
variable,entityor whatever could be a scope used for references) - expand by class
view.expand_by_class - expand by word
view.word
Oh, I get it now. Because name_of_function has no scope assigned, the selection expansion also includes the differently-scoped leading ! because they both share source.ruby. That sucks. Thanks for your patience in explaining.
The current implementation ignores scopes at all. So even if the function was scoped (correctly), it wouldn't work. But overall behavior - for other syntaxes - could be improved by using scopes as well.