748 lines
21 KiB
Markdown
748 lines
21 KiB
Markdown
# Org Babel Language Implementation Guide
|
|
|
|
This document provides a comprehensive guide to implementing org-babel support for a new programming language, specifically targeting Elixir.
|
|
|
|
## Table of Contents
|
|
|
|
- [Architecture Overview](#architecture-overview)
|
|
- [Required Components](#required-components)
|
|
- [Optional Components](#optional-components)
|
|
- [Header Arguments](#header-arguments)
|
|
- [Result Handling](#result-handling)
|
|
- [Session Management](#session-management)
|
|
- [Variable Handling](#variable-handling)
|
|
- [Utility Functions](#utility-functions)
|
|
- [Complete Implementation Template](#complete-implementation-template)
|
|
- [References](#references)
|
|
|
|
---
|
|
|
|
## Architecture Overview
|
|
|
|
### How Org Babel Executes Code
|
|
|
|
```
|
|
User presses C-c C-c on source block
|
|
│
|
|
▼
|
|
┌─────────────────────────────┐
|
|
│ org-babel-execute-src-block │
|
|
└─────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────┐
|
|
│ Parse header arguments │
|
|
│ Get block body │
|
|
└─────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────┐
|
|
│ org-babel-execute:LANG │ ◄── Your implementation
|
|
│ (language-specific) │
|
|
└─────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────┐
|
|
│ Process and format results │
|
|
└─────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────┐
|
|
│ Insert results in buffer │
|
|
└─────────────────────────────┘
|
|
```
|
|
|
|
### Source Block Structure
|
|
|
|
```org
|
|
#+NAME: block-name
|
|
#+HEADER: :var x=5
|
|
#+BEGIN_SRC elixir :results value :session my-session
|
|
# This is the body
|
|
x * 2
|
|
#+END_SRC
|
|
|
|
#+RESULTS: block-name
|
|
: 10
|
|
```
|
|
|
|
### Key Data Structures
|
|
|
|
**params alist** - Association list passed to execute function:
|
|
|
|
```elisp
|
|
((:results . "value")
|
|
(:session . "my-session")
|
|
(:var . ("x" . 5))
|
|
(:var . ("y" . 10))
|
|
(:colnames . no)
|
|
(:rownames . no)
|
|
(:result-params . ("value" "replace"))
|
|
(:result-type . value)
|
|
...)
|
|
```
|
|
|
|
---
|
|
|
|
## Required Components
|
|
|
|
### 1. The Execute Function
|
|
|
|
This is the **only strictly required** function. It must be named exactly `org-babel-execute:LANG`:
|
|
|
|
```elisp
|
|
(defun org-babel-execute:elixir (body params)
|
|
"Execute a block of Elixir code with org-babel.
|
|
|
|
BODY is the code to execute.
|
|
PARAMS is an alist of header arguments.
|
|
|
|
This function is called by `org-babel-execute-src-block'."
|
|
(let* ((session (cdr (assq :session params)))
|
|
(result-type (cdr (assq :result-type params)))
|
|
(result-params (cdr (assq :result-params params)))
|
|
(full-body (org-babel-expand-body:generic
|
|
body params
|
|
(org-babel-variable-assignments:elixir params))))
|
|
;; Execute and return results
|
|
(org-babel-reassemble-table
|
|
(org-babel-result-cond result-params
|
|
;; For :results output - return raw output
|
|
(ob-elixir--execute full-body session)
|
|
;; For :results value - parse as elisp data
|
|
(ob-elixir--table-or-string
|
|
(ob-elixir--execute full-body session)))
|
|
(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))))))
|
|
```
|
|
|
|
### 2. Default Header Arguments
|
|
|
|
Define language-specific defaults:
|
|
|
|
```elisp
|
|
(defvar org-babel-default-header-args:elixir
|
|
'((:results . "value")
|
|
(:session . "none"))
|
|
"Default header arguments for Elixir code blocks.")
|
|
```
|
|
|
|
### 3. Language Registration
|
|
|
|
Enable the language for evaluation:
|
|
|
|
```elisp
|
|
;; Add to tangle extensions (for org-babel-tangle)
|
|
(add-to-list 'org-babel-tangle-lang-exts '("elixir" . "ex"))
|
|
|
|
;; Add to language mode mapping (for syntax highlighting)
|
|
(add-to-list 'org-src-lang-modes '("elixir" . elixir))
|
|
```
|
|
|
|
---
|
|
|
|
## Optional Components
|
|
|
|
### Language-Specific Header Arguments
|
|
|
|
Define what header arguments your language supports:
|
|
|
|
```elisp
|
|
(defconst org-babel-header-args:elixir
|
|
'((mix-project . :any) ; Path to mix project
|
|
(mix-env . :any) ; MIX_ENV value
|
|
(iex-args . :any) ; Extra args for iex
|
|
(timeout . :any) ; Execution timeout
|
|
(async . ((yes no)))) ; Async execution
|
|
"Elixir-specific header arguments.")
|
|
```
|
|
|
|
### Initiate Session Function
|
|
|
|
For persistent REPL sessions:
|
|
|
|
```elisp
|
|
(defun org-babel-elixir-initiate-session (&optional session params)
|
|
"Create or return an Elixir session buffer.
|
|
|
|
SESSION is the session name (string or nil).
|
|
PARAMS are the header arguments.
|
|
|
|
Returns the session buffer, or nil if SESSION is \"none\"."
|
|
(unless (string= session "none")
|
|
(let ((session-name (or session "default")))
|
|
(ob-elixir--get-or-create-session session-name params))))
|
|
```
|
|
|
|
### Prep Session Function
|
|
|
|
Prepare a session with variables before execution:
|
|
|
|
```elisp
|
|
(defun org-babel-prep-session:elixir (session params)
|
|
"Prepare SESSION according to PARAMS.
|
|
|
|
Injects variables into the session before code execution."
|
|
(let ((session-buffer (org-babel-elixir-initiate-session session params))
|
|
(var-lines (org-babel-variable-assignments:elixir params)))
|
|
(when session-buffer
|
|
(org-babel-comint-in-buffer session-buffer
|
|
(dolist (var-line var-lines)
|
|
(insert var-line)
|
|
(comint-send-input nil t)
|
|
(org-babel-comint-wait-for-output session-buffer))))
|
|
session-buffer))
|
|
```
|
|
|
|
### Load Session Function
|
|
|
|
Load code into session without executing:
|
|
|
|
```elisp
|
|
(defun org-babel-load-session:elixir (session body params)
|
|
"Load BODY into SESSION without executing.
|
|
|
|
Useful for setting up definitions to be used later."
|
|
(save-window-excursion
|
|
(let ((buffer (org-babel-prep-session:elixir session params)))
|
|
(with-current-buffer buffer
|
|
(goto-char (process-mark (get-buffer-process buffer)))
|
|
(insert (org-babel-chomp body)))
|
|
buffer)))
|
|
```
|
|
|
|
### Variable Assignments Function
|
|
|
|
Convert Elisp values to language-specific variable assignments:
|
|
|
|
```elisp
|
|
(defun org-babel-variable-assignments:elixir (params)
|
|
"Return list of Elixir statements to assign variables from PARAMS."
|
|
(mapcar
|
|
(lambda (pair)
|
|
(format "%s = %s"
|
|
(car pair)
|
|
(ob-elixir--elisp-to-elixir (cdr pair))))
|
|
(org-babel--get-vars params)))
|
|
```
|
|
|
|
### Expand Body Function
|
|
|
|
Customize how the code body is expanded (optional, generic works for most):
|
|
|
|
```elisp
|
|
(defun org-babel-expand-body:elixir (body params)
|
|
"Expand BODY according to PARAMS.
|
|
|
|
Add variable bindings, prologue, epilogue."
|
|
(let ((vars (org-babel-variable-assignments:elixir params))
|
|
(prologue (cdr (assq :prologue params)))
|
|
(epilogue (cdr (assq :epilogue params))))
|
|
(concat
|
|
(when prologue (concat prologue "\n"))
|
|
(mapconcat #'identity vars "\n")
|
|
(when vars "\n")
|
|
body
|
|
(when epilogue (concat "\n" epilogue)))))
|
|
```
|
|
|
|
---
|
|
|
|
## Header Arguments
|
|
|
|
### Standard Header Arguments (Always Available)
|
|
|
|
| Argument | Values | Description |
|
|
|-------------|---------------------------|----------------------|
|
|
| `:results` | value, output | What to capture |
|
|
| `:session` | name, "none" | Session to use |
|
|
| `:var` | name=value | Variable binding |
|
|
| `:exports` | code, results, both, none | Export behavior |
|
|
| `:file` | path | Save results to file |
|
|
| `:dir` | path | Working directory |
|
|
| `:prologue` | code | Code to run before |
|
|
| `:epilogue` | code | Code to run after |
|
|
| `:noweb` | yes, no, tangle | Noweb expansion |
|
|
| `:cache` | yes, no | Cache results |
|
|
| `:tangle` | yes, no, filename | Tangle behavior |
|
|
|
|
### Result Types
|
|
|
|
```elisp
|
|
;; Accessing result configuration
|
|
(let* ((result-params (cdr (assq :result-params params)))
|
|
(result-type (cdr (assq :result-type params))))
|
|
|
|
;; result-type is 'value or 'output
|
|
;; result-params is a list like ("value" "replace" "scalar")
|
|
|
|
(cond
|
|
((eq result-type 'output)
|
|
;; Capture stdout
|
|
(ob-elixir--execute-output body))
|
|
((eq result-type 'value)
|
|
;; Return last expression value
|
|
(ob-elixir--execute-value body))))
|
|
```
|
|
|
|
### Parsing Header Arguments
|
|
|
|
```elisp
|
|
(defun ob-elixir--parse-params (params)
|
|
"Parse PARAMS into a structured format."
|
|
(let ((session (cdr (assq :session params)))
|
|
(result-type (cdr (assq :result-type params)))
|
|
(result-params (cdr (assq :result-params params)))
|
|
(dir (cdr (assq :dir params)))
|
|
(vars (org-babel--get-vars params))
|
|
;; Custom header args
|
|
(mix-project (cdr (assq :mix-project params)))
|
|
(mix-env (cdr (assq :mix-env params)))
|
|
(timeout (cdr (assq :timeout params))))
|
|
(list :session session
|
|
:result-type result-type
|
|
:result-params result-params
|
|
:dir dir
|
|
:vars vars
|
|
:mix-project mix-project
|
|
:mix-env mix-env
|
|
:timeout (or timeout ob-elixir-default-timeout))))
|
|
```
|
|
|
|
---
|
|
|
|
## Result Handling
|
|
|
|
### The `org-babel-result-cond` Macro
|
|
|
|
This handles result formatting based on `:results` header:
|
|
|
|
```elisp
|
|
(org-babel-result-cond result-params
|
|
;; This form is used for :results output/scalar/verbatim
|
|
(ob-elixir--raw-output body)
|
|
|
|
;; This form is used for :results value (default)
|
|
;; Should return elisp data for possible table conversion
|
|
(ob-elixir--parsed-value body))
|
|
```
|
|
|
|
### Converting Results to Tables
|
|
|
|
```elisp
|
|
(defun ob-elixir--table-or-string (result)
|
|
"Convert RESULT to an Emacs table or string.
|
|
|
|
If RESULT looks like a list/table, parse it.
|
|
Otherwise return as string."
|
|
(let ((res (org-babel-script-escape result)))
|
|
(if (listp res)
|
|
(mapcar (lambda (el)
|
|
(if (eq el 'nil)
|
|
org-babel-elixir-nil-to
|
|
el))
|
|
res)
|
|
res)))
|
|
|
|
(defvar org-babel-elixir-nil-to 'hline
|
|
"Value to use for nil in Elixir results.
|
|
When nil values appear in lists/tables, convert to this.")
|
|
```
|
|
|
|
### Using `org-babel-reassemble-table`
|
|
|
|
Restore column/row names to tables:
|
|
|
|
```elisp
|
|
(org-babel-reassemble-table
|
|
result ; The result data
|
|
(org-babel-pick-name ; Column names
|
|
(cdr (assq :colname-names params))
|
|
(cdr (assq :colnames params)))
|
|
(org-babel-pick-name ; Row names
|
|
(cdr (assq :rowname-names params))
|
|
(cdr (assq :rownames params))))
|
|
```
|
|
|
|
### File Results
|
|
|
|
For `:results file`:
|
|
|
|
```elisp
|
|
(defun ob-elixir--handle-file-result (result params)
|
|
"Handle file output for RESULT based on PARAMS."
|
|
(let ((file (cdr (assq :file params))))
|
|
(when file
|
|
(with-temp-file file
|
|
(insert result))
|
|
file))) ; Return filename for link creation
|
|
```
|
|
|
|
---
|
|
|
|
## Session Management
|
|
|
|
### Using Comint for Sessions
|
|
|
|
Org-babel provides utilities for managing comint-based REPLs:
|
|
|
|
```elisp
|
|
(require 'ob-comint)
|
|
|
|
(defvar ob-elixir-prompt-regexp "^iex([0-9]+)> "
|
|
"Regexp matching the IEx prompt.")
|
|
|
|
(defun ob-elixir--get-or-create-session (name params)
|
|
"Get or create an IEx session named NAME."
|
|
(let ((buffer-name (format "*ob-elixir-%s*" name)))
|
|
(if (org-babel-comint-buffer-livep (get-buffer buffer-name))
|
|
(get-buffer buffer-name)
|
|
(ob-elixir--start-session buffer-name params))))
|
|
|
|
(defun ob-elixir--start-session (buffer-name params)
|
|
"Start a new IEx session in BUFFER-NAME."
|
|
(let* ((iex-cmd (or (cdr (assq :iex-command params)) "iex"))
|
|
(mix-project (cdr (assq :mix-project params)))
|
|
(cmd (if mix-project
|
|
(format "cd %s && iex -S mix"
|
|
(shell-quote-argument mix-project))
|
|
iex-cmd)))
|
|
(with-current-buffer (get-buffer-create buffer-name)
|
|
(make-comint-in-buffer "ob-elixir" (current-buffer)
|
|
shell-file-name nil "-c" cmd)
|
|
;; Wait for prompt
|
|
(sit-for 1)
|
|
;; Configure IEx for non-interactive use
|
|
(ob-elixir--configure-session (current-buffer))
|
|
(current-buffer))))
|
|
|
|
(defun ob-elixir--configure-session (buffer)
|
|
"Configure IEx session in BUFFER for programmatic use."
|
|
(org-babel-comint-in-buffer buffer
|
|
(insert "IEx.configure(colors: [enabled: false])")
|
|
(comint-send-input nil t)
|
|
(org-babel-comint-wait-for-output buffer)))
|
|
```
|
|
|
|
### Evaluating in Session
|
|
|
|
```elisp
|
|
(defun ob-elixir--evaluate-in-session (session body)
|
|
"Evaluate BODY in SESSION buffer, return output."
|
|
(let ((session-buffer (ob-elixir--get-or-create-session session nil))
|
|
(eoe-indicator ob-elixir-eoe-indicator))
|
|
(org-babel-comint-with-output
|
|
(session-buffer ob-elixir-prompt-regexp t eoe-indicator)
|
|
(insert body)
|
|
(comint-send-input nil t)
|
|
;; Send EOE marker
|
|
(insert (format "\"%s\"" eoe-indicator))
|
|
(comint-send-input nil t))))
|
|
```
|
|
|
|
### Key Comint Utilities
|
|
|
|
```elisp
|
|
;; Check if buffer has live process
|
|
(org-babel-comint-buffer-livep buffer)
|
|
|
|
;; Execute forms in comint buffer context
|
|
(org-babel-comint-in-buffer buffer
|
|
(insert "code")
|
|
(comint-send-input))
|
|
|
|
;; Wait for output with timeout
|
|
(org-babel-comint-wait-for-output buffer)
|
|
|
|
;; Capture output until EOE marker
|
|
(org-babel-comint-with-output (buffer prompt t eoe-marker)
|
|
body...)
|
|
```
|
|
|
|
---
|
|
|
|
## Variable Handling
|
|
|
|
### Getting Variables from Params
|
|
|
|
```elisp
|
|
;; org-babel--get-vars returns list of (name . value) pairs
|
|
(let ((vars (org-babel--get-vars params)))
|
|
;; vars = (("x" . 5) ("y" . "hello") ("data" . ((1 2) (3 4))))
|
|
(dolist (var vars)
|
|
(let ((name (car var))
|
|
(value (cdr var)))
|
|
;; Process each variable
|
|
)))
|
|
```
|
|
|
|
### Converting Elisp to Elixir
|
|
|
|
```elisp
|
|
(defun ob-elixir--elisp-to-elixir (value)
|
|
"Convert Elisp VALUE to Elixir literal syntax."
|
|
(cond
|
|
;; nil -> nil
|
|
((null value) "nil")
|
|
|
|
;; t -> true
|
|
((eq value t) "true")
|
|
|
|
;; Numbers stay as-is
|
|
((numberp value) (number-to-string value))
|
|
|
|
;; Strings get quoted
|
|
((stringp value) (format "\"%s\"" (ob-elixir--escape-string value)))
|
|
|
|
;; Symbols become atoms
|
|
((symbolp value) (format ":%s" (symbol-name value)))
|
|
|
|
;; Lists become Elixir lists
|
|
((listp value)
|
|
(if (ob-elixir--alist-p value)
|
|
;; Association list -> keyword list or map
|
|
(ob-elixir--alist-to-elixir value)
|
|
;; Regular list
|
|
(format "[%s]"
|
|
(mapconcat #'ob-elixir--elisp-to-elixir value ", "))))
|
|
|
|
;; Fallback: convert to string
|
|
(t (format "%S" value))))
|
|
|
|
(defun ob-elixir--escape-string (str)
|
|
"Escape special characters in STR for Elixir."
|
|
(replace-regexp-in-string
|
|
"\\\\" "\\\\\\\\"
|
|
(replace-regexp-in-string
|
|
"\"" "\\\\\""
|
|
(replace-regexp-in-string
|
|
"\n" "\\\\n" str))))
|
|
|
|
(defun ob-elixir--alist-p (list)
|
|
"Return t if LIST is an association list."
|
|
(and (listp list)
|
|
(cl-every (lambda (el)
|
|
(and (consp el) (atom (car el))))
|
|
list)))
|
|
|
|
(defun ob-elixir--alist-to-elixir (alist)
|
|
"Convert ALIST to Elixir keyword list or map."
|
|
(format "[%s]"
|
|
(mapconcat
|
|
(lambda (pair)
|
|
(format "%s: %s"
|
|
(car pair)
|
|
(ob-elixir--elisp-to-elixir (cdr pair))))
|
|
alist
|
|
", ")))
|
|
```
|
|
|
|
### Handling Tables (hlines)
|
|
|
|
```elisp
|
|
(defvar org-babel-elixir-hline-to ":hline"
|
|
"Elixir representation for org table hlines.")
|
|
|
|
(defun ob-elixir--table-to-elixir (table)
|
|
"Convert org TABLE to Elixir list of lists."
|
|
(format "[%s]"
|
|
(mapconcat
|
|
(lambda (row)
|
|
(if (eq row 'hline)
|
|
org-babel-elixir-hline-to
|
|
(format "[%s]"
|
|
(mapconcat #'ob-elixir--elisp-to-elixir row ", "))))
|
|
table
|
|
", ")))
|
|
```
|
|
|
|
---
|
|
|
|
## Utility Functions
|
|
|
|
### From ob.el / ob-core.el
|
|
|
|
```elisp
|
|
;; Execute external command with body as stdin
|
|
(org-babel-eval command body)
|
|
;; Returns stdout as string
|
|
|
|
;; Create temporary file
|
|
(org-babel-temp-file "prefix-")
|
|
;; Returns temp file path
|
|
|
|
;; Read file contents
|
|
(org-babel-eval-read-file filename)
|
|
|
|
;; Process file path for use in scripts
|
|
(org-babel-process-file-name filename)
|
|
|
|
;; Parse script output as Elisp data
|
|
(org-babel-script-escape output)
|
|
;; Parses "[1, 2, 3]" -> (1 2 3)
|
|
|
|
;; Remove trailing newlines
|
|
(org-babel-chomp string)
|
|
|
|
;; Expand generic body with vars
|
|
(org-babel-expand-body:generic body params var-lines)
|
|
```
|
|
|
|
### Wrapper Method Pattern
|
|
|
|
For `:results value`, wrap code to capture return value:
|
|
|
|
```elisp
|
|
(defconst ob-elixir-wrapper-method
|
|
"
|
|
result = (fn ->
|
|
%s
|
|
end).()
|
|
|
|
result
|
|
|> inspect(limit: :infinity, printable_limit: :infinity)
|
|
|> IO.puts()
|
|
"
|
|
"Template for wrapping Elixir code to capture value.
|
|
%s is replaced with the user's code.")
|
|
|
|
(defun ob-elixir--wrap-for-value (body)
|
|
"Wrap BODY to capture its return value."
|
|
(format ob-elixir-wrapper-method body))
|
|
```
|
|
|
|
---
|
|
|
|
## Complete Implementation Template
|
|
|
|
Here's a minimal but functional template:
|
|
|
|
```elisp
|
|
;;; ob-elixir.el --- Org Babel functions for Elixir -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (C) 2024 Your Name
|
|
;; Author: Your Name <email@example.com>
|
|
;; Version: 1.0.0
|
|
;; Package-Requires: ((emacs "27.1") (org "9.4"))
|
|
;; Keywords: literate programming, elixir
|
|
;; URL: https://github.com/user/ob-elixir
|
|
|
|
;;; Commentary:
|
|
|
|
;; Org Babel support for evaluating Elixir code blocks.
|
|
|
|
;;; Code:
|
|
|
|
(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."
|
|
:type 'string
|
|
:group 'ob-elixir)
|
|
|
|
;;; Header Arguments
|
|
|
|
(defvar org-babel-default-header-args:elixir
|
|
'((:results . "value")
|
|
(:session . "none"))
|
|
"Default header arguments for Elixir code blocks.")
|
|
|
|
(defconst org-babel-header-args:elixir
|
|
'((mix-project . :any))
|
|
"Elixir-specific header arguments.")
|
|
|
|
;;; File Extension
|
|
|
|
(add-to-list 'org-babel-tangle-lang-exts '("elixir" . "ex"))
|
|
|
|
;;; Execution
|
|
|
|
(defun org-babel-execute:elixir (body params)
|
|
"Execute Elixir BODY according to PARAMS."
|
|
(let* ((result-type (cdr (assq :result-type params)))
|
|
(result-params (cdr (assq :result-params params)))
|
|
(full-body (org-babel-expand-body:generic
|
|
body params
|
|
(org-babel-variable-assignments:elixir params)))
|
|
(result (ob-elixir--execute full-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))))))
|
|
|
|
(defun ob-elixir--execute (body result-type)
|
|
"Execute BODY and return result based on RESULT-TYPE."
|
|
(let* ((tmp-file (org-babel-temp-file "elixir-" ".exs"))
|
|
(wrapper (if (eq result-type 'value)
|
|
(ob-elixir--wrap-for-value body)
|
|
body)))
|
|
(with-temp-file tmp-file
|
|
(insert wrapper))
|
|
(org-babel-eval
|
|
(format "%s %s" ob-elixir-command
|
|
(org-babel-process-file-name tmp-file))
|
|
"")))
|
|
|
|
(defconst ob-elixir--value-wrapper
|
|
"result = (\n%s\n)\nIO.puts(inspect(result, limit: :infinity))"
|
|
"Wrapper to capture return value.")
|
|
|
|
(defun ob-elixir--wrap-for-value (body)
|
|
"Wrap BODY to capture its return value."
|
|
(format ob-elixir--value-wrapper body))
|
|
|
|
;;; Variables
|
|
|
|
(defun org-babel-variable-assignments:elixir (params)
|
|
"Return Elixir code to assign variables from PARAMS."
|
|
(mapcar
|
|
(lambda (pair)
|
|
(format "%s = %s"
|
|
(car pair)
|
|
(ob-elixir--elisp-to-elixir (cdr pair))))
|
|
(org-babel--get-vars params)))
|
|
|
|
(defun ob-elixir--elisp-to-elixir (value)
|
|
"Convert Elisp VALUE to Elixir syntax."
|
|
(cond
|
|
((null value) "nil")
|
|
((eq value t) "true")
|
|
((numberp value) (number-to-string value))
|
|
((stringp value) (format "\"%s\"" value))
|
|
((symbolp value) (format ":%s" (symbol-name value)))
|
|
((listp value)
|
|
(format "[%s]" (mapconcat #'ob-elixir--elisp-to-elixir value ", ")))
|
|
(t (format "%S" value))))
|
|
|
|
(provide 'ob-elixir)
|
|
;;; ob-elixir.el ends here
|
|
```
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- [Org Mode Manual - Working with Source Code](https://orgmode.org/manual/Working-with-Source-Code.html)
|
|
- [Org Mode Manual - Languages](https://orgmode.org/manual/Languages.html)
|
|
- [Org Mode Manual - Results of Evaluation](https://orgmode.org/manual/Results-of-Evaluation.html)
|
|
- [Worg - Babel Language Template](https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-template.html)
|
|
- [Worg - Python Documentation](https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-python.html)
|
|
- [Org Source - ob.el](https://git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/lisp/ob.el)
|
|
- [Org Source - ob-python.el](https://git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/lisp/ob-python.el)
|
|
- [Org Source - ob-ruby.el](https://git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/lisp/ob-ruby.el)
|
|
- [Org Element API](https://orgmode.org/worg/dev/org-element-api.html)
|