StatProfilerHTML.jl report
Generated on Thu, 21 Dec 2023 13:06:16
File source code
Line Exclusive Inclusive Code
1 # Most of the code here is copied from the Juno codebase
2
3 using REPL
4 using REPL.LineEdit
5
6 const ENABLE_SHELL_INTEGRATION = Ref{Bool}(false)
7
8 isREPL() = isdefined(Base, :active_repl) &&
9 isdefined(Base.active_repl, :interface) &&
10 isdefined(Base.active_repl.interface, :modes) &&
11 isdefined(Base.active_repl, :mistate) &&
12 isdefined(Base.active_repl.mistate, :current_mode)
13
14 juliaprompt = "julia> "
15
16 current_prompt = juliaprompt
17
18 function get_main_mode(repl = Base.active_repl)
19 mode = repl.interface.modes[1]
20 mode isa LineEdit.Prompt || error("no julia repl mode found")
21 mode
22 end
23
24
272 (100 %) samples spent in hideprompt
272 (100 %) (incl.) when called from #65 line 150
function hideprompt(f)
25 isREPL() || return f()
26
27 repl = Base.active_repl
28 mistate = repl.mistate
29 mode = mistate.current_mode
30
31 buf = String(take!(copy(LineEdit.buffer(mistate))))
32
33 # clear input buffer
34 truncate(LineEdit.buffer(mistate), 0)
35 LineEdit.refresh_multi_line(mistate)
36
37 print(stdout, "\e[1K\r")
38 272 (100 %)
272 (100 %) samples spent calling #66
r = f()
39
40 flush(stdout)
41 flush(stderr)
42 sleep(0.05)
43
44 # TODO Fix this
45 # pos = @rpc cursorpos()
46 pos = 1, 1
47 pos[1] != 0 && println()
48
49 # restore prompt
50 if applicable(LineEdit.write_prompt, stdout, mode)
51 LineEdit.write_prompt(stdout, mode)
52 elseif applicable(LineEdit.write_prompt, stdout, mode, true)
53 LineEdit.write_prompt(stdout, mode, true)
54 elseif mode isa LineEdit.PrefixHistoryPrompt || :parent_prompt in fieldnames(typeof(mode))
55 if applicable(LineEdit.write_prompt, stdout, mode.parent_prompt)
56 LineEdit.write_prompt(stdout, mode.parent_prompt)
57 elseif applicable(LineEdit.write_prompt, stdout, mode.parent_prompt, true)
58 LineEdit.write_prompt(stdout, mode.parent_prompt, true)
59 else
60 printstyled(stdout, current_prompt, color = :green, bold = true)
61 end
62 else
63 printstyled(stdout, current_prompt, color = :green, bold = true)
64 end
65
66 truncate(LineEdit.buffer(mistate), 0)
67
68 # restore input buffer
69 LineEdit.edit_insert(LineEdit.buffer(mistate), buf)
70 LineEdit.refresh_multi_line(mistate)
71 r
72 end
73
74 si(f) = (args...) -> ENABLE_SHELL_INTEGRATION[] ? f(args...) : ""
75
76 function sanitize_shell_integration_string(cmd)
77 replace(replace(replace(cmd, "\n" => "<LF>"), ";" => "<CL>"), "\a" => "<ST>")
78 end
79
80 const SHELL = (
81 prompt_start = si(() -> "\e]633;A\a"),
82 prompt_end = si(() -> "\e]633;B\a"),
83 output_start = si(() -> "\e]633;C\a"),
84 output_end = si(function ()
85 if REPL_PROMPT_STATE[] === REPLPromptStates.NoUpdate
86 return ""
87 elseif REPL_PROMPT_STATE[] === REPLPromptStates.NoStatus
88 REPL_PROMPT_STATE[] = REPLPromptStates.NoUpdate
89 return "\e]633;D\a"
90 else
91 exitcode = REPL_PROMPT_STATE[] == REPLPromptStates.Error
92 REPL_PROMPT_STATE[] = REPLPromptStates.NoUpdate
93 return "\e]633;D;$(Int(exitcode))\a"
94 end
95 end),
96 update_cmd = si(function (cmd)
97 cmd = sanitize_shell_integration_string(cmd)
98 "\e]633;E;$cmd\a"
99 end),
100 continuation_prompt_start = si(() -> "\e]633;F\a"),
101 continuation_prompt_end = si(() -> "\e]633;G\a"),
102 update_cwd = si(() -> "\e]633;P;Cwd=$(pwd())\a"),
103 windows_compat = si(() -> "\e]633;P;IsWindows=True\a")
104 )
105
106 as_func(x) = () -> x
107 as_func(x::Function) = x
108
109 function install_vscode_shell_integration(prompt)
110 if Sys.iswindows()
111 print(stdout, SHELL.windows_compat())
112 end
113 prefix = as_func(prompt.prompt_prefix)
114 suffix = as_func(prompt.prompt_suffix)
115 prompt.prompt_prefix = () -> string(SHELL.output_end(), SHELL.prompt_start(), prefix())
116 prompt.prompt_suffix = () -> string(suffix(), SHELL.update_cwd(), SHELL.prompt_end())
117
118 on_done = prompt.on_done
119 prompt.on_done = function (mi, buf, ok)
120 print(stdout, SHELL.output_start(), SHELL.update_cmd(String(take!(deepcopy(buf)))))
121 REPL_PROMPT_STATE[] = REPLPromptStates.NoStatus
122 on_done(mi, buf, ok)
123 end
124 end
125
126 if VERSION > v"1.9-"
127 active_module = Base.active_module
128 else
129 active_module() = Main
130 end
131
132 const HAS_REPL_TRANSFORM = Ref{Bool}(false)
133 function hook_repl(repl)
134 if HAS_REPL_TRANSFORM[]
135 return
136 end
137 @debug "installing REPL hook"
138 if !isdefined(repl, :interface)
139 repl.interface = REPL.setup_interface(repl)
140 end
141 main_mode = get_main_mode(repl)
142
143 if VERSION > v"1.5-"
144 for _ = 1:20 # repl backend should be set up after 10s -- fall back to the pre-ast-transform approach otherwise
145 isdefined(Base, :active_repl_backend) && continue
146 sleep(0.5)
147 end
148 if isdefined(Base, :active_repl_backend)
149 push!(Base.active_repl_backend.ast_transforms, ast -> transform_backend(ast, repl, main_mode))
150 HAS_REPL_TRANSFORM[] = true
151 install_vscode_shell_integration(main_mode)
152 @debug "REPL AST transform installed"
153 return
154 end
155 end
156
157 main_mode.on_done = REPL.respond(repl, main_mode; pass_empty = false) do line
158 quote
159 $(evalrepl)($(active_module)(), $line, $repl, $main_mode)
160 end
161 end
162 @debug "legacy REPL hook installed"
163 HAS_REPL_TRANSFORM[] = true
164 return nothing
165 end
166
167 function transform_backend(ast, repl, main_mode)
168 quote
169 $(evalrepl)($(active_module)(), $(QuoteNode(ast)), $repl, $main_mode)
170 end
171 end
172
173 const REPLPromptStates = (
174 NoUpdate = 0,
175 NoStatus = 1,
176 Success = 2,
177 Error = 3,
178 )
179
180 const REPL_PROMPT_STATE = Ref{Int}(REPLPromptStates.NoUpdate)
181 function evalrepl(m, line, repl, main_mode)
182 did_notify = false
183 return try
184 try
185 JSONRPC.send_notification(conn_endpoint[], "repl/starteval", nothing)
186 did_notify = true
187 catch err
188 @debug "Could not send repl/starteval notification" exception = (err, catch_backtrace())
189 end
190 r = run_with_backend() do
191 fix_displays(; is_repl = true)
192 f = () -> repleval(m, line, REPL.repl_filename(repl, main_mode.hist))
193 PROGRESS_ENABLED[] ? Logging.with_logger(f, VSCodeLogger()) : f()
194 end
195 REPL_PROMPT_STATE[] = REPLPromptStates.Error
196 if r isa EvalError
197 display_repl_error(stderr, r.err, r.bt)
198 nothing
199 elseif r isa EvalErrorStack
200 set_error_global(r)
201 display_repl_error(stderr, r)
202 nothing
203 else
204 REPL_PROMPT_STATE[] = REPLPromptStates.Success
205 r
206 end
207 catch err
208 # This is for internal errors only.
209 Base.display_error(stderr, err, catch_backtrace())
210 nothing
211 finally
212 if did_notify
213 try
214 JSONRPC.send_notification(conn_endpoint[], "repl/finisheval", nothing)
215 catch err
216 @debug "Could not send repl/finisheval notification" exception = (err, catch_backtrace())
217 end
218 end
219 end
220 end
221
222 # don't inline this so we can find it in the stacktrace
223 @noinline function repleval(m, code::String, file)
224 args = VERSION >= v"1.5" ? (REPL.softscope, m, code, file) : (m, code, file)
225 return include_string(args...)
226 end
227
228 @noinline function repleval(m, code, _)
229 return Base.eval(m, code)
230 end
231
232 replcontext(io, limitflag) = IOContext(
233 io,
234 :limit => true,
235 :displaysize => get(stdout, :displaysize, (60, 120)),
236 :stacktrace_types_limited => limitflag,
237 )
238
239 # basically the same as Base's `display_error`, with internal frames removed
240 function display_repl_error(io, err, bt)
241 limitflag = Ref(false)
242
243 st = stacktrace(crop_backtrace(bt))
244 printstyled(io, "ERROR: "; bold = true, color = Base.error_color())
245 showerror(replcontext(io, limitflag), err, st)
246 if limitflag[]
247 print(io, "Some type information was truncated. Use `show(err)` to see complete types.")
248 end
249 println(io)
250 end
251
252 function display_repl_error(io, stack::EvalErrorStack)
253 limitflag = Ref(false)
254
255 printstyled(io, "ERROR: "; bold = true, color = Base.error_color())
256 for (i, (err, bt)) in enumerate(reverse(stack.stack))
257 i !== 1 && print(io, "\ncaused by: ")
258 st = stacktrace(crop_backtrace(bt))
259 showerror(replcontext(io, limitflag), i == 1 ? unwrap_loaderror(err) : err, st)
260 println(io)
261 end
262
263 if limitflag[]
264 println(io, "Some type information was truncated. Use `show(err)` to see complete types.")
265 end
266 end
267
268
272 (100 %) samples spent in withpath
272 (100 %) (incl.) when called from #66 line 179
function withpath(f, path)
269 tls = task_local_storage()
270 hassource = haskey(tls, :SOURCE_PATH)
271 hassource && (path′ = tls[:SOURCE_PATH])
272 tls[:SOURCE_PATH] = path
273 try
274 272 (100 %)
272 (100 %) samples spent calling #67
return f()
275 finally
276 hassource ? (tls[:SOURCE_PATH] = path′) : delete!(tls, :SOURCE_PATH)
277 end
278 end