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 # Generic fallback
orgshow(io::IO, Any, i; kwargs...) = show(io, i) 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...) orgshow(io, MIME("text/org"), i; kwargs...)
end 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 # 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) function orgshow(io::IO, ::MIME"text/org", t::NamedTuple)
print(io, join(string.(keys(t)), ',')) print(io, join(string.(keys(t)), ','))
@ -48,6 +57,26 @@ function orgshow(io::IO, ::MIME"text/org",
end end
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() function define_Plots()
# Fallback: we will try to plot any image/png or image/svg # Fallback: we will try to plot any image/png or image/svg
Main.@eval function orgshow(io::IO, Main.@eval function orgshow(io::IO,
@ -65,9 +94,10 @@ function define_Plots()
p::Plots.Plot; kwargs...) p::Plots.Plot; kwargs...)
show(io, MIME("image/png"), plot(p; kwargs...)) show(io, MIME("image/png"), plot(p; kwargs...))
end 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) 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...) orgshow(io, MIME("image/png"), p; kwargs...)
end end
end end
@ -78,7 +108,10 @@ function define_Plots()
end end
end end
Main.@eval function orgshow(io::IO, ::MIME"text/html", p::Plots.Plot; kwargs...) 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 end
Main.@eval function orgshow(io::IO, ::MIME"image/svg+xml", p::Plots.Plot; kwargs...) Main.@eval function orgshow(io::IO, ::MIME"image/svg+xml", p::Plots.Plot; kwargs...)
show(io, MIME("image/svg+xml"), plot(p; kwargs...)) show(io, MIME("image/svg+xml"), plot(p; kwargs...))
@ -116,13 +149,30 @@ function define_DataFrames()
end end
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() function define_NamedArrays()
Main.@eval function orgshow(io::IO, ::MIME"text/org", 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) n = names(na)
a = collect(na) a = collect(na)
print(io, join(string.(na.dimnames), "/") * ',') # The char used by NamedArrays is '╲' but by default it's not
print(io, join(n[2], ','') * '\n') # shown in pdf export
print(io, join(string.(na.dimnames), " \\ ") * ',')
print(io, join(n[2], ',') * '\n')
print(io, join([join([string(n[1][i], ','), print(io, join([join([string(n[1][i], ','),
join([a[i,j] join([a[i,j]
for j in 1:size(na,2) for j in 1:size(na,2)
@ -130,7 +180,7 @@ function define_NamedArrays()
for i in 1:size(na,1) for i in 1:size(na,1)
], '\n')) ], '\n'))
end 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) n = names(na)
a = collect(na) a = collect(na)
print(io, string(na.dimnames[1], ',', '\n')) print(io, string(na.dimnames[1], ',', '\n'))
@ -141,15 +191,16 @@ end
define_package_functions(pkg::Symbol) = (@eval $pkg)() define_package_functions(pkg::Symbol) = (@eval $pkg)()
function OrgBabelImport(_imports; forced = false) function OrgBabelImport(imports; forced = false)
"Load dependencies. Do this before calling OrgBabelReload()" "Load dependencies. Do this before calling OrgBabelReload()"
# Reload this module, so that if new packages have been imported, # Reload this module, so that if new packages have been imported,
# we can use them to save the output # we can use them to save the output
!forced && isempty(_imports) && return !forced && isempty(imports) && return
println("$imports")
try try
Main.eval(Meta.parse( Main.eval(Meta.parse(
"""begin """begin
$_imports $imports
end""")) end"""))
true true
catch e catch e
@ -183,29 +234,30 @@ function OrgBabelFormat(output_type::Symbol,
dir, vars, dir, vars,
src_file, src_file,
silently::Bool, silently::Bool,
pure::Bool,
kwargs) kwargs)
content = read(src_file, String) content = read(src_file, String)
# Fake a prompt with the current input # Fake a prompt with the current input
try try
println(string("\njulia> ", content)) printstyled(IOContext(stdout, :color => true),
"\njulia> ", color = :blue)
println(content)
catch catch
end end
# Dispatch on output type # Dispatch on output type
code = pure ? "let $vars; $content; end" : "begin $vars; $content end"
if output_type == :value if output_type == :value
# Run the code # Run the code
result = cd(dir) do result = cd(dir) do
try try
# Variable assignment Main.eval(Meta.parse(code))
Main.eval(Meta.parse(vars))
# src block evaluation
Main.eval(Meta.parse("begin $content end"))
catch e catch e
e (e, stacktrace())
end end
end end
if !silently if !silently
try try
print(result) display(MIME("text/plain"), result)
catch e catch e
println("Error $e while showing results") println("Error $e while showing results")
end end
@ -236,12 +288,10 @@ function OrgBabelFormat(output_type::Symbol,
redirect_stderr(f) do redirect_stderr(f) do
cd(dir) do cd(dir) do
try try
# Variable assignment Main.eval(Meta.parse(code))
Main.eval(Meta.parse(vars))
# src block evaluation
Main.eval(Meta.parse("begin $content end"))
catch e catch e
println(e) println(e)
println(join(stacktrace(), '\n'))
end end
end end
end end
@ -260,3 +310,4 @@ function OrgBabelFormat(output_type::Symbol,
end end
OrgBabelReload() OrgBabelReload()

View File

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