debug icon indicating copy to clipboard operation
debug copied to clipboard

Stepping into a specific call on a line

Open nilbus opened this issue 3 years ago • 9 comments

Problem Description It can be extremely tedious (and even hard to know how) to step into the intended method call on a line that has many method calls. Consider this sample code with 9 method/variable references:

writer.hset(meta_key(user), uuids_key, meta_uuids.excluding(uuid).join(","))

If my goal is to step into the hset call, I must first step into, finish, and step/next into the next call for up to 8 other method calls before I can step into hset. It's extremely easy to get lost during this process, especially in unfamiliar code and when it's unclear which references are to methods and which are to local variables.

Ideas I'm interested in your feedback about what would be feasible. Here are some ideas:

  1. The pry-moves debugger supports step <method_name>: step into method method_name, with partial name match supported. (implementation) If given a numeric argument, step would continue to step n times. If given a string argument, it would attempt to match step into the method where __callee__ matches the given pattern.
  2. The RubyMine editor provides a Smart step into feature that provides a solution that requires a cursor. Lacking the environment with the cursor, perhaps debug can provide a similar choice via a menu/list of calls on the current line that the user can select from to step into, maybe when the argument is ?, i.e. step ?.

Are any of these feasible? What are your thoughts? Thanks for considering!

nilbus avatar Jun 14 '22 20:06 nilbus

Debugger supports breaking at an object's method call with the b[reak] command. So I think you can do b writer.hset and then c in this case.

st0012 avatar Jun 14 '22 22:06 st0012

@st0012 that's a really good workaround that I hadn't considered. Thank you!

The biggest issue I see is that it leaves lingering breakpoints that can be hit later, from what was intended to be a transient step operation. These breakpoints need to be manually deleted to keep the program from breaking there again later unintentionally. I think the community would appreciate having this built into step.

As an aside, the first scenario I ran into where I could use it was this, where I wanted to step into import:

result = described_class.new(operations).import

In this case, I couldn't break described_class.new(operations).import, because I'd be breaking on a new instance, different than the one that would be hit after continue. Moreover, I had to look up what described_class referred to. I ran break ThatReferencedClassName#import, which worked. This required some manual lookup, but at least it's not the most common case.

Regardless, thank you—this is useful in the meantime!

nilbus avatar Jun 15 '22 11:06 nilbus

Alternatively, if we could create one-time breakpoints, this might be less effort to implement and still eliminate the biggest drawback to this workaround.

nilbus avatar Jun 15 '22 11:06 nilbus

We do support one-time breakpoints internally but it's not exposed to commands yet. But I think it's worth considering for the cases you mentioned.

st0012 avatar Jun 15 '22 11:06 st0012

I think one-time breakpoint doesn't solve this issue because if the breakpoint is not reached (because of exception and so on) it will be remained.

Extending step command is considerable.

# c.rb
class C
  def foo
  end
end

I think pattern should be

  • method name (step foo / step #foo)
  • class/module name the method defined (step C)
  • path (step a.rb)

and combination? step C#foo

or only method name?

Note that regexp pattern should be use / like /foo/ on command. ... but I understand typing two / is inconvenient on this case.

ko1 avatar Jun 25 '22 19:06 ko1

I like the idea of extending step command 👍 Regarding the method signature, I think it should accept what break takes:

  • step C#foo
  • step C.foo
  • step c.foo

That'll give users enough options to navigate complicated method calls like

# step Template#format
# step Diestor.diest
# step template.virtual_path
digest = Digestor.digest(name: template.virtual_path, format: template.format, finder: lookup_context, dependencies: view_cache_dependencies)

source

st0012 avatar Jun 26 '22 11:06 st0012

maybe this feature can be decomposed into the following features

  • introduce "temporary" breakpoint which will be removed just after stopping by any reasons. I'm not sure break command can make it (maybe it is not needed. At least there is request).
  • make step XXX as a shortcut of the following two commands:
    • break XXX as temporary breakpoint
    • continue

ko1 avatar Jul 05 '22 18:07 ko1

English question: step into XXX makes sense for me, but does step XXX make sense?

ko1 avatar Jul 05 '22 18:07 ko1

Step XXX maxes sense. “Into” can be implied.

nilbus avatar Jul 06 '22 00:07 nilbus

I choose step into <name> because there are already step back and step reset commands.

ko1 avatar Nov 01 '22 18:11 ko1