Add :let keyword, more supported packages

master
nixo 2020-02-16 12:48:04 +01:00
parent e86200172c
commit e789251ce9
3 changed files with 130 additions and 65 deletions

97
init.jl
View File

@ -1,4 +1,4 @@
const supported_packages = [:DataFrames, :NamedArrays, :Plots]
const supported_packages = [:DataFrames, :NamedArrays, :Plots, :HypothesisTests, :CSVFiles]
# Generic fallback
orgshow(io::IO, Any, i; kwargs...) = show(io, i)
@ -27,8 +27,17 @@ function orgshow(io::IO, ::MIME"text/csv", i::Array{T,2}; kwargs...) where T <:
orgshow(io, MIME("text/org"), i; kwargs...)
end
function orgstring(e::Tuple{Exception,Any})
string("ERROR,", e[1], "\n",
"Stacktrace:\n",
join(e[2], '\n'))
end
# The comma is needed to allow export as table
orgshow(io::IO, ::MIME"text/org", e::Exception; kwargs...) = print(io, "ERROR,", e)
function orgshow(io::IO, ::MIME"text/org", e::Tuple{Exception,Any};
kwargs...)
print(io, orgstring(e))
end
function orgshow(io::IO, ::MIME"text/org", t::NamedTuple)
print(io, join(string.(keys(t)), ','))
@ -48,6 +57,26 @@ function orgshow(io::IO, ::MIME"text/org",
end
end
OrgAPApvalue(p) = p < 0.001 ? "< .001" : string("= ", round(p, sigdigits = 2))
function define_HypothesisTests()
Main.@eval function orgshow(io::IO,
m::MIME"text/org",
test::PowerDivergenceTest; kwargs...)
println(io, join(["test", "df", "N", "stat", "p-value"], ','))
print(io, join(["X^2", test.df, test.n, test.stat, pvalue(test)], ','))
end
Main.@eval function orgshow(io::IO,
m::MIME"text/html",
test::PowerDivergenceTest; kwargs...)
print(io, string("X^2",
" (", test.df, ", N = ", test.n,
") = ",
round(test.stat; digits = 2),
", p ", OrgAPApvalue(pvalue(test))))
end
end
function define_Plots()
# Fallback: we will try to plot any image/png or image/svg
Main.@eval function orgshow(io::IO,
@ -65,9 +94,10 @@ function define_Plots()
p::Plots.Plot; kwargs...)
show(io, MIME("image/png"), plot(p; kwargs...))
end
Main.@eval function orgshow(io::IO, ::MIME"image/png", e::Exception; kwargs...)
Main.@eval function orgshow(io::IO, ::MIME"image/png", e::Tuple{Exception,Any};
kwargs...)
let p = plot(showaxis = false, grid = false, bg = :yellow)
annotate!([0.5], [0.5], (string("ERROR: ", e), :red))
annotate!([0.5], [0.5], (orgstring(e), :red))
orgshow(io, MIME("image/png"), p; kwargs...)
end
end
@ -78,7 +108,10 @@ function define_Plots()
end
end
Main.@eval function orgshow(io::IO, ::MIME"text/html", p::Plots.Plot; kwargs...)
Plots._show(io, MIME("text/html"), plot(p; kwargs...))
p = plot(p; kwargs...)
p.attr[:html_output_format] = "png"
# Plots._show(io::IO, MIME("text/html"), p::Plots.Plot; kwargs...)
Plots._show(io, MIME("text/html"), p)
end
Main.@eval function orgshow(io::IO, ::MIME"image/svg+xml", p::Plots.Plot; kwargs...)
show(io, MIME("image/svg+xml"), plot(p; kwargs...))
@ -116,13 +149,30 @@ function define_DataFrames()
end
end
function define_CSVFiles()
Main.@eval function orgshow(io::IO, ::MIME"text/csv", d::CSVFiles.CSVFile)
orgshow(io, MIME("text/org"), d)
end
Main.@eval function orgshow(io::IO, ::MIME"text/html", d::CSVFiles.CSVFile)
show(io, MIME("text/html"), RES)
end
Main.@eval function orgshow(io::IO, ::MIME"application/json", d::CSVFiles.CSVFile)
show(io, MIME("application/vnd.dataresource+json"), RES)
end
Main.@eval function orgshow(io::IO, ::MIME"text/org", d::CSVFiles.CSVFile)
orgshow(io, MIME("text/org"), collect(d))
end
end
function define_NamedArrays()
Main.@eval function orgshow(io::IO, ::MIME"text/org",
na::NamedArrays.NamedArray{T,2} where T <: Any)
na::NamedArray{T,2} where T <: Any)
n = names(na)
a = collect(na)
print(io, join(string.(na.dimnames), "/") * ',')
print(io, join(n[2], ','') * '\n')
# The char used by NamedArrays is '╲' but by default it's not
# shown in pdf export
print(io, join(string.(na.dimnames), " \\ ") * ',')
print(io, join(n[2], ',') * '\n')
print(io, join([join([string(n[1][i], ','),
join([a[i,j]
for j in 1:size(na,2)
@ -130,7 +180,7 @@ function define_NamedArrays()
for i in 1:size(na,1)
], '\n'))
end
Main.@eval function orgshow(io::IO, ::MIME"text/org", na::NamedArrays.NamedArray{T,1} where T <: Any)
Main.@eval function orgshow(io::IO, ::MIME"text/org", na::NamedArray{T,1} where T <: Any)
n = names(na)
a = collect(na)
print(io, string(na.dimnames[1], ',', '\n'))
@ -141,15 +191,16 @@ end
define_package_functions(pkg::Symbol) = (@eval $pkg)()
function OrgBabelImport(_imports; forced = false)
function OrgBabelImport(imports; forced = false)
"Load dependencies. Do this before calling OrgBabelReload()"
# Reload this module, so that if new packages have been imported,
# we can use them to save the output
!forced && isempty(_imports) && return
!forced && isempty(imports) && return
println("$imports")
try
Main.eval(Meta.parse(
"""begin
$_imports
$imports
end"""))
true
catch e
@ -183,29 +234,30 @@ function OrgBabelFormat(output_type::Symbol,
dir, vars,
src_file,
silently::Bool,
pure::Bool,
kwargs)
content = read(src_file, String)
# Fake a prompt with the current input
try
println(string("\njulia> ", content))
printstyled(IOContext(stdout, :color => true),
"\njulia> ", color = :blue)
println(content)
catch
end
# Dispatch on output type
code = pure ? "let $vars; $content; end" : "begin $vars; $content end"
if output_type == :value
# Run the code
result = cd(dir) do
try
# Variable assignment
Main.eval(Meta.parse(vars))
# src block evaluation
Main.eval(Meta.parse("begin $content end"))
Main.eval(Meta.parse(code))
catch e
e
(e, stacktrace())
end
end
if !silently
try
print(result)
display(MIME("text/plain"), result)
catch e
println("Error $e while showing results")
end
@ -236,12 +288,10 @@ function OrgBabelFormat(output_type::Symbol,
redirect_stderr(f) do
cd(dir) do
try
# Variable assignment
Main.eval(Meta.parse(vars))
# src block evaluation
Main.eval(Meta.parse("begin $content end"))
Main.eval(Meta.parse(code))
catch e
println(e)
println(join(stacktrace(), '\n'))
end
end
end
@ -260,3 +310,4 @@ function OrgBabelFormat(output_type::Symbol,
end
OrgBabelReload()

View File

@ -12,7 +12,7 @@
:version "24.1"
:type 'string)
(defcustom org-babel-julia-startup-script
(defcustom ob-julia-startup-script
(concat (file-name-directory (or load-file-name (buffer-file-name)))
"init.jl")
"Julia file path to run at startup. Must be absolute."
@ -52,6 +52,7 @@ There's no effect in non-session evaluations"
(height . :any)
(size . :any)
(inline . :any)
(let . :any)
(import . :any)
(using . :any)
(async . :any)
@ -81,7 +82,14 @@ There's no effect in non-session evaluations"
(set (make-local-variable 'org-babel-julia-session-directory) dir)
(save-window-excursion
(require 'ess)
(julia)
;; load the julia startup script (defined in ob-julia-startup-script)
;; pass it along with other arguments defined in inferior-julia-args
(let* ((start-script-arg
(concat (format "--load=%s" ob-julia-startup-script)))
(inferior-julia-args (if inferior-julia-args
(concat inferior-julia-args start-script-arg)
start-script-arg)))
(julia))
(rename-buffer
(if (bufferp session)
(buffer-name session)
@ -92,14 +100,7 @@ There's no effect in non-session evaluations"
;; running the command
(set-process-filter (get-buffer-process
(org-babel-comint-buffer-livep session))
'org-julia-async-process-filter)
;; Initialization
(let ((julia-init
(with-temp-buffer
(insert-file-contents org-babel-julia-startup-script)
(buffer-string))))
(ess-send-string (ess-get-process) julia-init nil))
(current-buffer))))
'org-julia-async-process-filter))))
(defun org-babel-julia-get-session-name (params)
"Extract the session name from the PARAMS.
@ -153,8 +154,7 @@ This function is used for all async processing with and without session."
;; This will evaluate the code again
;; (cl-callf org-babel-process-params (nth 2 info))
(setq params (nth 2 info))
(setq cache (let ((c (cdr (assq :cache params))))
(and c (string= "yes" c))))
(setq cache (ob-julia-check-trueness params :cache))
;; pass info to have a different hash
(setq new-hash (if cache (org-babel-sha1-hash) nil))
(org-babel-remove-result)
@ -181,14 +181,14 @@ Does not rely on an ESS session."
(make-process :name "*julia-async-process*"
:filter #'org-julia-async-process-filter
:command `(,org-babel-julia-command
"--load" ,org-babel-julia-startup-script
"--load" ,ob-julia-startup-script
"--eval"
,(format "include(%S);%s" tmpfile command)))
(concat "julia-async:" uuid ":" outfile))
(progn
(shell-command
(format "%s --load %s %s" org-babel-julia-command
org-babel-julia-startup-script tmpfile))
ob-julia-startup-script tmpfile))
outfile))))
(defun org-babel-julia-assign-to-var-or-array (var)
@ -201,7 +201,7 @@ Does not rely on an ESS session."
"Create a Matrix (Vector{Any,2} from `MATRIX' and assign it to `NAME'"
(format "%s = [%s]" name
(mapconcat (lambda (line) (mapconcat (lambda (e)
(format "%s" e))
(format "%S" e))
line " ")) matrix ";")))
(defun org-babel-julia-assign-to-var (name value)
@ -290,19 +290,22 @@ else OUTFILE is used, and data is _write()_ to it."
(format "OrgBabelImport(%S);OrgBabelReload();"
(concat (if using (mapconcat (lambda (x) (concat "using " x))
using "\n") "")
"\n"
(if import (mapconcat (lambda (x) (concat "import " x))
import "\n") "")))
"")
(format
"OrgBabelFormat(%s,%S,%S,%S,%S,%s,%S);"
output-type outfile
dir
(mapconcat 'concat vars ";") srcfile
(if org-babel-julia-silent-repl
"true" "false")
(org-babel-julia-make-kwargs `((width . ,width)
(height . ,height)
(size . ,size)))))))
"OrgBabelFormat(%s,%S,%S,%S,%S,%s,%s,%S);"
output-type outfile
dir
(mapconcat 'concat vars ";") srcfile
(if org-babel-julia-silent-repl
"true" "false")
(if (ob-julia-check-trueness params :let)
"true" "false")
(org-babel-julia-make-kwargs `((width . ,width)
(height . ,height)
(size . ,size)))))))
(defun org-babel-execute:julia-async (buffer session body block output params)
(let* ((uuid (org-id-uuid))
@ -361,14 +364,14 @@ If PARAMS is :async, insert a link, unless CALLBACK is true."
(if inlined
(with-temp-buffer
(when (bound-and-true-p org-export-current-backend)
(insert (format "@@%s:"
(insert (format "#+begin_export %s\n"
(if org-export-current-backend
org-export-current-backend
inlined))))
(insert-file-contents results)
(when (bound-and-true-p org-export-current-backend)
(goto-char (point-max))
(insert "@@"))
(insert "\n#+end_export"))
(buffer-string))
(org-babel-result-cond (if res (split-string res) nil)
(with-temp-buffer
@ -391,27 +394,25 @@ table. To force a matrix, use matrix"
((member "raw" results) 'raw)
(t 'auto))))
(defun ob-julia-check-trueness (params param)
""
(and (assoc param params)
(let ((val (cdr (assoc param params))))
(or
(not val)
(string= "t" val)
(string= "yes" val)))))
(defun org-babel-julia-async-p (params)
"Check whether the session should be async or not."
(let* ((res (cdr (assoc :results params)))
(async (assoc :async params)))
(and async
(or
(not (cdr async))
(string= "t" (cdr async))
(string= "yes" (cdr async)))
(ob-julia-check-trueness params :async)
(and (eq org-babel-current-src-block-location (org-babel-where-is-src-block-head)))
(not (and res (stringp res) (member "silent" (split-string res)))))))
(defun org-babel-julia-really-async-p ()
;; (let*
;; ((head (org-babel-where-is-src-block-head))
;; (async (and (not (bound-and-true-p org-export-current-backend))
;; head
;; org-babel-current-src-block-location
;; (equal org-babel-current-src-block-location
;; head))))
;; async)
;; Disable async on export
(not (bound-and-true-p org-export-current-backend)))
;; Copied from ob-python
@ -442,7 +443,6 @@ This function is called by `org-babel-execute-src-block'.
BODY is the content of the src block
PARAMS are the parameter passed to the block"
;; org-babel-current-src-block-location ; this variable does not work >.<
(message (format "body: %s, params: %s" body params))
(save-excursion
(let* ((buffer (buffer-name))
(session (org-babel-julia-get-session-name params))

View File

@ -19,7 +19,10 @@ See [[Implemented features]] for more details.
(customized by =org-babel-julia-default-session=)
3. If session has a values, use it's name
3. Check if we want to use a session or not. Check if the session
exists. Start the session accordingly.
exists. Start the session accordingly. During startup, the file
defined in =ob-julia-startup-script= is loaded. This file must define
functions like OrgBabelFormat, to let emacs send code to
execute. See =init.jl= for details.
4. Is the evaluation async?
1. YES:
1. Register a filter function
@ -100,7 +103,18 @@ sleep(1)
Asynchronous evaluation is automatically disabled on export, or when a
code block depends on one (=:var=)
** Variables input (=:var=), Standard output
** Variables input (=:var =:let=), Standard output
As usual, you can set variables with the =:var= header. To make ob-julia
behave like the julia REPL, variables are set in the global scope. If
you want a block to be isolated, you can use the extra =:let= header
argument: with this, the src block is inside a let block:
#+begin_src julia :exports code :eval never
let vars
src_block
end
#+end_src
*** Inputs