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

Feat/persist inline results #3123

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion scripts/packages/VSCodeServer/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ uuid = "9f5989ce-84fe-42d4-91ec-6a7a8d53ed0f"
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
Expand Down
4 changes: 3 additions & 1 deletion scripts/packages/VSCodeServer/src/VSCodeServer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module VSCodeServer
export vscodedisplay, @vscodedisplay, @enter, @run
export view_profile, view_profile_allocs, @profview, @profview_allocs

using REPL, Sockets, Base64, Pkg, UUIDs, Dates, Profile
using REPL, Sockets, Base64, Pkg, UUIDs, Dates, Profile, SHA
import Base: display, redisplay
import Dates
import Profile
Expand Down Expand Up @@ -120,6 +120,8 @@ function serve(args...; is_dev=false, crashreporting_pipename::Union{AbstractStr
msg_dispatcher = JSONRPC.MsgDispatcher()

msg_dispatcher[repl_runcode_request_type] = repl_runcode_request
msg_dispatcher[repl_loadpersist_request_type] = repl_loadpersist_request
msg_dispatcher[repl_rm_persist_line_request_type] = repl_rm_persist_line_request
msg_dispatcher[repl_interrupt_notification_type] = repl_interrupt_request
msg_dispatcher[repl_getvariables_request_type] = repl_getvariables_request
msg_dispatcher[repl_getlazy_request_type] = repl_getlazy_request
Expand Down
41 changes: 34 additions & 7 deletions scripts/packages/VSCodeServer/src/display.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
struct InlineDisplay <: AbstractDisplay
is_repl::Bool
startLine::Int
startColumn::Int
endLine::Int
endColumn::Int
filename::String
codeHash::String
persistent::Bool
end

InlineDisplay() = InlineDisplay(false)
InlineDisplay() = InlineDisplay(false, -1, -1, -1, -1, "", "", false)
InlineDisplay(is_repl::Bool) = InlineDisplay(is_repl, -1, -1, -1, -1, "", "", false)

const PLOT_PANE_ENABLED = Ref(true)
const DIAGNOSTICS_ENABLED = Ref(true)
Expand All @@ -20,7 +28,7 @@ function toggle_progress(_, params::NamedTuple{(:enable,),Tuple{Bool}})
PROGRESS_ENABLED[] = params.enable
end

function fix_displays(; is_repl = false)
function fix_displays(; is_repl=false)
for d in reverse(Base.Multimedia.displays)
if d isa InlineDisplay
popdisplay(d)
Expand All @@ -42,11 +50,26 @@ function with_no_default_display(f)
end
end

function sendDisplayMsg(kind, data)
JSONRPC.send_notification(conn_endpoint[], "display", Dict{String,Any}("kind" => kind, "data" => data))
function sendDisplayMsg(kind, data; startLine=-1, startColumn=-1, endLine=-1, endColumn=-1, filename="", codeHash="")
JSONRPC.send_notification(conn_endpoint[], "display", Dict{String,Any}(
"kind" => kind, "data" => data, "startLine" => startLine, "startColumn" => startColumn,
"endLine" => endLine, "endColumn" => endColumn, "filename" => filename, "codeHash" => codeHash))
JSONRPC.flush(conn_endpoint[])
end

function persistPlot(d::InlineDisplay, mime::String, payload::String)
persistOutputFilePath = d.filename * ".vsjlplt"
jsonD = Dict()
if isfile(persistOutputFilePath)
jsonD = JSON.parsefile(persistOutputFilePath)
end
jsonD[string(d.endLine)] = Dict("startLine" => d.startLine, "startCol" => d.startColumn, "endLine" => d.endLine, "endCol" => d.endColumn,
"mime" => mime, "payload" => payload, "code_hash" => d.codeHash)
open(persistOutputFilePath, "w") do io
JSON.print(io, jsonD)
end
end

function Base.display(d::InlineDisplay, m::MIME, x)
if !PLOT_PANE_ENABLED[]
with_no_default_display(() -> display(m, x))
Expand All @@ -55,7 +78,11 @@ function Base.display(d::InlineDisplay, m::MIME, x)
if mime in DISPLAYABLE_MIMES
# we now all except for `image/...` mime types are not binary
payload = startswith(mime, "image") ? stringmime(m, x) : String(repr(m, x))
sendDisplayMsg(mime, payload)
sendDisplayMsg(mime, payload, startLine=d.startLine, startColumn=d.startColumn, endLine=d.endLine,
endColumn=d.endColumn, filename=d.filename, codeHash=d.codeHash)
if d.persistent
persistPlot(d, mime, payload)
end
else
throw(MethodError(display, (d, m, x)))
end
Expand Down Expand Up @@ -198,15 +225,15 @@ function repl_showingrid_notification(conn, params::NamedTuple{(:code,),Tuple{St
end
end

function internal_vscodedisplay(x, title::AbstractString = "")
function internal_vscodedisplay(x, title::AbstractString="")
if is_table_like(x)
showtable(x, title)
else
_display(InlineDisplay(), x)
end
end

vscodedisplay(x, title::AbstractString = "") = internal_vscodedisplay(x, title)
vscodedisplay(x, title::AbstractString="") = internal_vscodedisplay(x, title)
vscodedisplay(title::AbstractString) = i -> vscodedisplay(i, title)
macro vscodedisplay(x)
:(vscodedisplay($(esc(x)), $(string(x))))
Expand Down
136 changes: 123 additions & 13 deletions scripts/packages/VSCodeServer/src/eval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ end
function repl_interrupt_request(conn, ::Nothing)
println(stderr, "^C")
if EVAL_BACKEND_TASK[] !== nothing && !istaskdone(EVAL_BACKEND_TASK[]) && IS_BACKEND_WORKING[]
schedule(EVAL_BACKEND_TASK[], InterruptException(); error = true)
schedule(EVAL_BACKEND_TASK[], InterruptException(); error=true)
end
end

Expand Down Expand Up @@ -96,6 +96,111 @@ function add_code_to_repl_history(code)
end
end

function savePersistentResults(render_res::ReplRunCodeRequestReturn, source_filename, code_line, code_column, endLine, endColumn, code_hash)
persistOutputFilePath = source_filename * ".vsjlpo"
jsonD = Dict()
if isfile(persistOutputFilePath)
jsonD = JSON.parsefile(persistOutputFilePath)
end
jsonD[string(endLine)] = Dict("startLine" => code_line, "startCol" => code_column, "endLine" => endLine, "endCol" => endColumn,
"inline" => render_res.inline, "all" => render_res.all, "code_hash" => code_hash)
open(persistOutputFilePath, "w") do io
JSON.print(io, jsonD)
end
end



function repl_loadpersist_request(conn, params::ReplLoadPersistParams)::ReplLoadPersistReturn
persistOutputFilePath = params.filename * ".vsjlpo"
tryLineResult = true
if !isfile(persistOutputFilePath)
tryLineResult = false
end
persistRanges = Vector{PersistRange}()
persistResults = Vector{ReplRunCodeRequestReturn}()
msg = ""
if tryLineResult
try
jsonD = JSON.parsefile(persistOutputFilePath)
endLines = sort(parse.(Int, keys(jsonD)))

for l in endLines
jData = jsonD[string(l)]
push!(persistRanges,
PersistRange(jData["startLine"], jData["startCol"], jData["endLine"], jData["endCol"], jData["code_hash"]))
push!(persistResults, ReplRunCodeRequestReturn(jData["inline"], jData["all"]))
end
catch err
msg = string(err)
end
end
# load plots
persistPlotOutputFilePath = params.filename * ".vsjlplt"
if !isfile(persistPlotOutputFilePath)
return ReplLoadPersistReturn(msg, persistRanges, persistResults, nothing)
end
persistPlots = Vector{PlotData}()
try
jsonD = JSON.parsefile(persistPlotOutputFilePath)
endLines = sort(parse.(Int, keys(jsonD)))

for l in endLines
jData = jsonD[string(l)]
pltData = PlotData(PersistRange(jData["startLine"],
jData["startCol"], jData["endLine"], jData["endCol"], jData["code_hash"]),
jData["mime"], jData["payload"])
push!(persistPlots, pltData)
end
catch err
msg *= "\nPlot Loading Error:\n" * string(err)
end
return ReplLoadPersistReturn(msg, persistRanges, persistResults, persistPlots)
end

function repl_rm_persist_line_request(conn, params::ReplRmPersistLineParams)::ReplRmPersistLineReturn
persistOutputFilePath = params.filename * ".vsjlpo"
msg = ""
tryLineResult = true
if !isfile(persistOutputFilePath)
tryLineResult = false
end
if tryLineResult
try
jsonD = JSON.parsefile(persistOutputFilePath)
endLine = string(params.endLine)
if haskey(jsonD, endLine)
delete!(jsonD, endLine)
end

open(persistOutputFilePath, "w") do io
JSON.print(io, jsonD)
end
catch err
msg = string(err)
end
end

# load plots
persistPlotOutputFilePath = params.filename * ".vsjlplt"
if !isfile(persistPlotOutputFilePath)
return ReplRmPersistLineReturn(msg)
end
try
jsonD = JSON.parsefile(persistPlotOutputFilePath)
endLine = string(params.endLine)
if haskey(jsonD, endLine)
delete!(jsonD, endLine)
end
open(persistPlotOutputFilePath, "w") do io
JSON.print(io, jsonD)
end
catch err
msg *= "\nPlot Loading Error:\n" * string(err)
end
return ReplRmPersistLineReturn(msg)
end

CAN_SET_ANS = Ref{Bool}(true)
function repl_runcode_request(conn, params::ReplRunCodeRequestParams)::ReplRunCodeRequestReturn
run_with_backend() do
Expand All @@ -117,12 +222,14 @@ function repl_runcode_request(conn, params::ReplRunCodeRequestParams)::ReplRunCo
show_code = params.showCodeInREPL
show_result = params.showResultInREPL
show_error = params.showErrorInREPL
persistentEnabled = params.persistentEnabled
endLine = params.endLine
endColumn = params.endColumn
try
JSONRPC.send_notification(conn_endpoint[], "repl/starteval", nothing)
catch err
@debug "Could not send repl/starteval notification."
end

f = () -> hideprompt() do
revise()

Expand Down Expand Up @@ -154,7 +261,7 @@ function repl_runcode_request(conn, params::ReplRunCodeRequestParams)::ReplRunCo

return withpath(source_filename) do
res = try
val = inlineeval(resolved_mod, source_code, code_line, code_column, source_filename, softscope = params.softscope)
val = inlineeval(resolved_mod, source_code, code_line, code_column, source_filename, softscope=params.softscope)
if CAN_SET_ANS[]
try
@static if @isdefined setglobal!
Expand Down Expand Up @@ -204,11 +311,11 @@ function repl_runcode_request(conn, params::ReplRunCodeRequestParams)::ReplRunCo
else
try
if !ends_with_semicolon(source_code)
Base.invokelatest(display, InlineDisplay(), res)
Base.invokelatest(display, InlineDisplay(false, code_line, code_column, endLine, endColumn, source_filename, bytes2hex(sha256(source_code)), persistentEnabled), res)
end
catch err
if !(err isa MethodError && err.f === display)
printstyled(stderr, "Display Error: ", color = Base.error_color(), bold = true)
printstyled(stderr, "Display Error: ", color=Base.error_color(), bold=true)
Base.display_error(stderr, err, catch_backtrace())
end
end
Expand All @@ -218,6 +325,9 @@ function repl_runcode_request(conn, params::ReplRunCodeRequestParams)::ReplRunCo
res = nothing
end

if persistentEnabled
savePersistentResults(safe_render(res), source_filename, code_line, code_column, endLine, endColumn, bytes2hex(sha256(source_code)))
end
return safe_render(res)
end
end
Expand All @@ -227,7 +337,7 @@ function repl_runcode_request(conn, params::ReplRunCodeRequestParams)::ReplRunCo
end

# don't inline this so we can find it in the stacktrace
@noinline function inlineeval(m, code, code_line, code_column, file; softscope = false)
@noinline function inlineeval(m, code, code_line, code_column, file; softscope=false)
code = string('\n'^code_line, ' '^code_column, code)
args = softscope && VERSION >= v"1.5" ? (REPL.softscope, m, code, file) : (m, code, file)
return Base.invokelatest(include_string, args...)
Expand Down Expand Up @@ -262,13 +372,13 @@ Must return a `ReplRunCodeRequestReturn` with the following fields:
- `stackframe::Vector{Frame}`: Optional, should only be given on an error
"""
function render(x)
plain = sprintlimited(MIME"text/plain"(), x, limit = MAX_RESULT_LENGTH)
plain = sprintlimited(MIME"text/plain"(), x, limit=MAX_RESULT_LENGTH)
md = try
sprintlimited(MIME"text/markdown"(), x, limit = MAX_RESULT_LENGTH)
sprintlimited(MIME"text/markdown"(), x, limit=MAX_RESULT_LENGTH)
catch _
codeblock(plain)
end
inline = strlimit(first(split(plain, "\n")), limit = INLINE_RESULT_LENGTH)
inline = strlimit(first(split(plain, "\n")), limit=INLINE_RESULT_LENGTH)
return ReplRunCodeRequestReturn(inline, md)
end

Expand All @@ -291,14 +401,14 @@ unwrap_loaderror(err::LoadError) = err.error
unwrap_loaderror(err) = err

function sprint_error(err)
sprintlimited(err, [], func = Base.display_error, limit = MAX_RESULT_LENGTH)
sprintlimited(err, [], func=Base.display_error, limit=MAX_RESULT_LENGTH)
end

function render(err::EvalError)
bt = crop_backtrace(err.bt)

errstr = sprint_error_unwrap(err.err)
inline = strlimit(first(split(errstr, "\n")), limit = INLINE_RESULT_LENGTH)
inline = strlimit(first(split(errstr, "\n")), limit=INLINE_RESULT_LENGTH)
all = string('\n', codeblock(errstr), '\n', backtrace_string(bt))

# handle duplicates e.g. from recursion
Expand All @@ -319,7 +429,7 @@ function render(stack::EvalErrorStack)
append!(complete_bt, bt)

errstr = sprint_error_unwrap(err)
inline *= strlimit(first(split(errstr, "\n")), limit = INLINE_RESULT_LENGTH)
inline *= strlimit(first(split(errstr, "\n")), limit=INLINE_RESULT_LENGTH)
all *= string('\n', codeblock(errstr), '\n', backtrace_string(bt))
end

Expand Down Expand Up @@ -379,7 +489,7 @@ function backtrace_string(bt)

file = string(frame.file)
full_file = fullpath(something(Base.find_source_file(file), file))
cmd = vscode_cmd_uri("language-julia.openFile"; path = full_file, line = frame.line)
cmd = vscode_cmd_uri("language-julia.openFile"; path=full_file, line=frame.line)

print(io, counter, ". `")
Base.StackTraces.show_spec_linfo(io, frame)
Expand Down
Loading