-
Notifications
You must be signed in to change notification settings - Fork 136
Text-based UI support #503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
b66a47c
to
3dc8521
Compare
605e44d
to
3412e69
Compare
Because I've never seen I apologize for giving you detailed informations. https://github.com/ruby/debug/runs/5010975029?check_suite_focus=true |
@ono-max Yeah it's weird but I don't see how this PR can cause the detaching test failure. The DAP-related changes are:
|
I read your patch.
Others:
|
c7dde90
to
cf604c2
Compare
I can explore it and it should be possible. But because it requires redrawing the screen repeatedly, the result won't be good in remote consoles under current implementation (with bigger overhead to transfer data). I prefer to wait until optimizations like Another reason to wait would be to have a more complete test framework (or helpers) for TUI.
Do you mean we should preserve the stdout/err output after the debug session is finished? |
5340165
to
ba57149
Compare
My question is, what happens when the program prints something to stdout/err? |
Currently, program's output to stdout/err won't be displayed because they don't trigger the screen refresh. I'm still finding the least impactful way to pipe program output to the console screen. |
9f0af2d
to
629f1cc
Compare
@ko1 I've added stderr/stdout support. I've tested these scenarios in the demo:
2022-03-19.22.34.35.mov |
def deactivate | ||
super | ||
clear_screen! | ||
$stdout.reopen(@original_stdout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will reconnect the $stdout
object to the original stdout object's stream. So both STDOUT
and $stdout
will be resumed.
$stdout.define_singleton_method(:write) do |*args| | ||
original_stdout.write(*args) | ||
tui_console.puts(args.join) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to listen to stdout's printing events and reflect the message on the TUI window as well. The patch will be cleared after #deactivate
is called (see below).
|
||
tui_console = self | ||
original_stdout = @original_stdout | ||
original_stderr = @original_stderr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Locals are for referencing from define_singleton_method
(can't use ivar there), and ivars are for referencing from #deactivate
.
true | ||
end | ||
|
||
def store_prev_line(line) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is mainly for storing the user input line.
Minor comments: I tried with the script: # target.rb
def rec n
rec n - 1 if n > 0
a = b = c = d = e = f = g = h = i = j = k = l = m = n = o = p = q = r = s = t = u = nil
bb
end
rec 100
|
First of all, thank you for taking the time reviewing this PR 🙏
at the very least, I hope we support these:
(I've also listed them in the description) and users should be able to use
yes. I'm not sure how to prioritize it with vertical window split though. I think we need some discussion around it.
of course. will let you know when done.
you can use
do you mean it doesn't show as many locals as possible? that's because currently the window height is fixed. I think the best way to address this issue is to have a key-binding for scrolling. I'll write a plan for that.
yes that's a known issue.
how about |
b4dbfe2
to
a68c655
Compare
c639929
to
cd922a1
Compare
@ko1 I've reduced the number of commits to just 4. and the first 2 commits can actually be merged separately (that'll help reducing conflicts). |
Sorry I missed it.
Yes I'm considering how to scroll the window to access more information like editors. If the window is small and information are many (for example ivar, frames, srcs) the importance of scroll is high. |
@ko1 I've checked Ruby TUI applications like ruby_jard and Ruco to see how they handle keyboard input. I think ruby_jard implements input capturing directly from IO, while Ruco is built on top of curses. So I think to support key-binding, we can't use IMO, the principles should be
That leaves us with these options:
Do you have any thoughts on this? |
is another idea. Controlling terminal is very difficult task so I don't recommend you to dive into this area. I think it is more reasonable to make a CLI DAP client in another gem and connect with debug.gem. |
I agree that the debugger shouldn't maintain complicated terminal controlling mechanism. I'm not too keen to dive into that now either. And I just found that GDB uses But if that's not acceptable, I don't think building another UI on top of DAP is what I'd pursuit. My goal for this TUI are:
Which would be hard to achieve with DAP's architecture. |
@ko1 Since the chance of this PR getting merged is small, I won't proceed on it anymore. Do you think the first 2 refactoring commits worth merging? Otherwise I'll just close it. |
Could you make another PR for that? |
This PR adds the TUI support proposed in #305.
Usage
tui
opens TUI with a default top window (locals)tui on [type]
opens TUI with atype
top window (current haslocals
andsrc
)tui on src,locals
can open 2 windowstui off
closes TUIDemo
Single-window
(The top window shows the current frame's local variables)
TUI.mov
Multi-window
TUI.-.stacked.view.mov
Specs
These specs will be improved in the future.
When disabled (default)
It doesn't affect any existing funcionality.
When enabled with
tui
commandsrc
) or local variables (locals
).tui off
Future Plans
More window types
breakpoints
- the list of registered breakpointstracers
- the list of activated tracerstrace
- tracers' output will be directed herethreads
frames
- nearby frames (it's unlikely we'll be able to show all frames)Multi-window support
tui on [w1:h1,w2:h2]
opens windows with specified types. We'll start from stack layout. Example:tui on src:20,locals:10
RUBY_DEBUG_TUI_WINDOWS=w1:h1,w2,h2
- configures default windows.RUBY_DEBUG_TUI_HEIGHT=15
- configures the default window height.Scroll support
We can add scroll support to both the REPL and the top frame. But probably only command or key-binding scrolling at first.
Implementation notes
Additional data channel
TUI requires frequent data exchange between the Session and ThreadClient. More specifically:
For 2), we already pass
@internal_info
in everyTC -> Session
events so we can use it directly.However, we currently don't have anything for 1). So in this PR I added change to let every
Session -> TC
request also carry ametadata
payload. It's a Hash that can carry some general information, like TUI info. I think this can also help simplify the implementation of #422.Separate frame data's collection/display logic
Currently, frame data like file source, local variables...etc. are printed during collection. This is enough for the current local console but is not convenient when we need to process the data differently, like DAP server or the TUI. So I also extracted
get_*
methods (return collections) from some of theshow_*
methods (show collections). Part of this change was done in #465 and merged here and can be merged it separately.(I know technically
puts
in ThreadClient doesn't "print" the data immediately. But it's hard to get the collection back from the@output
variable as a collection anyway.)TODO: Reduce screen refreshing
Currently, screen is refreshed on each
UI#puts
call. Solines.each {|l| @ui.puts(l)}
will refresh screen multiple times. If we can change that into@ui.puts(lines)
, we can avoid unnecessary refreshes. It won't be a huge change (mainly here and maybe a few other places), but I want to wait for the general design is approved before making further changes.