Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: remove repl prompt prefix before parsing #17599

Merged
merged 10 commits into from
Aug 8, 2016
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ Julia v0.6.0 Release Notes
New language features
---------------------

* The REPL now supports something called *prompt pasting*.
This activates when pasting text that starts with `julia> ` into the REPL.
In that case, only expressions starting with `julia> ` are parsed, the rest are removed.
This makes it possible to paste a chunk of code that has been copied from a REPL session
without having to scrub away prompts and outputs.
This can be disabled or enabled at will with `Base.REPL.enable_promptpaste(::Bool)`.

Language changes
----------------

Expand Down
31 changes: 29 additions & 2 deletions base/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ abstract AbstractREPL

answer_color(::AbstractREPL) = ""

const JULIA_PROMPT = "julia> "

type REPLBackend
"channel for AST"
repl_channel::Channel
Expand Down Expand Up @@ -207,7 +209,7 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
hit_eof = false
while true
Base.reseteof(repl.terminal)
write(repl.terminal, "julia> ")
write(repl.terminal, JULIA_PROMPT)
line = ""
ast = nothing
interrupted = false
Expand Down Expand Up @@ -692,6 +694,9 @@ end
repl_filename(repl, hp::REPLHistoryProvider) = "REPL[$(length(hp.history)-hp.start_idx)]"
repl_filename(repl, hp) = "REPL"

const JL_PROMT_PASTE = Ref(true)
enable_promtpaste(v::Bool) = JL_PROMT_PASTE[] = v

function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_repl_keymap = Dict{Any,Any}[])
###
#
Expand Down Expand Up @@ -723,7 +728,7 @@ function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_rep
replc = REPLCompletionProvider(repl)

# Set up the main Julia prompt
julia_prompt = Prompt("julia> ";
julia_prompt = Prompt(JULIA_PROMPT;
# Copy colors from the prompt object
prompt_prefix = hascolor ? repl.prompt_color : "",
prompt_suffix = hascolor ?
Expand Down Expand Up @@ -837,7 +842,29 @@ function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_rep
input = takebuf_string(sbuffer)
oldpos = start(input)
firstline = true
isprompt_paste = false
while !done(input, oldpos) # loop until all lines have been executed
if JL_PROMT_PASTE[]
# Check if the next statement starts with "julia> ", in that case
# skip it. But first skip whitespace
while input[oldpos] in ('\n', ' ', '\t')
oldpos = nextind(input, oldpos)
oldpos >= sizeof(input) && return
end
# Check if input line starts with "julia> ", remove it if we are in prompt paste mode
jl_prompt_len = 7
if (firstline || isprompt_paste) && (oldpos + jl_prompt_len <= sizeof(input) && input[oldpos:oldpos+jl_prompt_len-1] == JULIA_PROMPT)
isprompt_paste = true
oldpos += jl_prompt_len
# If we are prompt pasting and current statement does not begin with julia> , skip to next line
elseif isprompt_paste
while input[oldpos] != '\n'
oldpos = nextind(input, oldpos)
oldpos >= sizeof(input) && return
end
continue
end
end
ast, pos = Base.syntax_deprecation_warnings(false) do
Base.parse(input, oldpos, raise=false)
end
Expand Down
7 changes: 7 additions & 0 deletions doc/manual/interacting-with-julia.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ There are a number useful features unique to interactive work. In addition to sh
julia> ans
"12"

In Julia mode, the REPL supports something called *prompt pasting*. This activates when pasting text that starts with ``julia> `` into the REPL.
In that case, only expressions starting with ``julia> `` are parsed, others are removed.
This makes it is possible to paste a chunk of code that has been copied from a REPL session without having to scrub away prompts and outputs.
This feature is enabled by default but can be disabled or enabled at will with ``Base.REPL.enable_promptpaste(::Bool)``.
If it is enabled, you can try it out by pasting the code block above this paragraph straight into the REPL.
This feature does not work on the standard Windows command prompt due to its limitation at detecting when a paste occurs.

Help mode
~~~~~~~~~

Expand Down
60 changes: 60 additions & 0 deletions test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,66 @@ begin
# Try entering search mode while in custom repl mode
LineEdit.enter_search(s, custom_histp, true)
end

# Test removal of prompt in bracket pasting
begin
stdin_write, stdout_read, stderr_read, repl = fake_repl()

repl.interface = REPL.setup_interface(repl)
repl_mode = repl.interface.modes[1]
shell_mode = repl.interface.modes[2]
help_mode = repl.interface.modes[3]

repltask = @async begin
Base.REPL.run_repl(repl)
end

c = Condition()

sendrepl2(cmd) = write(stdin_write,"$cmd\n notify(c)\n")

# Test removal of prefix in single statement paste
sendrepl2("\e[200~julia> A = 2\e[201~\n")
wait(c)
@test A == 2

# Test removal of prefix in multiple statement paste
sendrepl2("""\e[200~
julia> type T17599; a::Int; end

julia> function foo(julia)
julia> 3
end

julia> A = 3\e[201~
""")
wait(c)
@test A == 3
@test foo(4)
@test T17599(3).a == 3
@test !foo(2)

sendrepl2("""\e[200~
julia> goo(x) = x + 1
error()

julia> A = 4
4\e[201~
""")
wait(c)
@test A == 4
@test goo(4) == 5

# Test prefix removal only active in bracket paste mode
sendrepl2("julia = 4\n julia> 3 && (A = 1)\n")
wait(c)
@test A == 1

# Close repl
write(stdin_write, '\x04')
wait(repltask)
end

# Simple non-standard REPL tests
if !is_windows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
stdin_write, stdout_read, stdout_read, repl = fake_repl()
Expand Down