From 206ed0e9d273d8f24c4b09898c0cffd2c5cd4cbc Mon Sep 17 00:00:00 2001 From: Luis Eduardo Bueso de Barrio Date: Sat, 24 Jan 2026 17:28:00 +0100 Subject: [PATCH] 02-basic-execution.md done --- ob-elixir.el | 88 ++++++++++++++++++++++++++++++++++++++++++ test/test-ob-elixir.el | 36 +++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/ob-elixir.el b/ob-elixir.el index 9343c4f..23fdc5e 100644 --- a/ob-elixir.el +++ b/ob-elixir.el @@ -37,5 +37,93 @@ (require 'ob) (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) ;;; ob-elixir.el ends here diff --git a/test/test-ob-elixir.el b/test/test-ob-elixir.el index e9c2635..ca52467 100644 --- a/test/test-ob-elixir.el +++ b/test/test-ob-elixir.el @@ -18,5 +18,41 @@ "Test that the package loads successfully." (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) ;;; test-ob-elixir.el ends here