ruby / yamatanooroti

MIT License
0 stars 6 forks source link

Use thread to respond to "\e[6n"(Cursor Position Report) as soon as possible #16

Open tompng opened 1 month ago

tompng commented 1 month ago

Description

In method write, yamatanooroti calls sync before and after writing to pty_input.

def write(str_to_write)
  sync
  @pty_input.write(str_to_write)
  sync
end

I think we don't need to sync before write, but removing it will make reline/irb's test fail. It does not mean that sync before is necessary. We need to use Thread to fix the root cause.

Detail

Reline writes "\e[6n", Cursor Position Report, to get the actual cursor position. Terminal emulator(== vterm) will respond to it with "\e[#{row};#{column}R".

I found that this test is working just by chance.

start_terminal(10, 10, 'irb')
write "1+2\n"
sleep 1
write "2+3\n"
assert_screen("irb(main):001>1+2\n=>3\nirb(main):002>2+3\n=>5\n")

Breaking it down

sync
@pty_input.write "1+2\n"
sync
sleep 1
sync
@pty_input.write "3+4\n"
sync

Breaking it down more

sync
@pty_input.write "1+2\n"
@vterm.write(@pty_output.read_nonblock(1024))
@pty_input.write(@vterm.read)
sleep 1
@vterm.write(@pty_output.read_nonblock(1024))
@pty_input.write(@vterm.read)
@pty_input.write "3+4\n"
sync

These are two cases how IRB, yamatanooroti and vterm interacts.

# Case 1, normal

@pty_input.write "1+2\n"
# irb receives "1+2\n"
# irb prints "\e[G1irb(main):001> 1+2\n=> 3\n\e[6n"
@vterm.write(@pty_output.read_nonblock(1024)) # yamatanooroti writes it to vterm
# vterm responds to "\e[6n" with "\e[2;1R"
@pty_input.write(@vterm.read)
# irb prints "irb(main):002>"
sleep 1
@vterm.write(@pty_output.read_nonblock(1024)) # nothing happens
@pty_input.write(@vterm.read) # nothing happens
@pty_input.write "3+4\n"
# irb receives "3+4\n"
sync
# Case 2, IRB prints "\e[6n" with a small delay

@pty_input.write "1+2\n"
# irb receives "1+2\n"
# irb prints "\e[G1irb(main):001> 1+2\n=> 3\n"
@vterm.write(@pty_output.read_nonblock(1024)) # yamatanooroti writes it to vterm
# irb prints "\e[6n" with a small delay
@pty_input.write(@vterm.read) # nothing happens
sleep 1 # irb is waiting for cursor position report and doing nothing
@vterm.write(@pty_output.read_nonblock(1024)) # yamatanooroti writes "\e[6n" to vterm
@pty_input.write(@vterm.read) # vterm responds to "\e[6n" with "\e[2;1R"
@pty_input.write "3+4\n" # IRB's prompt "irb(main):002>" is not ready yet
# irb prints "irb(main):002>"
# irb receives "3+4\n"
sync

Even with a long sleep, there is a chance that test won't run as expected. While executing sleep 1, another thread should read from pty_output, write to vterm, and write cursor position report response to pty_input.