Skip to content

Commit

Permalink
remove REPL prompt prefix before parsing in bracket paste mode (Julia…
Browse files Browse the repository at this point in the history
…Lang#17599)

* remove REPL prompt prefix before parsing in bracket paste mode

* remove created tmpdir and fix var name

* use realpath to check path equality

* remove prompt paste from shell> and help?> and ignore non prompted lines

* add news

* add option and update manual

* fix typo variable name

* fix rst code quote [ci skip]

* split into more lines [ci skip]

* update grammar and formatting [ci skip]
  • Loading branch information
KristofferC authored and mfasi committed Sep 5, 2016
1 parent 2455654 commit 86765d9
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 2 deletions.
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

0 comments on commit 86765d9

Please sign in to comment.