Packages icon indicating copy to clipboard operation
Packages copied to clipboard

[Ruby] Go to definition does not work when method end in "?" and invocation is followed by !

Open odydoum opened this issue 4 years ago • 10 comments

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

  1. 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
  1. Right-click on each invocation of the method test_go_to_definition?
  2. Observe where the "Goto definition" option is available.

odydoum avatar Nov 16 '21 12:11 odydoum

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.

deathaxe avatar Nov 16 '21 16:11 deathaxe

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?

odydoum avatar Nov 16 '21 16:11 odydoum

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.

deathaxe avatar Nov 16 '21 17:11 deathaxe

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.

deathaxe avatar Nov 16 '21 17:11 deathaxe

I think my previous suggestion applies here: https://github.com/sublimehq/sublime_text/issues/2960#issuecomment-526893989

keith-hall avatar Nov 16 '21 17:11 keith-hall

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": "./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}`~",
}

deathaxe avatar Nov 16 '21 19:11 deathaxe

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

michaelblyons avatar Nov 16 '21 21:11 michaelblyons

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 ...

  1. try to expand by scope (e.g.: by variable, entity or whatever could be a scope used for references)
  2. expand by class view.expand_by_class
  3. expand by word view.word

deathaxe avatar Nov 17 '21 12:11 deathaxe

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.

michaelblyons avatar Nov 17 '21 14:11 michaelblyons

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.

deathaxe avatar Nov 17 '21 16:11 deathaxe