-
Notifications
You must be signed in to change notification settings - Fork 120
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
Show a quick preview of inspect result before pager launch #1040
Conversation
c09c633
to
c4e9a3e
Compare
lib/irb/pager.rb
Outdated
def should_page? | ||
IRB.conf[:USE_PAGER] && STDIN.tty? && (ENV.key?("TERM") && ENV["TERM"] != "dumb") | ||
end | ||
|
||
def page_with_preview(width, height, modifier_proc) |
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.
The block passing around modifier_proc
, yield
, and overflow_callback
is quite confusing imo.
I think if we keep this method's logic inside Irb
class, the implementation could be simpler as we don't need to package the context as block as much. WDYT?
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.
Even if we move this part to Irb, the code is still complicated. We still need modifier_proc because it is used in two place.
+ modifier_proc = ->(content, multipage) do
+ content = content.chomp
+ content = "\n#{content}" if @context.newline_before_multiline_output? && (multipage || content.include?("\n"))
+ format(@context.return_format, content)
+ end
...
- yield out
+ @context.inspect_last_value(out)
...
Irb#output_value
will be long, we will want to extract it into a method. This implementation is doing it.
I don't have a good idea to make it more simple.
The usage of this method is similar to Pager.page do |io| end
and IMO it is not so bad.
Pager.page_with_preview(winwidth, winheight, modifier_proc) do |out|
@context.inspect_last_value(out)
end
c4e9a3e
to
1054cba
Compare
lib/irb/pager.rb
Outdated
end | ||
out = PageOverflowIO.new(width, height, overflow_callback) | ||
yield out | ||
content = modifier_proc.call(out.string, out.multipage?) |
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.
I'm a bit lost on this part. Why do we need to call modifier proc in the overflow callback, and why here too?
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.
modifier_proc formats the content. (Maybe we should rename it to formatter_proc)
The format is typically "(prefix)%s(postfix)"
.
We can do this instead of specifying modifier_proc if prefix and postfix is a constant, and the format is using %s
.
Pager.page_with_preview(winwidth, winheight) do |out|
out.write(prefix)
@context.inspect_last_value(out)
out.write(postfix)
end
But there are two difficulties:
- Prefix will change depending on
content.lines.size
when newline_before_multiline_output is set to true - Format can contain
%p
or any other % format instead of%s
So this method will
- Calculates overflow without applying format
- Prints the formatted preview just after IRB knows that the content overflows screen
- Continue preparing the whole content
- Prints the full formatted content with pager
Step 2 calls modifier_proc with partial content.
Step 4 calls modifier_proc with full content.
I'm used to using With this feature, I can get a preview fast, which is great 👍 But to users:
So if I keep my old habit to press Screen.Recording.2025-01-11.at.14.36.07.movIs there a way we can make this a smoother experience or easier to understand through UI hints....etc.? |
1054cba
to
e614719
Compare
That's a good point. I updated to show a message like this. Before pager
After pager launch
Considering pretty_print prints something to stdout(ex: SQL log in rails console), cursor should be at the last line and the line should be blank. |
The last commit adds 0.1s delay before calling overflow_callback. |
05625fb
to
1a7781a
Compare
@@ -1,5 +1,7 @@ | |||
# frozen_string_literal: true | |||
|
|||
require 'reline' |
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.
Q: do you think pager should be part of Reline?
If we position Reline as Ruby's default terminal application utility library, it kinda makes sense for it to have a pager class too 🤔
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.
I don't think the basic pager should be in Reline, because it's mainly about command process names(less, more) and options(-R, -X).
The added preview part is IRB specific that we can't assume pretty_print not printing a log.
If we can assume no log while pritty-printing, there is an easy way: just stream the output to pager io.
Making a pure-ruby pager library (key handling, rendering, scrolling, etc): I think it is very interesting.
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.
👍
Co-authored-by: Stan Lo <[email protected]>
1a7781a
to
4cb7730
Compare
launch (ruby/irb#1040) * Quickly show inspect preview even if pretty_print takes too much time * Show a message "Inspecting..." while generating pretty_print content * Update inspecting message Co-authored-by: Stan Lo <[email protected]> * Update rendering test for preparing inspect message * Don't show preview if pretty_print does not take time --------- ruby/irb@03c36586e6 Co-authored-by: Stan Lo <[email protected]>
launch (ruby/irb#1040) * Quickly show inspect preview even if pretty_print takes too much time * Show a message "Inspecting..." while generating pretty_print content * Update inspecting message Co-authored-by: Stan Lo <[email protected]> * Update rendering test for preparing inspect message * Don't show preview if pretty_print does not take time --------- ruby/irb@03c36586e6 Co-authored-by: Stan Lo <[email protected]>
launch (ruby/irb#1040) * Quickly show inspect preview even if pretty_print takes too much time * Show a message "Inspecting..." while generating pretty_print content * Update inspecting message Co-authored-by: Stan Lo <[email protected]> * Update rendering test for preparing inspect message * Don't show preview if pretty_print does not take time --------- ruby/irb@03c36586e6 Co-authored-by: Stan Lo <[email protected]>
IRB takes about 2 seconds to show the result of
200000.times.to_a
.In this case, creating the full pretty_print content takes time. Fortunately, there is a way to get the first WINDOW_HEIGHT lines of the content quickly.
This pull request will change IRB to show the first page of the pretty_print result quickly even when pretty_print is slow.
First page is displayed 0.1sec after pressing ENTER key.
After two seconds, the whole pretty_print content is ready and pager launches (
:
on the bottom of the line indicates that).newline_before_multiline_output
If the inspect takes more than 0.1 sec and result exceeds screen height, IRB will print the first page of the content immediately.
If the inspect of assignment expression result exceeds 1 line, generating rest of the content will be skipped.
In this case, output will be treated as multiline even if the un-generated whole inspect result does not contain newline.
Considering pretty_print prints SQL log in Rails console
In rails console, pretty printing ActiveRecord::Relation will print a SQL log.
Here is an example of the terminal buffer with terminal height = 5
There is a duplicated output in the terminal buffer if pretty_print takes more than 0.1sec. This is the downside of this pull request.
It is possible to erase it by switching to alternate screen with escape sequence
"\e[?1049h", "\e[?1049l"
.This pull request does not do that because it will also erase the SQL log from the terminal buffer.
Another simple implementation
This works pretty well in most cases, but I didn't use it in this pull request.
When evaluating a code like
[100.times.to_a, User.where(condition)]
orClassThatStoresRelationAsInstanceVariable.new(User.where(condition))
, SQL log messes up the pager output.