#+TITLE: ob-elixir #+AUTHOR: Luis Eduardo Bueso de Barrio #+LANGUAGE: en Org Babel support for evaluating Elixir code blocks in Emacs org-mode. * Features - Execute Elixir code directly in org-mode source blocks - Support for =:results value= and =:results output= - Variable passing with =:var= header argument - Persistent IEx sessions with =:session= - Mix dependency management via =#+BEGIN_DEPS= blocks - Shared imports/aliases via =#+BEGIN_IMPORTS= blocks - Module definitions with =:module= header argument - Automatic conversion of Elixir lists to org tables - Configurable error handling and warning display * Requirements - Emacs 27.1 or later - Org-mode 9.4 or later - Elixir installed and available in PATH * Installation ** With straight.el #+begin_src emacs-lisp (straight-use-package '(ob-elixir :type git :host github :repo "username/ob-elixir")) #+end_src ** With use-package and straight.el #+begin_src emacs-lisp (use-package ob-elixir :straight (:type git :host github :repo "username/ob-elixir")) #+end_src ** Manual Installation Clone the repository and add it to your load path: #+begin_src emacs-lisp (add-to-list 'load-path "/path/to/ob-elixir") (require 'ob-elixir) #+end_src * Configuration Enable Elixir in org-babel: #+begin_src emacs-lisp (org-babel-do-load-languages 'org-babel-load-languages '((elixir . t))) #+end_src * Usage ** Basic Execution Execute Elixir code with =C-c C-c=: #+begin_example ,#+begin_src elixir 1 + 2 ,#+end_src ,#+RESULTS: : 3 #+end_example ** Results: Value vs Output By default, blocks return the value of the last expression (=:results value=): #+begin_example ,#+begin_src elixir :results value Enum.map([1, 2, 3], fn x -> x * 2 end) ,#+end_src ,#+RESULTS: | 2 | 4 | 6 | #+end_example Use =:results output= to capture printed output: #+begin_example ,#+begin_src elixir :results output IO.puts("Hello, World!") IO.puts("Line 2") ,#+end_src ,#+RESULTS: : Hello, World! : Line 2 #+end_example ** Variables Pass variables to Elixir code with =:var=: #+begin_example ,#+begin_src elixir :var name="Elixir" count=3 String.duplicate(name, count) ,#+end_src ,#+RESULTS: : ElixirElixirElixir #+end_example Variables are automatically converted: - Elisp strings become Elixir strings - Elisp numbers become Elixir numbers - Elisp lists become Elixir lists - Elisp vectors become Elixir tuples - Elisp =t= becomes =true=, =nil= becomes =nil= - Elisp symbols become atoms ** Sessions Use =:session= to maintain state across blocks: #+begin_example ,#+begin_src elixir :session my-session x = 10 ,#+end_src ,#+RESULTS: : 10 ,#+begin_src elixir :session my-session x * 2 ,#+end_src ,#+RESULTS: : 20 #+end_example Manage sessions with: - =M-x ob-elixir-kill-session= - Kill a specific session - =M-x ob-elixir-kill-all-sessions= - Kill all sessions ** Dependencies Define Mix dependencies with =#+BEGIN_DEPS= blocks. Dependencies apply to all subsequent Elixir blocks in the document: #+begin_example ,#+BEGIN_DEPS elixir [ {:jason, "~> 1.4"}, {:decimal, "~> 2.0"} ] ,#+END_DEPS ,#+begin_src elixir Jason.encode!(%{hello: "world"}) ,#+end_src ,#+RESULTS: : {"hello":"world"} #+end_example Dependencies are cached in =~/.cache/ob-elixir/= (configurable via =ob-elixir-deps-cache-dir=). The first execution fetches and compiles dependencies; subsequent executions reuse the cache. Manage dependency projects with: - =M-x ob-elixir-list-deps-projects= - List cached projects - =M-x ob-elixir-cleanup-deps-projects= - Delete all cached projects ** Imports Define shared imports, aliases, and requires with =#+BEGIN_IMPORTS= blocks: #+begin_example ,#+BEGIN_IMPORTS elixir import Enum, only: [map: 2, filter: 2] alias String, as: S ,#+END_IMPORTS ,#+begin_src elixir map([1, 2, 3], &(&1 * 2)) |> filter(&(&1 > 2)) ,#+end_src ,#+RESULTS: | 4 | 6 | ,#+begin_src elixir S.upcase("hello") ,#+end_src ,#+RESULTS: : HELLO #+end_example ** Module Definitions Define reusable modules with the =:module= header argument: #+begin_example ,#+begin_src elixir :module MyMath def add(a, b), do: a + b def multiply(a, b), do: a * b ,#+end_src ,#+RESULTS: : Module MyMath: functions defined #+end_example Multiple blocks with the same =:module= name merge their contents: #+begin_example ,#+begin_src elixir :module MyMath def subtract(a, b), do: a - b ,#+end_src #+end_example Use the module in subsequent blocks (requires explicit import): #+begin_example ,#+BEGIN_IMPORTS elixir import MyMath ,#+END_IMPORTS ,#+begin_src elixir add(1, 2) |> multiply(3) ,#+end_src ,#+RESULTS: : 9 #+end_example ** Tables Elixir lists are automatically converted to org tables: #+begin_example ,#+begin_src elixir [ ["Name", "Age"], ["Alice", 30], ["Bob", 25] ] ,#+end_src ,#+RESULTS: | Name | Age | | Alice | 30 | | Bob | 25 | #+end_example Tuples are also supported: #+begin_example ,#+begin_src elixir {:ok, "success"} ,#+end_src ,#+RESULTS: | ok | success | #+end_example * Customization ** Commands - =ob-elixir-command= (default: ="elixir"=) - Command to execute Elixir code - =ob-elixir-iex-command= (default: ="iex"=) - Command to start IEx sessions - =ob-elixir-mix-command= (default: ="mix"=) - Command to run Mix ** Behavior - =ob-elixir-signal-errors= (default: =t=) - When non-nil, Elixir errors are signaled as Emacs errors. When nil, errors are returned as result strings. - =ob-elixir-show-warnings= (default: =t=) - When non-nil, Elixir warnings are included in output. ** Paths - =ob-elixir-deps-cache-dir= (default: =~/.cache/ob-elixir/=) - Directory for caching temporary Mix projects created for dependencies. ** Example Configuration #+begin_src emacs-lisp (use-package ob-elixir :after org :config (setq ob-elixir-signal-errors nil) ; Return errors as results (setq ob-elixir-show-warnings nil) ; Hide warnings (org-babel-do-load-languages 'org-babel-load-languages '((elixir . t)))) #+end_src * Development ** Running Tests Using Make: #+begin_src sh make test #+end_src Using Eldev: #+begin_src sh eldev test #+end_src ** Byte Compilation #+begin_src sh make compile #+end_src ** Linting #+begin_src sh make lint #+end_src ** Project Structure #+begin_example ob-elixir/ ├── ob-elixir.el # Main source file ├── Makefile # Build and test commands ├── Eldev # Eldev configuration ├── test/ # Test suite │ ├── test-ob-elixir.el │ ├── test-ob-elixir-core.el │ ├── test-ob-elixir-vars.el │ ├── test-ob-elixir-results.el │ ├── test-ob-elixir-errors.el │ ├── test-ob-elixir-org.el │ ├── test-ob-elixir-deps.el │ ├── test-ob-elixir-imports.el │ ├── test-ob-elixir-modules.el │ └── test-ob-elixir-sessions.el └── docs/ # Implementation documentation #+end_example * License This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.