README
This commit is contained in:
345
README.org
Normal file
345
README.org
Normal file
@@ -0,0 +1,345 @@
|
||||
#+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.
|
||||
Reference in New Issue
Block a user