Skip to content

Commit 1b9bdee

Browse files
committed
pmjs - improve ^C handling wrt leftovers in multi-line statements
1 parent aeba6ce commit 1b9bdee

File tree

1 file changed

+33
-7
lines changed

1 file changed

+33
-7
lines changed

pmjs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ def repl():
155155
got_sigint = 0
156156
statement = ''
157157
readline_skip_chars = 0
158-
158+
inner_loop = False
159+
159160
def save_history():
160161
nonlocal histfile
161162
readline.write_history_file(histfile)
@@ -172,20 +173,43 @@ def repl():
172173
def sigint_handler(signum, frame):
173174
"""
174175
Handle ^C by aborting the entry of the current statement and quitting when double-struck.
176+
177+
Sometimes this happens in the main input() function. When that happens statement is "", because
178+
we have not yet returned from input(). Sometimes it happens in the middle of the inner loop's
179+
input() - in that case, statement is the beginning of a multiline expression. Hitting ^C in the
180+
middle of a multiline express cancels its input, but readline's input() doesn't return, so we
181+
have to print the extra > prompt and fake it by later getting rid of the first readline_skip_chars
182+
characters from the input buffer.
175183
"""
176184
nonlocal got_sigint
177185
nonlocal statement
178186
nonlocal readline_skip_chars
187+
nonlocal inner_loop
179188

180189
got_sigint = got_sigint + 1
181-
if (got_sigint == 1 and statement == "" and readline.get_line_buffer() == ""):
182-
sys.stdout.write("\n(To exit, press Ctrl+C again or Ctrl+D or type .exit)")
183-
elif (got_sigint > 1):
190+
if (got_sigint > 1):
184191
sys.stdout.write("\n")
185192
quit()
186193

194+
if (inner_loop != True):
195+
if (got_sigint == 1 and len(readline.get_line_buffer()) == readline_skip_chars):
196+
# First ^C with nothing in the input buffer
197+
sys.stdout.write("\n(To exit, press Ctrl+C again or Ctrl+D or type .exit)")
198+
elif (got_sigint == 1 and readline.get_line_buffer() != ""):
199+
# Input buffer has text - clear it
200+
got_sigint = 0
201+
readline_skip_chars = len(readline.get_line_buffer())
202+
else:
203+
if (got_sigint == 1 and statement == "" and len(readline.get_line_buffer()) == readline_skip_chars):
204+
# statement == "" means that the inner loop has already seen ^C and is now faking the outer loop
205+
sys.stdout.write("\n(To exit, press Ctrl+C again or Ctrl+D or type .exit)")
206+
elif (got_sigint == 1 and statement != ""):
207+
# ^C happened on inner loop while it was still thinking we were doing a multiline-expression; since
208+
# we can't break the input() function, we set it up to return an outer expression and fake the outer loop
209+
got_sigint = 0
210+
readline_skip_chars = len(readline.get_line_buffer())
211+
187212
sys.stdout.write("\n> ")
188-
readline_skip_chars = len(readline.get_line_buffer())
189213
statement = ""
190214
signal.signal(signal.SIGINT, sigint_handler)
191215

@@ -202,6 +226,7 @@ def repl():
202226
#
203227
while got_sigint < 2:
204228
try:
229+
inner_loop = False
205230
if (statement == ""):
206231
statement = input('> ')[readline_skip_chars:]
207232
readline_skip_chars = 0
@@ -223,10 +248,11 @@ def repl():
223248
# SIGINT is received, so we have to patch things up so that the next-entered line is
224249
# treated as the input at the top of the loop.
225250
while (got_sigint == 0):
226-
more = input('... ')[readline_skip_chars:]
251+
inner_loop = True
252+
lineBuffer = input('... ')
253+
more = lineBuffer[readline_skip_chars:]
227254
readline_skip_chars = 0
228255
if (got_sigint > 0):
229-
got_sigint = 0
230256
statement = more
231257
break
232258
statement = statement + '\n' + more

0 commit comments

Comments
 (0)