Skip to content

Commit

Permalink
handle multiline shell commands
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth committed May 2, 2021
1 parent 7c4115e commit ee3d6c8
Showing 1 changed file with 41 additions and 18 deletions.
59 changes: 41 additions & 18 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ function setup_interface(
pkg_prompt_len = length(PKG_PROMPT)
shell_prompt_len = length(SHELL_PROMPT)
help_prompt_len = length(HELP_PROMPT)
curr_prompt_len = 0
while oldpos <= lastindex(input) # loop until all lines have been executed
if JL_PROMPT_PASTE[]
# Check if the next statement starts with "julia> ", in that case
Expand All @@ -1071,27 +1072,32 @@ function setup_interface(
if (firstline || isprompt_paste) && startswith(SubString(input, oldpos), JULIA_PROMPT)
isprompt_paste = true
oldpos += jl_prompt_len
curr_prompt_len = jl_prompt_len
transition(s, julia_prompt)
# Check if input line starts with "pkg> ", remove it if we are in prompt paste mode and switch mode
elseif (firstline || isprompt_paste) && startswith(SubString(input, oldpos), PKG_PROMPT)
isprompt_paste = true
oldpos += pkg_prompt_len
curr_prompt_len = pkg_prompt_len
Base.active_repl.interface.modes[1].keymap_dict[']'](s, o...)
# Check if input line starts with "(xxx) pkg> ", remove it if we are in prompt paste mode and switch mode
# Check if input line starts with "(...) pkg> ", remove it if we are in prompt paste mode and switch mode
elseif (firstline || isprompt_paste) && startswith(SubString(input, oldpos), Regex("\\(.+\\) $(PKG_PROMPT)"))
env_pkg_prompt = match(Regex("\\(.+\\) $(PKG_PROMPT)"), SubString(input, oldpos)).match
isprompt_paste = true
oldpos += length(env_pkg_prompt)
curr_prompt_len = length(env_pkg_prompt)
oldpos += curr_prompt_len
Base.active_repl.interface.modes[1].keymap_dict[']'](s, o...)
# Check if input line starts with "shell> ", remove it if we are in prompt paste mode and switch mode
elseif (firstline || isprompt_paste) && startswith(SubString(input, oldpos), SHELL_PROMPT)
isprompt_paste = true
oldpos += shell_prompt_len
curr_prompt_len = shell_prompt_len
transition(s, shell_mode)
# Check if input line starts with "help?> ", remove it if we are in prompt paste mode and switch mode
elseif (firstline || isprompt_paste) && startswith(SubString(input, oldpos), HELP_PROMPT)
isprompt_paste = true
oldpos += help_prompt_len
curr_prompt_len = help_prompt_len
transition(s, help_mode)
# If we are prompt pasting and current statement does not begin with a mode prefix, skip to next line
elseif isprompt_paste
Expand All @@ -1102,29 +1108,46 @@ function setup_interface(
continue
end
end
ast, pos = Meta.parse(input, oldpos, raise=false, depwarn=false)
if (isa(ast, Expr) && (ast.head === :error || ast.head === :incomplete)) ||
(pos > ncodeunits(input) && !endswith(input, '\n'))
# remaining text is incomplete (an error, or parser ran to the end but didn't stop with a newline):
# Insert all the remaining text as one line (might be empty)
tail = input[oldpos:end]
if !firstline
# strip leading whitespace, but only if it was the result of executing something
# (avoids modifying the user's current leading wip line)
tail = lstrip(tail)
if s.current_mode == julia_prompt
ast, pos = Meta.parse(input, oldpos, raise=false, depwarn=false)
if (isa(ast, Expr) && (ast.head === :error || ast.head === :incomplete)) ||
(pos > ncodeunits(input) && !endswith(input, '\n'))
# remaining text is incomplete (an error, or parser ran to the end but didn't stop with a newline):
# Insert all the remaining text as one line (might be empty)
tail = input[oldpos:end]
if !firstline
# strip leading whitespace, but only if it was the result of executing something
# (avoids modifying the user's current leading wip line)
tail = lstrip(tail)
end
if isprompt_paste # remove indentation spaces corresponding to the prompt
tail = replace(tail, r"^"m * ' '^curr_prompt_len => "")
end
LineEdit.replace_line(s, tail, true)
LineEdit.refresh_line(s)
break
end
if isprompt_paste # remove indentation spaces corresponding to the prompt
tail = replace(tail, r"^"m * ' '^jl_prompt_len => "")
elseif s.current_mode == shell_mode # handle multiline shell commands
lines = split(input[oldpos:end], '\n')
pos = oldpos + sizeof(lines[1]) + 1
if length(lines) > 1
for line in lines[2:end]
# to be recognized as a multiline shell command, the lines must be indented to the
# same prompt position
if !startswith(line, ' '^curr_prompt_len)
break
end
pos += sizeof(line) + 1
end
end
LineEdit.replace_line(s, tail, true)
LineEdit.refresh_line(s)
break
else
pos = oldpos + findfirst('\n', input[oldpos:end]) + 1
end
# get the line and strip leading and trailing whitespace
line = strip(input[oldpos:prevind(input, pos)])
if !isempty(line)
if isprompt_paste # remove indentation spaces corresponding to the prompt
line = replace(line, r"^"m * ' '^jl_prompt_len => "")
line = replace(line, r"^"m * ' '^curr_prompt_len => "")
end
# put the line on the screen and history
LineEdit.replace_line(s, line)
Expand Down

0 comments on commit ee3d6c8

Please sign in to comment.