Skip to content

Commit 26d887e

Browse files
authored
REPL: try to coalesce updates (#39538)
When input is arriving too fast, delay computing and rendering the full screen until a short delay after typing stops.
1 parent a01d7f3 commit 26d887e

File tree

2 files changed

+60
-12
lines changed

2 files changed

+60
-12
lines changed

stdlib/REPL/src/LineEdit.jl

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ mutable struct PromptState <: ModeState
102102
beeping::Float64
103103
# this option is to detect when code is pasted in non-"bracketed paste mode" :
104104
last_newline::Float64 # register when last newline was entered
105+
# this option is to speed up output
106+
refresh_wait::Union{Timer,Nothing}
105107
end
106108

107109
options(s::PromptState) =
@@ -371,8 +373,17 @@ function complete_line(s::PromptState, repeats::Int)
371373
return true
372374
end
373375

376+
function clear_input_area(terminal::AbstractTerminal, s::PromptState)
377+
if s.refresh_wait !== nothing
378+
close(s.refresh_wait)
379+
s.refresh_wait = nothing
380+
end
381+
_clear_input_area(terminal, s.ias)
382+
s.ias = InputAreaState(0, 0)
383+
end
374384
clear_input_area(terminal::AbstractTerminal, s::ModeState) = (_clear_input_area(terminal, s.ias); s.ias = InputAreaState(0, 0))
375385
clear_input_area(s::ModeState) = clear_input_area(s.terminal, s)
386+
376387
function _clear_input_area(terminal::AbstractTerminal, state::InputAreaState)
377388
# Go to the last line
378389
if state.curs_row < state.num_rows
@@ -395,6 +406,13 @@ prompt_string(p::Prompt) = prompt_string(p.prompt)
395406
prompt_string(s::AbstractString) = s
396407
prompt_string(f::Function) = Base.invokelatest(f)
397408

409+
function refresh_multi_line(s::PromptState; kw...)
410+
if s.refresh_wait !== nothing
411+
close(s.refresh_wait)
412+
s.refresh_wait = nothing
413+
end
414+
refresh_multi_line(terminal(s), s; kw...)
415+
end
398416
refresh_multi_line(s::ModeState; kw...) = refresh_multi_line(terminal(s), s; kw...)
399417
refresh_multi_line(termbuf::TerminalBuffer, s::ModeState; kw...) = refresh_multi_line(termbuf, terminal(s), s; kw...)
400418
refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState; kw...) = (@assert term === terminal(s); refresh_multi_line(termbuf,s; kw...))
@@ -738,7 +756,7 @@ function edit_insert(s::PromptState, c::StringLike)
738756
buf = s.input_buffer
739757

740758
if ! options(s).auto_indent_bracketed_paste
741-
pos=position(buf)
759+
pos = position(buf)
742760
if pos > 0
743761
if buf.data[pos] != _space && string(c) != " "
744762
options(s).auto_indent_tmp_off = false
@@ -757,20 +775,46 @@ function edit_insert(s::PromptState, c::StringLike)
757775
end
758776
end
759777

778+
old_wait = s.refresh_wait !== nothing
779+
if old_wait
780+
close(s.refresh_wait)
781+
s.refresh_wait = nothing
782+
end
760783
str = string(c)
761784
edit_insert(buf, str)
762-
offset = s.ias.curs_row == 1 || s.indent < 0 ?
763-
sizeof(prompt_string(s.p.prompt)::String) : s.indent
764-
if !('\n' in str) && eof(buf) &&
765-
((position(buf) - beginofline(buf) + # size of current line
766-
offset + sizeof(str) - 1) < width(terminal(s)))
767-
# Avoid full update when appending characters to the end
768-
# and an update of curs_row isn't necessary (conservatively estimated)
769-
write(terminal(s), str)
770-
else
785+
if '\n' in str
771786
refresh_line(s)
787+
else
788+
after = options(s).auto_refresh_time_delay
789+
termbuf = terminal(s)
790+
w = width(termbuf)
791+
delayup = !eof(buf) || old_wait
792+
offset = s.ias.curs_row == 1 || s.indent < 0 ?
793+
sizeof(prompt_string(s.p.prompt)::String) : s.indent
794+
offset += position(buf) - beginofline(buf) # size of current line
795+
if offset + textwidth(str) <= w
796+
# Avoid full update when appending characters to the end
797+
# and an update of curs_row isn't necessary (conservatively estimated)
798+
write(termbuf, str)
799+
elseif after == 0
800+
refresh_line(s)
801+
delayup = false
802+
else
803+
delayup = true
804+
end
805+
if delayup
806+
write(termbuf, spin_seq[mod1(position(buf) - w, length(spin_seq))])
807+
cmove_left(termbuf)
808+
s.refresh_wait = Timer(after) do t
809+
s.refresh_wait === t || return
810+
s.refresh_wait = nothing
811+
refresh_line(s)
812+
end
813+
end
772814
end
815+
nothing
773816
end
817+
const spin_seq = ("", "", "", "")
774818

775819
function edit_insert(buf::IOBuffer, c::StringLike)
776820
if eof(buf)
@@ -804,6 +848,7 @@ function edit_insert_newline(s::PromptState, align::Int = 0 - options(s).auto_in
804848
if ! options(s).auto_indent_bracketed_paste
805849
s.last_newline = time()
806850
end
851+
nothing
807852
end
808853

809854
# align: delete up to 4 spaces to align to a multiple of 4 chars
@@ -2420,7 +2465,7 @@ run_interface(::Prompt) = nothing
24202465

24212466
init_state(terminal, prompt::Prompt) =
24222467
PromptState(terminal, prompt, IOBuffer(), :off, IOBuffer[], 1, InputAreaState(1, 1),
2423-
#=indent(spaces)=# -1, Threads.SpinLock(), 0.0, -Inf)
2468+
#=indent(spaces)=# -1, Threads.SpinLock(), 0.0, -Inf, nothing)
24242469

24252470
function init_state(terminal, m::ModalInterface)
24262471
s = MIState(m, m.modes[1], false, IdDict{Any,Any}())

stdlib/REPL/src/options.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ mutable struct Options
2525
auto_indent_bracketed_paste::Bool # set to true if terminal knows paste mode
2626
# cancel auto-indent when next character is entered within this time frame :
2727
auto_indent_time_threshold::Float64
28+
# refresh after time delay
29+
auto_refresh_time_delay::Float64
2830
# default IOContext settings at the REPL
2931
iocontext::Dict{Symbol,Any}
3032
end
@@ -44,14 +46,15 @@ Options(;
4446
auto_indent_tmp_off = false,
4547
auto_indent_bracketed_paste = false,
4648
auto_indent_time_threshold = 0.005,
49+
auto_refresh_time_delay = 0.05,
4750
iocontext = Dict{Symbol,Any}()) =
4851
Options(hascolor, extra_keymap, tabwidth,
4952
kill_ring_max, region_animation_duration,
5053
beep_duration, beep_blink, beep_maxduration,
5154
beep_colors, beep_use_current,
5255
backspace_align, backspace_adjust, confirm_exit,
5356
auto_indent, auto_indent_tmp_off, auto_indent_bracketed_paste,
54-
auto_indent_time_threshold,
57+
auto_indent_time_threshold, auto_refresh_time_delay,
5558
iocontext)
5659

5760
# for use by REPLs not having an options field

0 commit comments

Comments
 (0)