debug icon indicating copy to clipboard operation
debug copied to clipboard

[POC] Command Registration

Open st0012 opened this issue 3 years ago • 5 comments

Benefits

Extensible

Users or gems will be able to register new commands with:

DEBUGGER__::Session.register_command("foo", aliases: ["fo"]) do |arg|
  # my command
end.document category: "My Gem", content: <<~MD
  * foo does blablabla
MD

And then the command document will appear in the help

❯ ruby -Ilib -rdebug target.rb
[4, 9] in target.rb
     4|   # my command
     5| end.document category: "My Gem", content: <<~MD
     6|   * foo does blablabla
     7| MD
     8|
=>   9| binding.b
=>#0    <main> at target.rb:9
(rdbg) help foo    # command

### My Gem

* foo does blablabla
(rdbg)

And because commands are stored together, it can support autocompletion in the future too.

This extensibility will allow:

  1. Integration with popular libraries, like Rails integration demonstrated in the comments below.
  2. 3rd-party command implementations. We can have commands like show-source implemented in a 3rd-party gem instead of implementing everything in the debugger core. (not saying that show-source should be implemented externally, but just an example).

Easier to maintain

Currently, command documents need to be written in Ruby comments with a strict format. And the parsing logic is quite adhoc for that reason. With this approach, we can simplify the parsing logic and make the document naturally a part of the command definition.

Additional thoughts

  • The CommandSet logic is a replication of the Config class, which prevents sharing mutable hash while allowing modifying values. I hope this will make it Ractor-safe?

st0012 avatar Jul 20 '22 09:07 st0012

Do you want to invoke some Ruby code on the session thread?

Could you list the example usages? This is a kind of language design and examples are very important.

ko1 avatar Jul 27 '22 06:07 ko1

Example - fetching Rails configurations via rails config

Command Code

COMMAND_OPTIONS_SPLITTING_REGEXP = /([^\s]+)(?:\s+(.+))?/

DEBUGGER__::Session.register_command("rails") do |arg|
  COMMAND_OPTIONS_SPLITTING_REGEXP =~ arg
  sub_command, options = $1, $2

  case sub_command
  when "config"
    config = ::Rails.application.config

    configs =
      if options
        if config.respond_to?(options)
          config.send(options)
        else
          @ui.puts("unknown Rails component #{options}")
          []
        end
      else
        config.instance_variables.each_with_object({}) do |ivar, hash|
          key = ivar.to_s.sub("@", "")
          value = config.instance_variable_get(ivar)
          hash[key] = value
        end
      end

    configs.each do |key, value|
      key = colorize_cyan(key)
      @ui.puts("  #{key} => #{DEBUGGER__.safe_inspect(value)}")
    end
  else
    @ui.puts("unknown Rails debuggger command: #{arg}")
  end

  :retry
end.document category: "Rails", content: <<~MD
  * `rails config`
    * List top-level Rails configurations
  * `rails config <component>`
    * List <component>'s configurations
MD

Result

截圖 2022-07-31 13 15 10

Other useful command extensions for Rails could be:

  • rails breadcrumbs - See all the recorded ActiveSupport::Notifications events.
    • Of course this needs extra work to store/access the events but it's relatively easy to achieve without debugger's support.
    • The breadcrumbs concept exists in many error monitoring services like Sentry. It'll help users track major application events happened before the program reaches the current state
  • rails callbacks Model - List a model's callbacks by running Model._save_callbacks and filter/display the result nicely
  • rails routes /regexp/ - List the application's routes

@rafaelfranca is there other Rails specific debugger commands you'd like to see as well?

st0012 avatar Jul 31 '22 22:07 st0012

So they are global information.

# define SOME_CLASS.some_method before

eval SOME_CLASS.some_method

doesn't work?

ko1 avatar Aug 10 '22 01:08 ko1

I didn't mean "use eval" on this but providing alias feature like:

alias rails "eval SOME_CLASS.some_method(args)"

with documentation.

Ah, @ui.puts is not work well. adding eval option? Anyway, I don't think it is good idea to expose @ui.puts for extensions.

ko1 avatar Aug 10 '22 01:08 ko1

alias rails "eval SOME_CLASS.some_method(args)"

That could work too. But I think alias is confusing and something like command should be clearer, along with an argument for documentation

command :rails, "eval RailsDebugger.execute(args)", doc: <<~DOC
  * `rails config`
    * List top-level Rails configurations
  * `rails config <component>`
    * List <component>'s configurations
DOC

Anyway, I don't think it is good idea to expose @ui.puts for extensions.

Yeah it's not ideal to expose instance variables. But we still need to access the UI from the extension code. Do you think calling DEBUGGER__::SESSION.print(msg) will be acceptable?

st0012 avatar Aug 14 '22 13:08 st0012