I’m using Jupyter notebooks in VSCode to do computations that I want to include in a word processor (LyX for now).
Generating plots is no problem: I just save them to file, and link the plots into the word processor.
Question: is there a clean way to save the content of a code cell to file, so that I can link it into a computer listing (of, say LyX)?
MS Copilot suggests to define a macro, say:
macro save_and_run(path, block)
# Write the code to a file
open(path, "w") do io
print(io, block)
end
# Evaluate the code in the caller's scope
return esc(block)
end
and place the code within a macro statement:
@save_and_run "cell1.jl" begin
x = 1:10
y = sum(x)
println("Sum is $y")
end
I have zero experience with writing macros. And for all I know, this functionality already exists?? Suggestions are appreciated.
NBInclude.jl has an nbexport function to convert a notebook to plain text. Notebook files are just JSON, so you can also use any JSON package like JSON.jl to extract whatever you want.
You can also use In[n] in IJulia (including via vscode, if you are using the actual Jupyter kernel instead of vscode’s partial re-implementation) to access the contents of any input code cell as a string.
I typically only want to save specific code segments in a notebook
I may re-arrange the order of content in a notebook (including adding markup code, etc.). This makes it challenging to auto-import code into, say LyX computer listing, because I may change the location of the code that I want to import.
After some back and forth with MS Copilot, it (=MSCopilot) came up with the following macro:
"""
@save_run path_expr begin
code...
end
Write the contents of the block to the file given by `path_expr`,
preserving user comments but removing Jupyter's line-number annotations.
Then evaluate the statements in the caller's scope.
"""
macro save_run(path_expr, block)
@assert block.head === :block "Second argument must be a `begin ... end` block."
# Evaluate the path expression in the caller's module
path = Core.eval(__module__, path_expr)
# --- Remove only LineNumberNodes, preserve comments ---
cleaned_stmts = filter(stmt -> !(stmt isa LineNumberNode), block.args)
# --- Write each cleaned statement to the file ---
open(path, "w") do io
for stmt in cleaned_stmts
print(io, stmt, "\n")
end
end
# --- Evaluate the cleaned statements in the caller's scope ---
return Expr(:block, (esc(stmt) for stmt in cleaned_stmts)...)
end
Here is an example of running it:
using Plots
listpath = "..."
listtype = ".jl"
@save_run listpath*"test"*listtype begin
# Testing comments
x = range(0,2pi,length=100)
# Plotting code
plot(x,sin.(x))
end
The code runs in JupyterNotebook as it should. But the implementation is not perfect…
My comments in the code are stripped off in the produced file
MS Copilot suggests that indentation I use in code, functions, etc. may be removed
However, MS Copilot suggests that the macro should work in VSCode itself, in Pluto, etc.