Skip to content
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

Add a timeout to cursor_pos #750

Merged
merged 1 commit into from
Oct 2, 2024

Conversation

tompng
Copy link
Member

@tompng tompng commented Sep 12, 2024

Add timeout to cursor_pos. Some terminal emulator or test code using pty does not respond to cursor position query \e[6n. Delay caused by timeout is better than unresponsive.
Fixes #674 and ruby/irb#296

Remove Reline::IO::ANSI#cursor_pos fallback to STDOUT.pread. Not available in some environment. We don't need inaccurate value.

Change fallback value of cursor_pos to [0, 0]. We don't need to return [dummy_row=1, default_ambiguous_width=1]

Always fallback east asian ambiguous width to 1.
Note that GNU Readline and zsh also uses ambiguous_width=1. The fallback value was inconsistent, some times 1 and sometimes 2.

@tompng tompng force-pushed the cursor_pos_report_timeout branch from d40e7ba to 64fadc7 Compare September 12, 2024 14:14
@tompng tompng force-pushed the cursor_pos_report_timeout branch from 64fadc7 to b598d0a Compare September 12, 2024 14:33
@@ -412,7 +412,7 @@ def ambiguous_width
end

private def may_req_ambiguous_char_width
@ambiguous_width = 2 if io_gate.dumb? || !STDIN.tty? || !STDOUT.tty?
@ambiguous_width = 1 if io_gate.dumb? || !STDIN.tty? || !STDOUT.tty?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

timeout_at = Time.now + timeout
buf = +''
while (wait = timeout_at - Time.now) > 0 && stdin.wait_readable(wait)
buf << stdin.readpartial(1024)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this stdin.readpartial(1024) become necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an io is ready for read, normally several bytes are readable. readpartial is more efficient than calling wait_readable and getc many times.

The code buf = match.pre_match + match.post_match assumes post_match exists. It assumes res << input part will append long bytes. I think the original intention of this method is to use readpartial instead of getc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using readpartial is more efficient, I understand now. Thanks!

end
end
Reline::CursorPos.new(column, row)
[match[:column].to_i - 1, match[:row].to_i - 1] if match
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IHMO: Instead of returning an array or false, returning an empty array or [0, 0] might be more consistent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think returning retrieved_value or nil (if not found/not available) is not a bad idea.
For example, [1,2,3,4].index 5 returns nil instead of -1 or 0.
User.find_by(id: -1) returns nil instead of User.new(id: nil, name: nil).

Actual retrieving part returns nil, def cursor_pos can return either CursorPos.new(0, 0) or CursorPos.new(-1, -1) or any other value. (I think def cursor_pos can return nil but it requires more changes in other files)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User.find_by(id: -1) returns nil instead of User.new(id: nil, name: nil).

Indeed, it seems good to return nil. Thank you for your explanation!

Copy link
Member

@ima1zumi ima1zumi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

timeout_at = Time.now + timeout
buf = +''
while (wait = timeout_at - Time.now) > 0 && stdin.wait_readable(wait)
buf << stdin.readpartial(1024)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using readpartial is more efficient, I understand now. Thanks!

end
end
Reline::CursorPos.new(column, row)
[match[:column].to_i - 1, match[:row].to_i - 1] if match
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User.find_by(id: -1) returns nil instead of User.new(id: nil, name: nil).

Indeed, it seems good to return nil. Thank you for your explanation!

@ima1zumi ima1zumi merged commit dd4a654 into ruby:master Oct 2, 2024
40 checks passed
matzbot pushed a commit to ruby/ruby that referenced this pull request Oct 2, 2024
@tompng tompng deleted the cursor_pos_report_timeout branch October 2, 2024 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Add a timeout to cursor_pos and warn if terminal does not respond to cursor position query
2 participants