02-basic-execution.md done
This commit is contained in:
88
ob-elixir.el
88
ob-elixir.el
@@ -37,5 +37,93 @@
|
|||||||
(require 'ob)
|
(require 'ob)
|
||||||
(require 'ob-eval)
|
(require 'ob-eval)
|
||||||
|
|
||||||
|
;;; Customization
|
||||||
|
|
||||||
|
(defgroup ob-elixir nil
|
||||||
|
"Org Babel support for Elixir."
|
||||||
|
:group 'org-babel
|
||||||
|
:prefix "ob-elixir-")
|
||||||
|
|
||||||
|
(defcustom ob-elixir-command "elixir"
|
||||||
|
"Command to execute Elixir code.
|
||||||
|
Can be a full path or command name if in PATH."
|
||||||
|
:type 'string
|
||||||
|
:group 'ob-elixir
|
||||||
|
:safe #'stringp)
|
||||||
|
|
||||||
|
;;; Header Arguments
|
||||||
|
|
||||||
|
(defvar org-babel-default-header-args:elixir
|
||||||
|
'((:results . "value")
|
||||||
|
(:session . "none"))
|
||||||
|
"Default header arguments for Elixir code blocks.")
|
||||||
|
|
||||||
|
;;; Language Registration
|
||||||
|
|
||||||
|
;; File extension for tangling
|
||||||
|
(add-to-list 'org-babel-tangle-lang-exts '("elixir" . "ex"))
|
||||||
|
|
||||||
|
;; Associate with elixir-mode for syntax highlighting (if available)
|
||||||
|
(with-eval-after-load 'org-src
|
||||||
|
(add-to-list 'org-src-lang-modes '("elixir" . elixir)))
|
||||||
|
|
||||||
|
;;; Execution
|
||||||
|
|
||||||
|
(defconst ob-elixir--value-wrapper
|
||||||
|
"result = (
|
||||||
|
%s
|
||||||
|
)
|
||||||
|
IO.puts(inspect(result, limit: :infinity, printable_limit: :infinity, charlists: :as_lists))
|
||||||
|
"
|
||||||
|
"Wrapper template for capturing Elixir expression value.
|
||||||
|
%s is replaced with the user's code.")
|
||||||
|
|
||||||
|
(defun ob-elixir--wrap-for-value (body)
|
||||||
|
"Wrap BODY to capture its return value.
|
||||||
|
|
||||||
|
The wrapper evaluates BODY, then prints the result using
|
||||||
|
`inspect/2` with infinite limits to avoid truncation."
|
||||||
|
(format ob-elixir--value-wrapper body))
|
||||||
|
|
||||||
|
(defun ob-elixir--execute (body result-type)
|
||||||
|
"Execute BODY as Elixir code.
|
||||||
|
|
||||||
|
RESULT-TYPE is either `value' or `output'.
|
||||||
|
For `value', wraps code to capture return value.
|
||||||
|
For `output', captures stdout directly.
|
||||||
|
|
||||||
|
Returns the result as a string."
|
||||||
|
(let* ((tmp-file (org-babel-temp-file "ob-elixir-" ".exs"))
|
||||||
|
(code (if (eq result-type 'value)
|
||||||
|
(ob-elixir--wrap-for-value body)
|
||||||
|
body)))
|
||||||
|
(with-temp-file tmp-file
|
||||||
|
(insert code))
|
||||||
|
(let ((result (org-babel-eval
|
||||||
|
(format "%s %s"
|
||||||
|
ob-elixir-command
|
||||||
|
(org-babel-process-file-name tmp-file))
|
||||||
|
"")))
|
||||||
|
(string-trim result))))
|
||||||
|
|
||||||
|
(defun org-babel-execute:elixir (body params)
|
||||||
|
"Execute a block of Elixir code with org-babel.
|
||||||
|
|
||||||
|
BODY is the Elixir code to execute.
|
||||||
|
PARAMS is an alist of header arguments.
|
||||||
|
|
||||||
|
This function is called by `org-babel-execute-src-block'."
|
||||||
|
(let* ((result-type (cdr (assq :result-type params)))
|
||||||
|
(result-params (cdr (assq :result-params params)))
|
||||||
|
(result (ob-elixir--execute body result-type)))
|
||||||
|
(org-babel-reassemble-table
|
||||||
|
(org-babel-result-cond result-params
|
||||||
|
result
|
||||||
|
(org-babel-script-escape result))
|
||||||
|
(org-babel-pick-name (cdr (assq :colname-names params))
|
||||||
|
(cdr (assq :colnames params)))
|
||||||
|
(org-babel-pick-name (cdr (assq :rowname-names params))
|
||||||
|
(cdr (assq :rownames params))))))
|
||||||
|
|
||||||
(provide 'ob-elixir)
|
(provide 'ob-elixir)
|
||||||
;;; ob-elixir.el ends here
|
;;; ob-elixir.el ends here
|
||||||
|
|||||||
@@ -18,5 +18,41 @@
|
|||||||
"Test that the package loads successfully."
|
"Test that the package loads successfully."
|
||||||
(should (featurep 'ob-elixir)))
|
(should (featurep 'ob-elixir)))
|
||||||
|
|
||||||
|
(ert-deftest ob-elixir-test-elixir-available ()
|
||||||
|
"Test that Elixir is available."
|
||||||
|
(should (executable-find ob-elixir-command)))
|
||||||
|
|
||||||
|
(ert-deftest ob-elixir-test-simple-value ()
|
||||||
|
"Test simple value evaluation."
|
||||||
|
(skip-unless (executable-find ob-elixir-command))
|
||||||
|
(let ((result (ob-elixir--execute "1 + 1" 'value)))
|
||||||
|
(should (equal "2" result))))
|
||||||
|
|
||||||
|
(ert-deftest ob-elixir-test-simple-output ()
|
||||||
|
"Test simple output evaluation."
|
||||||
|
(skip-unless (executable-find ob-elixir-command))
|
||||||
|
(let ((result (ob-elixir--execute "IO.puts(\"hello\")" 'output)))
|
||||||
|
(should (equal "hello" result))))
|
||||||
|
|
||||||
|
(ert-deftest ob-elixir-test-multiline-value ()
|
||||||
|
"Test multiline code value evaluation."
|
||||||
|
(skip-unless (executable-find ob-elixir-command))
|
||||||
|
(let ((result (ob-elixir--execute "x = 10\ny = 20\nx + y" 'value)))
|
||||||
|
(should (equal "30" result))))
|
||||||
|
|
||||||
|
(ert-deftest ob-elixir-test-list-result ()
|
||||||
|
"Test list result."
|
||||||
|
(skip-unless (executable-find ob-elixir-command))
|
||||||
|
(let ((result (ob-elixir--execute "[1, 2, 3]" 'value)))
|
||||||
|
(should (equal "[1, 2, 3]" result))))
|
||||||
|
|
||||||
|
(ert-deftest ob-elixir-test-map-result ()
|
||||||
|
"Test map result."
|
||||||
|
(skip-unless (executable-find ob-elixir-command))
|
||||||
|
(let ((result (ob-elixir--execute "%{a: 1, b: 2}" 'value)))
|
||||||
|
;; Maps are unordered in Elixir, so accept either order
|
||||||
|
(should (or (string-match-p "%{a: 1, b: 2}" result)
|
||||||
|
(string-match-p "%{b: 2, a: 1}" result)))))
|
||||||
|
|
||||||
(provide 'test-ob-elixir)
|
(provide 'test-ob-elixir)
|
||||||
;;; test-ob-elixir.el ends here
|
;;; test-ob-elixir.el ends here
|
||||||
|
|||||||
Reference in New Issue
Block a user