@@ -73,6 +73,9 @@ mutable struct PromptState <: ModeState
73
73
# indentation of lines which do not include the prompt
74
74
# if negative, the width of the prompt is used
75
75
indent:: Int
76
+ refresh_lock:: Threads.AbstractLock
77
+ # this would better be Threads.Atomic{Float64}, but not supported on some platforms
78
+ beeping:: Float64
76
79
end
77
80
78
81
options (s:: PromptState ) = isdefined (s. p, :repl ) ? s. p. repl. options : Base. REPL. Options ()
@@ -116,9 +119,54 @@ complete_line(c::EmptyCompletionProvider, s) = [], true, true
116
119
terminal (s:: IO ) = s
117
120
terminal (s:: PromptState ) = s. terminal
118
121
122
+
123
+ # these may be better stored in Prompt or LineEditREPL
124
+ const BEEP_DURATION = Ref (0.2 )
125
+ const BEEP_BLINK = Ref (0.2 )
126
+ const BEEP_MAXDURATION = Ref (1.0 )
127
+ const BEEP_COLORS = [" \e [90m" ] # gray (text_colors not yet available)
128
+ const BEEP_USE_CURRENT = Ref (true )
129
+
130
+ function beep (s:: PromptState , duration:: Real = BEEP_DURATION[], blink:: Real = BEEP_BLINK[],
131
+ maxduration:: Real = BEEP_MAXDURATION[];
132
+ colors= BEEP_COLORS, use_current:: Bool = BEEP_USE_CURRENT[])
133
+ isinteractive () || return # some tests fail on some platforms
134
+ s. beeping = min (s. beeping + duration, maxduration)
135
+ @async begin
136
+ trylock (s. refresh_lock) || return
137
+ orig_prefix = s. p. prompt_prefix
138
+ colors = Base. copymutable (colors)
139
+ use_current && push! (colors, orig_prefix)
140
+ i = 0
141
+ while s. beeping > 0.0
142
+ prefix = colors[mod1 (i+= 1 , end )]
143
+ s. p. prompt_prefix = prefix
144
+ refresh_multi_line (s, beeping= true )
145
+ sleep (blink)
146
+ s. beeping -= blink
147
+ end
148
+ s. p. prompt_prefix = orig_prefix
149
+ refresh_multi_line (s, beeping= true )
150
+ s. beeping = 0.0
151
+ unlock (s. refresh_lock)
152
+ end
153
+ end
154
+
155
+ function cancel_beep (s:: PromptState )
156
+ # wait till beeping finishes
157
+ while ! trylock (s. refresh_lock)
158
+ s. beeping = 0.0
159
+ sleep (.05 )
160
+ end
161
+ unlock (s. refresh_lock)
162
+ end
163
+
164
+ beep (:: ModeState ) = nothing
165
+ cancel_beep (:: ModeState ) = nothing
166
+
119
167
for f in [:terminal , :on_enter , :add_history , :buffer , :(Base. isempty),
120
168
:replace_line , :refresh_multi_line , :input_string , :update_display_buffer ,
121
- :empty_undo , :push_undo , :pop_undo , :options ]
169
+ :empty_undo , :push_undo , :pop_undo , :options , :cancel_beep , :beep ]
122
170
@eval ($ f)(s:: MIState , args... ) = $ (f)(state (s), args... )
123
171
end
124
172
@@ -175,16 +223,19 @@ end
175
223
176
224
# Prompt Completions
177
225
function complete_line (s:: MIState )
178
- complete_line (state (s), s. key_repeats)
179
- refresh_line (s)
180
- :complete_line
226
+ if complete_line (state (s), s. key_repeats)
227
+ refresh_line (s)
228
+ :complete_line
229
+ else
230
+ beep (s)
231
+ :ignore
232
+ end
181
233
end
182
234
183
235
function complete_line (s:: PromptState , repeats)
184
236
completions, partial, should_complete = complete_line (s. p. complete, s)
185
- if isempty (completions)
186
- beep (terminal (s))
187
- elseif ! should_complete
237
+ isempty (completions) && return false
238
+ if ! should_complete
188
239
# should_complete is false for cases where we only want to show
189
240
# a list of possible completions but not complete, e.g. foo(\t
190
241
show_completions (s, completions)
@@ -205,6 +256,7 @@ function complete_line(s::PromptState, repeats)
205
256
show_completions (s, completions)
206
257
end
207
258
end
259
+ true
208
260
end
209
261
210
262
clear_input_area (terminal, s) = (_clear_input_area (terminal, s. ias); s. ias = InputAreaState (0 , 0 ))
@@ -230,9 +282,9 @@ prompt_string(p::Prompt) = prompt_string(p.prompt)
230
282
prompt_string (s:: AbstractString ) = s
231
283
prompt_string (f:: Function ) = Base. invokelatest (f)
232
284
233
- refresh_multi_line (s:: ModeState ) = refresh_multi_line (terminal (s), s)
234
- refresh_multi_line (termbuf:: TerminalBuffer , s:: ModeState ) = refresh_multi_line (termbuf, terminal (s), s)
235
- refresh_multi_line (termbuf:: TerminalBuffer , term, s:: ModeState ) = (@assert term == terminal (s); refresh_multi_line (termbuf,s))
285
+ refresh_multi_line (s:: ModeState ; kw ... ) = refresh_multi_line (terminal (s), s; kw ... )
286
+ refresh_multi_line (termbuf:: TerminalBuffer , s:: ModeState ; kw ... ) = refresh_multi_line (termbuf, terminal (s), s; kw ... )
287
+ refresh_multi_line (termbuf:: TerminalBuffer , term, s:: ModeState ; kw ... ) = (@assert term == terminal (s); refresh_multi_line (termbuf,s; kw ... ))
236
288
function refresh_multi_line (termbuf:: TerminalBuffer , terminal:: UnixTerminal , buf, state:: InputAreaState , prompt = " " ; indent = 0 )
237
289
_clear_input_area (termbuf, state)
238
290
@@ -297,7 +349,6 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf
297
349
298
350
# columns are 1 based
299
351
cmove_col (termbuf, curs_pos + 1 )
300
-
301
352
# Updated cur_row,curs_row
302
353
return InputAreaState (cur_row, curs_row)
303
354
end
@@ -558,7 +609,7 @@ function edit_backspace(s::PromptState, align::Bool=options(s).backspace_align,
558
609
refresh_line (s)
559
610
else
560
611
pop_undo (s)
561
- beep (terminal (s) )
612
+ beep (s )
562
613
end
563
614
end
564
615
@@ -607,7 +658,7 @@ function edit_delete(s)
607
658
refresh_line (s)
608
659
else
609
660
pop_undo (s)
610
- beep (terminal (s) )
661
+ beep (s )
611
662
end
612
663
:edit_delete
613
664
end
676
727
677
728
function edit_yank (s:: MIState )
678
729
if isempty (s. kill_ring)
679
- beep (terminal (s) )
730
+ beep (s )
680
731
return :ignore
681
732
end
682
733
setmark (s) # necessary for edit_yank_pop
689
740
function edit_yank_pop (s:: MIState , require_previous_yank= true )
690
741
repeat = s. last_action ∈ (:edit_yank , :edit_yank_pop )
691
742
if require_previous_yank && ! repeat || isempty (s. kill_ring)
692
- beep (terminal (s) )
743
+ beep (s )
693
744
:ignore
694
745
else
695
746
require_previous_yank || repeat || setmark (s)
@@ -857,7 +908,7 @@ function history_prev(s, hist)
857
908
move_input_start (s)
858
909
refresh_line (s)
859
910
else
860
- beep (terminal (s) )
911
+ beep (s )
861
912
end
862
913
end
863
914
function history_next (s, hist)
@@ -867,7 +918,7 @@ function history_next(s, hist)
867
918
move_input_end (s)
868
919
refresh_line (s)
869
920
else
870
- beep (terminal (s) )
921
+ beep (s )
871
922
end
872
923
end
873
924
@@ -1249,12 +1300,12 @@ end
1249
1300
terminal (s:: SearchState ) = s. terminal
1250
1301
1251
1302
function update_display_buffer (s:: SearchState , data)
1252
- history_search (data. histprompt. hp, data. query_buffer, data. response_buffer, data. backward, false ) || beep (terminal (s) )
1303
+ history_search (data. histprompt. hp, data. query_buffer, data. response_buffer, data. backward, false ) || beep (s )
1253
1304
refresh_line (s)
1254
1305
end
1255
1306
1256
1307
function history_next_result (s:: MIState , data:: SearchState )
1257
- history_search (data. histprompt. hp, data. query_buffer, data. response_buffer, data. backward, true ) || beep (terminal (s) )
1308
+ history_search (data. histprompt. hp, data. query_buffer, data. response_buffer, data. backward, true ) || beep (s )
1258
1309
refresh_line (data)
1259
1310
end
1260
1311
@@ -1307,9 +1358,11 @@ function show(io::IO, s::PrefixSearchState)
1307
1358
isdefined (s,:mi ) ? s. mi : " no MI" )
1308
1359
end
1309
1360
1310
- refresh_multi_line (termbuf:: TerminalBuffer , terminal:: UnixTerminal ,
1311
- s:: Union{PromptState,PrefixSearchState} ) = s. ias =
1312
- refresh_multi_line (termbuf, terminal, buffer (s), s. ias, s, indent = s. indent)
1361
+ function refresh_multi_line (termbuf:: TerminalBuffer , terminal:: UnixTerminal ,
1362
+ s:: Union{PromptState,PrefixSearchState} ; beeping= false )
1363
+ beeping || cancel_beep (s)
1364
+ s. ias = refresh_multi_line (termbuf, terminal, buffer (s), s. ias, s, indent = s. indent)
1365
+ end
1313
1366
1314
1367
input_string (s:: PrefixSearchState ) = String (take! (copy (s. response_buffer)))
1315
1368
@@ -1456,15 +1509,15 @@ function setup_search_keymap(hp)
1456
1509
1457
1510
# Backspace/^H
1458
1511
' \b ' => (s,data,c)-> (edit_backspace (data. query_buffer) ?
1459
- update_display_buffer (s, data) : beep (terminal (s) )),
1512
+ update_display_buffer (s, data) : beep (s )),
1460
1513
127 => KeyAlias (' \b ' ),
1461
1514
# Meta Backspace
1462
1515
" \e\b " => (s,data,c)-> (edit_delete_prev_word (data. query_buffer) ?
1463
- update_display_buffer (s, data) : beep (terminal (s) )),
1516
+ update_display_buffer (s, data) : beep (s )),
1464
1517
" \e\x 7f" => " \e\b " ,
1465
1518
# Word erase to whitespace
1466
1519
" ^W" => (s,data,c)-> (edit_werase (data. query_buffer) ?
1467
- update_display_buffer (s, data) : beep (terminal (s) )),
1520
+ update_display_buffer (s, data) : beep (s )),
1468
1521
# ^C and ^D
1469
1522
" ^C" => (s,data,c)-> (edit_clear (data. query_buffer);
1470
1523
edit_clear (data. response_buffer);
@@ -1565,6 +1618,7 @@ function move_line_end(buf::IOBuffer)
1565
1618
end
1566
1619
1567
1620
function commit_line (s)
1621
+ cancel_beep (s)
1568
1622
move_input_end (s)
1569
1623
refresh_line (s)
1570
1624
println (terminal (s))
@@ -1712,6 +1766,7 @@ AnyDict(
1712
1766
try # raise the debugger if present
1713
1767
ccall (:jl_raise_debugger , Int, ())
1714
1768
end
1769
+ cancel_beep (s)
1715
1770
move_input_end (s)
1716
1771
refresh_line (s)
1717
1772
print (terminal (s), " ^C\n\n " )
@@ -1822,6 +1877,7 @@ activate(m::ModalInterface, s::MIState, termbuf, term::TextTerminal) =
1822
1877
commit_changes (t:: UnixTerminal , termbuf) = write (t, take! (termbuf. out_stream))
1823
1878
1824
1879
function transition (f:: Function , s:: MIState , newmode)
1880
+ cancel_beep (s)
1825
1881
if newmode === :abort
1826
1882
s. aborted = true
1827
1883
return
@@ -1879,7 +1935,7 @@ run_interface(::Prompt) = nothing
1879
1935
1880
1936
init_state (terminal, prompt:: Prompt ) =
1881
1937
PromptState (terminal, prompt, IOBuffer (), IOBuffer[], 1 , InputAreaState (1 , 1 ),
1882
- #= indent(spaces)=# - 1 )
1938
+ #= indent(spaces)=# - 1 , Threads . SpinLock (), 0.0 )
1883
1939
1884
1940
function init_state (terminal, m:: ModalInterface )
1885
1941
s = MIState (m, m. modes[1 ], false , Dict {Any,Any} ())
@@ -1941,7 +1997,7 @@ function edit_undo!(s::MIState)
1941
1997
if edit_undo! (state (s))
1942
1998
:edit_undo!
1943
1999
else
1944
- beep (terminal (s) )
2000
+ beep (s )
1945
2001
:ignore
1946
2002
end
1947
2003
end
@@ -1958,7 +2014,7 @@ function edit_redo!(s::MIState)
1958
2014
if s. last_action ∈ (:edit_redo! , :edit_undo! ) && edit_redo! (state (s))
1959
2015
:edit_redo!
1960
2016
else
1961
- beep (terminal (s) )
2017
+ beep (s )
1962
2018
:ignore
1963
2019
end
1964
2020
end
0 commit comments