Skip to content

Commit 4fd955a

Browse files
authored
support repl_ast_transforms in Julia 1.5 (#947)
1 parent 4bfda80 commit 4fd955a

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-4
lines changed

docs/src/library/internals.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ IJulia.init
1010

1111
## Cell execution hooks
1212

13+
These functions can be used to modify the behavior of IJulia
14+
by executing custom code before or after cells are executed
15+
(or errors are handled).
16+
17+
In Julia 1.5 or later, you can *also* add a transformation
18+
function to `REPL.repl_ast_transforms` which takes every parsed
19+
expression and transforms it to another expression. These
20+
transformations are [used in the Julia REPL](https://github.com/JuliaLang/julia/issues/37047) (technically, they are the deaults for `Base.active_repl_backend.ast_transforms` in new REPL instances),
21+
and are also executed by IJulia on each parsed code-cell expression.
22+
1323
```@docs
1424
IJulia.pop_posterror_hook
1525
IJulia.pop_postexecute_hook
@@ -19,7 +29,6 @@ IJulia.push_postexecute_hook
1929
IJulia.push_preexecute_hook
2030
```
2131

22-
2332
## Messaging
2433

2534
```@docs

docs/src/manual/usage.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ It defaults to `Main`.
122122

123123
By default, IJulia evaluates user code using "soft" global scope, via the [SoftGlobalScope.jl package](https://github.com/stevengj/SoftGlobalScope.jl): this means that you don't need explicit `global` declarations to modify global variables in `for` loops and similar, which is convenient for interactive use.
124124

125-
To opt out of this behavior, making notebooks behave similarly to global code in Julia `.jl` files,
125+
To opt out of this behavior, making notebooks behave similarly to global code in Julia `.jl` files or the REPL in Julia 1.0…1.4,
126126
you can set `IJulia.SOFTSCOPE[] = false` at runtime, or include the environment variable `IJULIA_SOFTSCOPE=no`
127127
environment of the IJulia kernel when it is launched.
128+
129+
In Julia 1.5 or later, [soft-scope became the default
130+
in the Julia REPL](https://julialang.org/blog/2020/08/julia-1.5-highlights/#the_return_of_quotsoft_scopequot_in_the_repl), and
131+
IJulia once again matches the REPL behavior. The `IJULIA_SOFTSCOPE`
132+
and `IJulia.SOFTSCOPE[]` can still be used to disable soft scope
133+
in IJulia, however. More generally, the soft scoping can
134+
be disabled in both the REPL and in IJulia by removing the
135+
`REPL.softscope` function from the variable `REPL.repl_ast_transforms`
136+
in your [`~/.julia/config/startup.jl`](https://docs.julialang.org/en/v1/manual/getting-started/) file.

src/execute_request.jl

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,30 @@ execute_msg = Msg(["julia"], Dict("username"=>"jlkernel", "session"=>uuid4()), D
1111
# request
1212
const stdio_bytes = Ref(0)
1313

14+
import REPL
1415
import REPL: helpmode
1516

1617
# use a global array to accumulate "payloads" for the execute_reply message
1718
const execute_payloads = Dict[]
1819

20+
function execute_code(code::AbstractString, filename::AbstractString)
21+
@static if !isdefined(REPL, :repl_ast_transforms)
22+
return SOFTSCOPE[] ? softscope_include_string(current_module[], code, "In[$n]") :
23+
include_string(current_module[], code, "In[$n]")
24+
else
25+
# use the default REPL ast transformations in Julia 1.5,
26+
# which include the soft-scope transformation.
27+
include_string(current_module[], code, "In[$n]") do ast
28+
for xf in REPL.repl_ast_transforms
29+
if xf !== REPL.softscope || SOFTSCOPE[]
30+
ast = Base.invokelatest(xf, ast)
31+
end
32+
end
33+
ast
34+
end
35+
end
36+
end
37+
1938
function execute_request(socket, msg)
2039
code = msg.content["code"]
2140
@vprintln("EXECUTING ", code)
@@ -65,8 +84,7 @@ function execute_request(socket, msg)
6584
else
6685
#run the code!
6786
occursin(magics_regex, code) && match(magics_regex, code).offset == 1 ? magics_help(code) :
68-
SOFTSCOPE[] ? softscope_include_string(current_module[], code, "In[$n]") :
69-
include_string(current_module[], code, "In[$n]")
87+
execute_code(code, "In[$n]")
7088
end
7189

7290
if silent

0 commit comments

Comments
 (0)