Elixir plugin for JetBrain's IntelliJ Platform (including Rubymine)
1,897
stars
6,370
commits
Kotlin
primary language
Sep 9, 2026
updated
This is a plugin that adds support for Elixir to JetBrains IDEs.
The plugin works both in the rich IDEs that allow alternative language SDK selection and small IDEs that are language specific. The rich IDEs work best for IntelliJ Elixir because only in the rich IDEs can have an Elixir SDK set as the Project SDK. In all small IDEs, the native language SDK is always there, which makes anything that uses the SDK, such as running elixir, erl, or mix more complicated both internally and externally in the configuration you have to setup.
The plugin is free to use in all JetBrains IDEs. The Cost column in the below table is what JetBrains charges for the IDE itself. IntelliJ Elixir is maintained by @KronicDeth who does not get any of the subscription money. If you want to support the plugin itself, make a donation.
| IDE | Rich/Small | Languages | Cost | Trial | License | Source |
|---|---|---|---|---|---|---|
| IntelliJ IDEA Community Edition | Rich | Java | Free | N/A | Apache 2.0 | JetBrains/intellij-community |
| IntelliJ IDEA Ultimate Edition | Rich | Java | Subscription | 30-days | Commercial | N/A |
| AppCode | Small | Objective-C | Subscription | 30-days | Commercial | N/A |
| CLion | Small | C/C++ | Subscription | 30-days | Commercial | N/A |
| DataGrip | Small | SQL | Subscription | 30-days | Commercial | N/A |
| GoLand | Small | Go | Subscription | 30-days | Commercial | N/A |
| PHPStorm | Small | PHP | Subscription | 30-days | Commercial | N/A |
| PyCharm Community Edition | Small | Python | Free | N/A | Apache 2.0 | JetBrains/intellij-community subdirectory |
| PyCharm Professional Edition | Small | Python | Subscription | N/A | Commercial | N/A |
| Rider | Small | .NET | Subscription | N/A | Commercial | N/A |
| RubyMine | Small | Ruby | Subscription | 30-days (90-day for whole team) | Commercial | N/A |
| WebStorm | Small | JavaScript | Subscription | 30-days | Commercial | N/A |
Once you have your IDE of choice installed, you can install this plugin
| Feature | Rich | Small | Alternative |
|---|---|---|---|
| Project | Yes | No | 1. Open directory 2. Setup the SDK |
| Project Structure | Automatic | Manual | |
| Project Settings | Yes | No | |
| Module Settings | Yes | No | |
| New Elixir File | Yes | Yes | |
| Syntax Highlighting and Semantic Annotation | Yes | Yes | |
| Grammar Parsing | Yes | Yes | |
| Inspections | Yes | Yes | |
| Quick Fixes | Yes | Yes | |
| Code Folding | Yes | Yes | |
| Commenter | Yes | Yes | |
| Debugger | Yes | Yes | |
| Delimiters | Yes | Yes | |
| Embedded Elixir (EEx) Templates | Yes | Yes | |
| Building/Compiling | Yes | No | Build/compile as part mix run configurations only |
| Live Templates | Yes | Yes | |
| Documentation | Yes | Yes | |
| Run Configurations | Yes | Yes | |
| Completion | Yes | Yes | |
| Decompilation | Yes | Yes | |
| Go To Declaration | Yes | Yes | |
| Formatting | Yes | Yes | |
| Go To Related | Yes | Yes | |
| Go To Symbol | Yes | Yes | |
| Go To Test | Yes | Yes | |
| Go To Test Subject | Yes | Yes | |
| Find Usage | Yes | Yes | |
| Live Embedded Elixir (LEEx) Templates | Yes | Yes | |
| HEEx Templates | Yes | Yes | |
| Refactor | Yes | Yes | |
| SDK | Yes | Yes | |
| Structure | Yes | Yes |
If you've already created a mix project, you can load it as an Elixir project into the plugin.
File > New > Project From Existing Sources...
Select the root directory of your project.
Select "Import project from external model"
Select Mix

Click Next
Select a Project SDK directory by clicking Configure.
The plugin will automatically find the newest version of Elixir installed. (NOTE: SDK detection only works for Linux, homebrew installs on OSX, and Windows. Open an issue with information about Elixir install locations on your operating system and package manager to have SDK detection added for it.)
If the automatic detection doesn't find your Elixir SDK or you want to use an older version, manually select select
the directory above the bin directory containing elixir, elixirc, iex, and mix. (On Windows it is the
directory containing elixir.bat, elixirc.bat, iex.bat, and mix.bat.)
Click Finish after you select SDK name from the Project SDK list.
The "Mix project root" will be filled in with the selected directory.
(Optional) Uncheck "Fetch dependencies with mix" if you don't want to run mix deps.get when importing the project
mix local.hex --force and mix deps.get will be run.Click Next
All directories with mix.exs files will be selected as "Mix projects to import". To import just the main project and not its dependencies, click Unselect All.
Check the box next to the project root to use only its mix.exs. (It will likely be the first checkbox at the top.)
Click Finish
If you've already created a (non-mix) project, you can load it as an Elixir project into the plugin.
mix.exs. Uncheck any project roots that you don't want added.bin directory containing elixir, elixirc, iex, and mix.If you want to create a basic (non-mix) Elixir project with a lib directory, perform the following steps.
File > New > Project

Select Elixir from the project type menu on the left
Click Next

Select a Project SDK directory by clicking Configure.

Select a Project SDK directory by clicking Configure.
The plugin will automatically find the newest version of Elixir installed.
/usr/local/Cellar/elixir)/nix/store)/usr/local/lib/elixir/nix/store)C:\Program Files\Elixir)C:\Program Files (x86)\Elixir)If the automatic detection doesn't find your Elixir SDK or you want to use an older version, manually select select the directory above the bin directory containing elixir, elixirc, iex, and mix. If the bin, lib, or src directory is incorrectly selected, it will be corrected to the parent directory.
Click Next after you select SDK name from the Project SDK list.
Change the Project name to the name your want for the project

(Optionally) change the Project location if the directory does not match what you want
(Optionally) expand More Settings to change the Module name, Content root, Module file location, and/or Project format. The defaults derived from the Project name and Project location should work for most projects.
Click Finish
Choose whether to open in a New Window or in This Window.


.elixir_ls (Elixir Language Server cache)assets/node_modules/phoenix (Phoenix symlink back to deps)assets/node_modules/phoenix_html (Phoenix HTML symlink back to deps)cover (Test coverage output)deps (Dependencies -- re-exposed as Libraries for Go-to-Declaration)doc (Documentation generated by ex_doc)logs (Conventional log directory)libweb (legacy Phoenix layout compatibility)spec (ESpec)test
The Project Settings include
When setting up Elixir and Erlang SDKs for the first time:
The plugin supports running Elixir, Mix, and IEx commands within WSL. This includes:
\\wsl$\Ubuntu\...) and Windows drive paths are automatically converted to POSIX paths when executing commands in WSL./mnt/c drive to access Windows files from WSL, but the performance is terrible.
The Module Settings include Marking directories as

Module paths list the output directories when compiling code in the module. There is a an "Output path" for dev
MIX_ENV and "Test output path" for the test MIX_ENV.

Module dependencies include the SDK and the module's own sources. Mix dependencies from deps/ are automatically detected and registered as IDE Libraries by processes that run in the background and monitor file changes:
deps/ and _build/ (after mix deps.get, mix compile or folder deletions).deps/APP/lib and class roots from _build/ENV/lib/APP/ebin.mix deps.get, try running mix compile to generate _build/ output, then wait a few seconds for the file watcher to pick up changes. You can also use Tools → Elixir → Install Mix Dependencies.The status bar can display an Elixir SDK status widget in the bottom-right corner of the IDE. It shows the current SDK version and warns about configuration problems. It will only display when you have an Elixir file open in the editor.
Enabling the widget:
Widget states:
| Display | Meaning |
|---|---|
Elixir: 1.17.3 | SDK configured correctly |
Elixir: 1.17.3 ! | Classpath issues (missing ebin paths or Erlang SDK classpath entries) |
Elixir: 1.17.3 ⚠ OTP | Elixir SDK was compiled against a different OTP major than the paired Erlang SDK |
Elixir: 1.17.3 ⚙ | Folder marks are suboptimal (e.g., test/ not marked as Test Sources) |
Elixir: SDK Error | Module .iml files reference an SDK that no longer exists |
Elixir: Issues | SDK is invalid or missing its Erlang SDK dependency |
No Elixir SDK | No Elixir SDK configured for the project |
Click the widget to open a popup with quick actions:
mix deps.get for the projectlib/ → Sources, test/ → Test Sources, deps/ → Excluded, etc.) and fix dangling module SDK referencesNotifications: When the widget detects a problem, it fires a notification with actionable buttons:
Notifications auto-expire when the issue is resolved (e.g., after running "Reconfigure Now"). Rapid project structure changes (such as importing multiple modules) are debounced to avoid notification spam.
Reconfigure Elixir Module Setup is also available from Tools → Elixir → Reconfigure Elixir Module Setup and via the action search (Ctrl+Shift+A / ⌘⇧A). It is additive -- it applies missing canonical marks without removing any user-customized source roots or exclusions.
Right-click a directory (such as lib or test in the standard mix new layout)
Select New > Elixir File.

Enter an Alias for the Module name, such as MyModule or MyNamespace.MyModule.
Select a Kind of Elixir File to use a different template.

An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created:
defmodule MyNamespace.MyModule do
@moduledoc false
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. It will have a start/2 function that calls MyNamespace.MyModule.Supervisor.start_link/0.
defmodule MyNamespace.MyModule do
@moduledoc false
use Application
def start(_type, _args) do
MyNamespace.MyModule.Supervisor.start_link()
end
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. It will have a start_link/1 function that calls Supervisor.start_link/0 and init/1 that sets
up the child specs. It assumes a MyWorker child that should be supervised :one_for_one.
defmodule MyNamespace.MyModule.Supervisor do
@moduledoc false
use Supervisor
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg)
end
def init(arg) do
children = [
worker(MyWorker, [arg], restart: :temporary)
]
supervise(children, strategy: :one_for_one)
end
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. It will have a start_link/2 function that calls GenServer.start_link/3 and the minimal
callback implementations for init/1, handle_call/3, and handle_cast/2.
The Elixir use GenServer supplies these callbacks, so this template is for when you want to change the callbacks, but
would like the stubs to get started without having to look them up in the documentation.
defmodule MyNamespace.MyModule do
@moduledoc false
use GenServer
def start_link(state, opts) do
GenServer.start_link(__MODULE__, state, opts)
end
def init(_opts) do
{:ok, %{}}
end
def handle_call(_msg, _from, state) do
{:reply, :ok, state}
end
def handle_cast(_msg, state) do
{:noreply, state}
end
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. The minimal callback implementations for init/1, handle_event/2, and handle_call/2,
handle_info/2.
The Elixir use GenEvent supplies these callbacks, so this template is for when you want to change the callbacks, but
would like the stubs to get started without having to look them up in the documentation.
defmodule MyNamespace.MyModule do
@moduledoc false
use GenEvent
# Callbacks
def init(_opts) do
{:ok, %{}}
end
def handle_event(_msg, state) do
{:ok, state}
end
def handle_call(_msg, state) do
{:ok, :ok, state}
end
def handle_info(_msg, state) do
{:ok, state}
end
end
Syntax highlighting of lexer tokens and semantic annotating of parser elements can be customized in in the Color Settings page for Elixir (Preferences > Editor > Color & Fonts > Elixir).
| Text Attribute Key Display Name | Tokens/Elements | Scheme | |||
|---|---|---|---|---|---|
| Default | Darcula | ||||
| Alias | String |
|
| ||
| Atom |
|
|
| ||
| Braces and Operators | Bit |
|
|
| |
| Braces and Operators | Braces |
|
|
| |
| Braces and Operators | Brackets |
|
|
| |
| Braces and Operators | Character Token | ? |
|
| |
| Braces and Operators | Comma | , |
|
| |
| Braces and Operators | Dot | . |
|
| |
| Braces and Operators | Interpolation |
|
|
| |
| Braces and Operators | Maps and Structs | Maps |
|
|
|
| Braces and Operators | Maps and Structs | Maps |
|
|
|
| Braces and Operators | Operation Sign |
|
|
| |
| Braces and Operators | Parentheses |
|
|
| |
| Braces and Operators | Semicolon | ; |
|
| |
| Calls | Function | inspect |
*Only the Italic attribute
|
*Only the Italic attribute
| |
| Calls | Macro | inspect |
*Only the Bold and Italic attributes
|
*Only the Bold and Italic attributes
| |
| Calls | Predefined |
|
*Only the Foreground attribute
|
*Only the Foreground attribute
| |
| Comment | # Numbers |
|
| ||
| Keywords | end |
|
| ||
| Module Attributes | @custom_attr |
|
| ||
| Module Attributes | Documentation | @doc |
|
| |
| Module Attributes | Documentation | Text | Simple module docstring |
|
|
| Module Attributes | Types | Callback | func |
|
|
| Module Attributes | Types | Specification | func |
|
|
| Module Attributes | Types | Type | parameterized |
|
|
| Module Attributes | Types | Type Parameter | type_parameter |
|
|
| Numbers | Base Prefix | Non-Decimal |
|
|
|
| Numbers | Base Prefix | Obsolete Non-Decimal |
|
|
|
| Numbers | Decimal Exponent, Mark, and Separator |
|
|
| |
| Numbers | Digits | Invalid |
|
|
|
| Numbers | Digits | Valid |
|
|
|
| Textual | Character List |
'This is a list'
|
|
| |
| Textual | Escape Sequence |
\x{12}
|
|
| |
| Textual | Sigil |
|
|
| |
| Textual | String |
"Hello world"
|
|
| |
| Variables | Ignored |
_
|
|
| |
| Variables | Parameter |
|
|
| |
| Variables | Variable |
pid
|
|
| |
Built on top of highlighted tokens above, the parser understands the following parts of Elixir grammar as valid or allows the grammar because they contain correctable errors:
())()) and matched expressions.true, false, and nil).do blocks that have either (1) a positional argument and keyword
arguments OR (2) two or more positional arguments with optional keyword arguments..() with either no arguments; a no parentheses arguments expression as an argument; keywords
as an argument; positional argument(s); or positional arguments followed by keywords as arguments.Alias.function, :atom.function, etc) and local function calls (function) with...
Alias.function)Alias.function key: value)Alias.function Inner.function positional, key: value)Alias.function positional, key: value)Alias.function 1 + 2)Alias.function())Alias.function(Inner.function positional, key: value)Alias.function(key: value))Alias.function(positional, key: value))def unquote(variable)(positional))variable[key])function do end)Inspections mark sections of code with warnings and errors. They can be customized from the Preferences > Inspections > Elixir.

Detects when compiler will throw unexpected comma. Parentheses are required to solve ambiguity in nested calls.
Function calls with multiple arguments without parentheses cannot take as arguments functions with multiple arguments
without parentheses because which functional gets which arguments is unclear as in the following example:
outer_function first_outer_argument,
# second argument is another function call without parentheses, but with multiple arguments
inner_function first_inner_argument,
ambiguous_keyword_key: ambiguous_keyword_value
To fix the ambiguity if first_inner_keyword_key: first_inner_keyword_value should be associated, add parentheses
around the inner function's arguments:
# keywords are for inner function
outer_function first_outer_argument
inner_function(
first_inner_argument
ambiguous_keyword_key: ambiguous_keyword_value
)
# keywords are for outer function
outer_function first_outer_argument
inner_function(
first_inner_argument
),
ambiguous_keyword_key: ambiguous_keyword_value
Detects when compiler will throw unexpected parenthesis. If you are making a function call, do not insert spaces in between the function name and the opening parentheses.
Function calls with space between the function name and the parentheses cannot distinguish between function calls with
parentheses, but with an accidental space before the ( and function calls without parentheses where the first
positional argument is in parentheses.
function ()
To fix the ambiguity remove the space or add outer parentheses without the space if the first argument should be ():
# extra space, no arguments to function
function()
# first argument is `()`
function(())
function (key: value)
Keywords inside parentheses is not valid, so the only way to fix this is to remove the space
function(key: value)
function (first_positional, second_positional)
A list of positional arguments in parenthenses is not valid, so the only way to fix this is to remove the space
function(first_positional, second_positional)
:) used in type spec instead of type operator (::)Type specifications separate the name from the definition using ::.
@type name: definition
Replace the : with ::
@type name :: definition
one.(
one,
two positional, key: value,
three
)
Keywords can only appear at the end of an argument list, so either surround the no parentheses expression argument with parentheses, or move the the keywords to the end of the list if it wasn't meant to be a no parentheses expression.
one.(
one
two(positional, key: value),
three
)
OR
one.(
one,
two,
three,
key: value
)
=) used in type spec instead of type operator (::)Type specifications separate the name from the definition using ::.
@type name = definition
Replace the = with ::
@type name :: definition
Quick Fixes are actions IntelliJ can take to change your code to correct errors (accessed with Alt+Enter by default).
: to :: in type specsIf a type specification uses a single : instead of ::, then hit Alt+Enter on the : to change it to :: and fix the type spec.
= to :: in type specsIf a type specification uses = instead of ::, then hit Alt+Enter on the = to change it to :: and fix the type spec.
If a set of parentheses is marked as ambiguous then the space before it can be removed to disambiguate the parentheses with Alt+Enter. (Will vary based on keymap.)
You can collapse (fold) pre-defined regions of your Elixir code to make it easier to quickly scroll through files or hide details you don't care about right now.
| Expanded | Collapsed | Folded By Default? |
|---|---|---|
do end | do: ... | No |
-> and right operand | -> ... | No |
@doc VALUE | @doc "..." | No |
@moduledoc VALUE | @moduledoc "..." | No |
@typedoc VALUE | @typedoc "..." | No |
| alias ALIAS1 alias ALIAS1 | alias ... | Yes |
| import ALIAS1 import ALIAS2 | import ... | Yes |
| require ALIAS1 require ALIAS2 | require ... | Yes |
| use ALIAS1 use ALIAS2 | use ALIAS1 | Yes |
@for | FOR in defimpl PROTOCOL, for: FOR | Yes |
@protocol | PROTOCOL in defimpl PROTOCOL, for: FOR | Yes |
| @MODULE_ATTRIBUTE | VALUE in @MODULE_ATTRIBUTE VALUE | No |
You can comment or uncomment the current line or selected block of source. By selecting a block of source first you can quickly comment out and entire function if you're trying to track down a compiling or testing error that's not giving a helpful line number.
Using the menus
Cmd+/.If you'd like to run mix credo use can run it as a global Inspection.
You'll be presented with a "Run 'Credo'" dialog

deps to "Custom scope"The Inspections Result Tool Pane will open and show results as each file is processed.
Click the ▶ to expand the Credo section to show all warnings

Click an entry for the details of an individual warning with a code highlighting.

The view will show the parts of the file that aren't annotated as collapsed with the discontinuous line number indicating the jumps.
If you click on + collapse markers, you can expand the collapsed sections to see the full context

Or you can hover over the collapsed section to see a tooltip preview of the expansion

| Preferences > Editor > Inspections | Preferences > Editor > Inspections > Credo | Editor | Inspections | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Elixir > Credo | Include Explanation | Highlight | Message | Explanation in tooltip |
mix credo Runs
| Highlight | Message |
mix credo Runs
| Action | ||
| Per File | Per Issue | Working Directory | Inspect Code | Run Inspection By Name | |||||||
| ☑ | ☑ | Yes | Yes | Yes | 1 | 1 | Yes | Yes | 1 | Yes | Yes |
| ☑ | ☐ | Yes | Yes | No | 1 | 0 | Yes | Yes | 1 | Yes | Yes |
| ☐ | ⁿ/ₐ | No | No | No | 0 | 0 | Yes | Yes | 1 | No | Yes |
If you want to limit the performance impact of the credo annotator because mix credo spikes your CPU, you can limit the number of mix credo runs to 1 per open file by disabling the Explanation tooltip
If you don't want the annotator to run at all on open editors, then you can disable the paired inspection
Once the annotator is disabled, you can still run the inspection in batch mode
IntelliJ Elixir allows for graphical debugging of *.ex files using line breakpoints.
*.ex filesAfter you have configured a run configuration for your project, you can launch it in debug mode by pressing Ctrl+D.
| Action | Keyword Shortcut |
|---|---|
| Toggle Breakpoint | Cmd+F8 |
| Resume Program | Alt+Cmd+R |
| Step Over | F8 |
| Step Into | F7 |
| View breakpoint details/all breakpoints | Shift+Cmd+F8 |
By default, the debugger will scan all the load paths and build path for .beam files and the corresponding modules will be interpreted which causes the Module's Erlang abstract code chunk to be interpreted in Erlang instead of the bytecode chunk being executed in the C parts of the BEAM. This interpretation is much slower than execution, so by default all of the Elixir standard library and the common modules installed in Phoenix projects are excluded from being interpreted when the debugger starts. The modules can be still be stepped into or have breakpoints explicitly set.

You can customize these module patterns as an application setting.






If you want to customize the modules to ignore on a per-Run-Configuration basis, you can set an environment variable in the Run Configuration.
| Variable | Example | Description |
|---|---|---|
| INTELLIJ_ELIXIR_DEBUG_BLACKLIST | iconv,some | Excluding modules from debugger |
Notice: If you want non Elixir. module in blacklist, write it with: :. This rule applies only to module atoms.
When a breakpoint is set, the editor displays a breakpoint icon in the gutter area to the left of the affected source code. A breakpoint icon denotes status of a breakpoint, and provides useful information about its type, location, and action.
The icons serve as convenient shortcuts for managing breakpoints. Clicking an icon removes the breakpoint. Successive use of Alt - click on an icon toggles its state between enabled and disabled. The settings of a breakpoint are shown in a tooltip when a mouse pointer hovers over a breakpoint icon in the gutter area of the editor.
| Status | Icon | Description |
|---|---|---|
| Enabled | ![]() | Indicates the debugger will stop at this line when the breakpoint is hit. |
| Disabled | ![]() | Indicates that nothing happens when the breakpoint is hit. |
| Conditionally Disabled | ![]() | This state is assigned to breakpoints when they depend on another breakpoint to be activated. |
When the button
is pressed in the toolbar of the Debug tool window, all the breakpoints in a project are muted, and their icons become grey:
.
To view the list of all breakpoints and their properties, do one of the following:
Shift+Cmd+F8
To view properties of a single breakpoint

To configure actions, suspend policy and dependencies of a breakpoint
Shift+Cmd+F8*DBG* 'Elixir.IntellijElixir.DebugServer' got cast {breakpoint_reached, PID} will appear in the console.A line breakpoint is a breakpoint assigned to a specific line in the source code.
Line breakpoints can be set on executable lines. Comments, declarations and empty lines are not valid locations for the line breakpoints. Line break points can be set in .ex and .eex files.

.eex line breaks will only work on Elixir code that is used in Phoenix view modules.

.eex breakpoints only work if a .beam file using the template's relative can be found. This means that the Phoenix view module .beam file must exist in _build prior to setting a breakpoint. Run the Run Configuration once, before debugging to complete the build if setting a breakpoint does not work.
Cmd+F8Cmd+Down. The caret will be placed at the line marked with the breakpoint in question.When you temporarily disable or enable a breakpoint, its icon changes from
to
and vice versa.
Do one of the following:
Cmd+F8.
on the toolbarCtrl+DOR
Debug quick menu
Ctrl+Alt+DEnterIt takes awhile, once the debugged process is started to configure the debugger in BEAM. To ensure that breakpoints are setup before allow the debugged code to run, the debugger blocks until setup is complete.
The debugged process will wait for the debugger to attach

Breakpoints will be set
The debugger will mark modules to be interpreted
.beam files

.beam files will be interpreted unless they match the Module Filter Pattern

The debugger attaches (so it can receive breakpoint events) and allows the debugged process to continue.

Up or Down to change framesWhen changing frames or jumping to definitions, you can lose track of where the debugger is paused. To get back to the current execution point, do one of the following:
Alt+F10
on the stepping toolbar of the Debug tool window.
Binaries show each byte at the byte's offset.

Bitstrings show each byte with any partial byte annotated with its bitwidth.

Boolean variables are rendered as their value.

Charlists show the integer values because they're treated as lists

Functions don't have literal representation, so the inspect form starting with #Fun<...> is shown

Lists render differently based on whether the list is improper or not. Improper lists show the head and tail while proper lists show their element by offset.

Maps render differently based on the key type. If the map uses all atom keys, the key will equal the value in the nested children while non-atom keys are shown as entries at a specific offset with the key and value. This is done, so that complex keys that have subterms can be expanded or collapsed, which is not possible for the simpler atom rendering.

Floats and integers are rendered as literals.

Pids are broken up into their hidden node, id, and serial`.

Strings show their literal value and unicode is fully supported.

Tuples show their elements at their offsets.

While Elixir allows rebinding variable names, Erlang does not, so when viewed in the Variables pane, rebound variables will have an @VERSION after their name indicating which rebinding of a the variable is.
When stopped at a breakpoint, you can use the Evaluate button (it looks like a simple pocket calculator) to open an editor to type code to be executed in the current stack frame.

The evaluator supports the full syntax.

The result of evaluating the code with be shown as the value of result below the entered "Expression".


Errors in the code will report back as a result tuple with an :EXIT tag. This reflects that the error has crashed the process that was evaluating the code. Thankfully, due to how how the interpreter is written, this does not lose the current stack frame and stepping or other evaluation can continue.
| Action | Icon | Shortcut | Description |
|---|---|---|---|
| Show Execution Point | ![]() | Alt+F10 | Click this button to highlight the current execution point in the editor and show the corresponding stack frame in the Frames pane. |
| Step Over | ![]() | F8 | Click this button to execute the program until the next line in the current function or file, skipping the function referenced at the current execution point (if any). If the current line is the last one in the function, execution steps to the line executed right after this function. |
| Step Into | ![]() | F7 | Click this button to have the debugger step into the function called at the current execution point. |
| Step Out | ![]() | Shift+F8 | Click this button to have the debugger step out of the current function, to the line executed right after it. |
The right-delimiter will be automatically inserted when the left delimiter is typed. In some cases, to prevent false positives, the the delimiter is only completed if when used for sigils.
| Preceded By | Left | Right |
|---|---|---|
do | end | |
fn | end | |
[ | ] | |
{ | } | |
( | ) | |
' | ' | |
''' | ''' | |
" | " | |
""" | """ | |
<< | >> | |
~<sigil-name> | < | > |
~<sigil-name> | / | / |
~<sigil-name> | | | | |
All delimiters that are auto-inserted are also matched for highlighting
| Left | Right |
|---|---|
do | end |
fn | end |
[ | ] |
{ | } |
( | ) |
' | ' |
''' | ''' |
" | " |
""" | """ |
<< | >> |
< | > |
/ | / |
| | | |
You'll be presented with a "Run 'Dialyzer based inspections (Elixir)'" dialog
%20Whole%20Project.png?raw=true)
The Inspections Result Tool Pane will open and show results as each file is processed.
Click the ▶ to expand the Credo section to show all warnings

Click an entry for the details of an individual warning with a code highlighting.

Any file with .eex as the final extension will be treated as Embedded Elixir (EEx) templates. To determine the Template Data Language, the .eex extension will be stripped and any remaining extension will be looked up to get the File Type and its associated Language. For example, *.txt.eex will be EEx with Plain Text (.txt) as the Data Template Language. Likewise, *.html.eex will be EEx with HTML as the Data Template Language. There's no need to register *.txt.eex or *.html.eex or any other *.DATA_TEMPLATE_LANGUAGE_EXTENSION.eex pattern explicitly: the nested extension will be looked up using the normal extension setup.


If you need more file-by-file configuration of the Template Data Language than can be achieved with a file extension/pattern, IntelliJ IDEA (Community or Ultimate Edition) has support for setting the Template Data Language on a specific path.
See JetBrains Documentation for more details.
Any file with .leex as the final extension will be treated as Live Embedded Elixir (LEEx) templates. To determine the Template Data Language, the .leex extension will be stripped and any remaining extension will be looked up to get the File Type and its associated Language. For example, *.txt.leex will be EEx with Plain Text (.txt) as the Data Template Language. Likewise, *.html.leex will be EEx with HTML as the Data Template Language. There's no need to register *.txt.leex or *.html.leex or any other *.DATA_TEMPLATE_LANGUAGE_EXTENSION.leex pattern explicitly: the nested extension will be looked up using the normal extension setup.

If you need more file-by-file configuration of the Template Data Language than can be achieved with a file extension/pattern, IntelliJ IDEA (Community or Ultimate Edition) has support for setting the Template Data Language on a specific path.
See JetBrains Documentation for more details.
Any file with .heex as the final extension will be treated as HEEx - Phoenix LiveView's HTML-aware, component-friendly template language. Unlike EEx/LEEx, the Template Data Language is always HTML; it isn't guessed from the rest of the file name, since HEEx's own compiler (Phoenix.LiveView.HTMLEngine) always renders to HTML.
~H sigils in .ex files use the same HEEx language, with separate HTML and Elixir PSI for the sigil's contents, once enabled - see ~H Sigil HEEx Language Injection Support below.
<.component> and <Module.component> tags navigate (Go To Declaration) to the def/defp that defines them, whether the component is defined in the same view module, brought in with an explicit import, or brought in via use MyAppWeb, :html (including a use nested inside it, such as use Phoenix.Component). Find Usages and Rename on the def/defp include its tags, Rename also works with the caret on the tag, and Quick Docs on a tag shows the function's documentation. All of this applies in .heex files and inside ~H sigils. A dotted name that is not valid component syntax is flagged the same way an unknown HTML tag is.
HEEx's special attributes (:let, :if, :for, :key, :type) and phx-* bindings are recognized on HTML tags, and <:slot> tags are not validated as HTML elements.

mix compile instead of elixirc directly)--no-docs elixirc flag)--no-debug-info elixirc flag)--warnings-as-errors elixirc flag)--ignore-module-conflict elixirc flag)
If a file has errors and warnings, they are group together in Build Messages under that file.

You can jump to errors and warnings in the Build Messages
Highlight the error or warning you want to jump to source
Do one of the following
Right-Click the error or warning
Select Jump to Source from the context menu

OR
You can also turn on Autoscroll to Source, which will Jump To Source whenever you Click or select an error or warning.


If you enable Warnings as Errors in the settings, then the Warnings will be treated as Errors by elixirc and mix and the Build Messages will show the Warnings as Errors.

If only warnings remain in the source.

With Warnings as Errors On, all the Warnings will appear as Errors and still fail the build

With Warnings as Errors Off, the Warnings will appear as Warnings and the build will succeed




Live Templates are snippets of code that can be inserted quickly and have placeholder locations that the cursor will automatically jump to when using the template. Whenever you start typing, Live Templates will start matching against the shortcuts. A template can be selected with Tab.
Live Templates can be customized in Preferences > Editor > Live Templates > Elixir.
| Shortcut | Code |
|---|---|
@doc
|
|
case
|
|
cond
|
|
def
|
|
def,
|
|
defi
|
|
defm
|
|
defmac
|
|
defmacp
|
|
defover
|
|
defp
|
|
defpro
|
|
defs
|
|
do
|
|
doc
|
|
fn
|
|
for
|
|
if
|
|
ife
|
|
ii
|
|
mdoc
|
|
rec
|
|
test
|
|
try
|
|
Documentation in @moduledoc, @typedoc or @doc module attribute values are marked as Markdown language, so that the JetBrains supplied Markdown support works in documentation.
Code in code blocks (indented 4 spaces) are marked as Elixir language, so all the normal language support works in code examples and doctests.
This includes highlighting reads and writes of variables

and Go To Definition for calls




Markdown documentation in @moduledoc, @typedoc or @doc can be toggled back and forth from the editable Markdown view and the rendered HTML view.
In the rendered view, aliases are clickable and navigatable.
Clicking the links will show the documentation for module.

Using Go To Definition (Cmd+Click) on the link will take you to the module definition.




You can get documentation for functions and macros that have a @doc, aliases of modules that have a @moduledoc or types that have a @typedoc in a pop-up using Quick Documentation.
Place the cursor over the name of callback being referenced.
Display the documentation with either:
@doc
@doc@moduledocPlace the cursor over the name of a type being used.
Display the documentation with either:
@typedoc
Quick Documentation also works for Erlang modules and functions referenced via atom syntax
(e.g. :math, :lists.any/2). The plugin reads documentation from the EEP-48 Docs chunk
embedded in .beam files, or from external .chunk files at <app>/doc/chunks/<module>.chunk.
For this to work, your Erlang/OTP installation must have been built with documentation.
Checking if docs are available:
If hovering over :lists or :math shows "No Documentation found", your OTP was built without
docs. Many mise/asdf/kerl installations omit docs by default.
Installing Erlang with documentation via mise:
KERL_BUILD_DOCS=yes MISE_ERLANG_COMPILE=true mise install erlang@X.Y.Z
Replace X.Y.Z with your desired OTP version (e.g. 27.3.3). The key variables are:
KERL_BUILD_DOCS=yes -- tells kerl to build documentation (including .chunk files)MISE_ERLANG_COMPILE=true -- forces a source build (pre-built binaries may not include docs)Installing Erlang with documentation via asdf:
KERL_BUILD_DOCS=yes asdf install erlang X.Y.Z
After reinstalling Erlang with docs, restart the IDE so it re-indexes the .beam files.
OTP 23 and earlier: You may need to add
CFLAGS="-O2 -g -Wno-error=implicit-function-declaration"to the install command to avoid compilation errors on newer toolchains.
Distillery's mix release produces a CLI for running the release.
Build the release: mix release
==> Release successfully built!
You can run it in one of the following ways:
Interactive: _build/ENV/rel/NAME/bin/NAME console
Foreground: _build/ENV/rel/NAME/bin/NAME foreground
Daemon: _build/ENV/rel/NAME/bin/NAME start
Run > Edit Configurations...

Click +
Select "Distillery Release CLI"

Fill in the "Release CLI Path" with the full path to the _build/ENV/rel/NAME/bin/NAME path produed by mix release above.
Fill in the "Release CLI arguments".
console runs a shell with the release loaded similar to iex -S mix.foreground to runs the release without a shell, like mix or mix run.
The available commands are controlled by your release config rel/config.exs that Distillery uses.(Optionally) fill in "erl arguments" with arguments to erl before it runs elixir.
This is the same as the ERL_OPTS environment variable supported by Distillery.
(Optionally) fill in "elixir -extra arguments" with arguments to pass to elixir before it run the release.
This is the same as the EXTRA_OPTS environment variable supported by Distillery.
(Optionally) change the Code Loading Mode
This is the same as the CODE_LOADING_MODE environment variable supported by Distillery.
rel/config.exs. Don't set CODE_LOADING_MODE environment variable.embedded - load all code immediately on boot. Set CODE_LOADING_MODE=embedded.interactive - load code on-demand as it is needed/referenced. Set CODE_LOADING_MODE=interactive.(Optionally) set the "Log Directory"
This is the same as the RUNNER_LOG_DIR environment variable supported by Distillery.
(Optionally) change "Replace OS Vars"
This is the same as the REPLACE_OS_VARS environment variable supported by Distillery.
rel/config.exs. Don't set REPLACE_OS_VARS environment variable.false - don't replace "${A_VAR_NAME}" in the generated configuration with A_VAR environment variable at runtime. Set REPLACE_OS_VARS=false.true - replace "${A_VAR_NAME}" in the generated configuration with A_VAR environment variable at runtime. Set REPLACE_OS_VARS=true.(Optionally) set "sys.config File"
This is the same the SYS_CONFIG_PATH environment variable supported by Distillery.
(Optionally) set "Release Config Directory".
This is the same as the RELEASE_CONFIG_DIR environment variable supported by Distillery.
(Optionally) set "Pipe directory".
This is the same as the PIPE_DIR environment variable supported by Distillery.
(Optionally) set "Use Pseudo-terminal (PTY).
If checked use PTY for interactive shells. Automatically on when "Release CLI Arguments" starts with one of the known interactive commands (attach, console, console_boot, console_clean, or remote_console).
Fill in the "Working directory.
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
_build/ENV/rel/NAME/bin/NAMEiex commands.:debugger application to your release
rel/config.exsrelease NAME block, in the set :applications block add :debugger:
--- a/rel/config.exs
+++ b/rel/config.exs
@@ -41,6 +41,8 @@ end
release :intellij_elixir do
set version: current_version(:intellij_elixir)
set applications: [
+ # needed for IntelliJ Elixir debugger
+ :debugger,
:runtime_tools
]
end
mix testsAlthough it is exceedingly rare, as most Elixir projects use mix, it is supported to run/debug elixir directly, such as when doing elixir script.exs.
Run > Edit Configurations...

Click +
Select "Elixir"

Fill in the "elixir arguments".
(Optionally) fill in "erl arguments" with arguments to erl before it runs elixir.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug elixir
Click the Run arrow in the Toolbar to run elixir.

The Run pane will open, showing the results of elixir.
elixiriex run configurations allow you to run iex with IntelliJ Elixir attached. It is most useful when debugging, but it also allows you save customizations in the configuration when it is more complicated than just iex.
Run > Edit Configurations...

Click +
Select "IEx"

(Optionally) fill in "iex arguments" with arguments to iex.
(Optionally) full in "erl arguments" with arguments to erl before it runs iex.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug the iex configuration.
Click the Run arrow in the Toolbar to run iex

iex.Much like rake tasks in Rubymine, this plugin can run mix tasks.
Run > Edit Configurations...

Click +
Select "Elixir Mix"

Fill in the "mix arguments" starting with the name of the mix task followed by any arguments to that task.
(Optionally) fill in "elixir arguments" with arguments to elixir before it runs mix.
(Optionally) fill in "erl arguments" with arguments to erl before it runs elixir.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug the Mix Task
Click the Run arrow in the Toolbar to run the mix task

The Run pane will open, showing the results of the mix task.
If there is an error with a FILE:LINE stack stack_frame, it will be a clickable link that will take you to that location

mix taskIf you want to run iex in the context of the project, you need to run iex -S mix, but if you don't want to have to worry about forgetting whether it's -s or -S or if it is mix -S iex or iex -S mix, you can use an IEx Mix configuration.
Run > Edit Configurations...

Click +
Select "IEx Mix"

(Optionally) fill in "mix arguments", such as phx.server if you want to launch Phoenix inside of iex.
(Optionally) fill in "iex arguments" with arguments to iex before -S mix.
(Optionally) full in "erl arguments" with arguments to erl before it runs iex.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug iex -S mix
iex -S mixiex -S mix.mix espec The mix espec task gets a special type of Run Configuration, Elixir Mix Espec. Using this Run Configuration type instead, of the basic Elixir Mix Run Configuration will cause the IDE to attach a special formatter to mix espec, so that you get the standard graphical tree of Test Results.
The Run pane will show Test Results. If there is a compilation error before or during mix espec, it will be shown as a test failure. If the compilation failure is in a _spec.exs file can it can be inferred from the stacktrace, the compilation error will show up as a test failure in that specific module.
If you override the default formatters you will need to add the following code to your spec_helper.exs.
If you override formatters similar to below
ESpec.configure fn(config) ->
config.formatters ...
ESpec.configure fn(config) ->
config.formatters [
{ESpec.Formatters.Json, %{out_path: "results.json"}},
{ESpec.Formatters.Html, %{out_path: "results.html"}},
{ESpec.Formatters.Doc, %{details: true, out_path: "results.txt"}},
{ESpec.Formatters.Doc, %{details: true, diff_enabled?: false, out_path: "results-no-diff.txt"}},
{ESpec.CustomFormatter, %{a: 1, b: 2}},
]
end
Replace them with code that checks for the graphical formatter TeamCityESpecFormatter and uses only it when available.
ESpec.configure fn(config) ->
config.formatters(if Code.ensure_loaded?(TeamCityESpecFormatter) do
[{TeamCityESpecFormatter, %{}}]
else
...
end)
end
ESpec.configure fn(config) ->
config.formatters(if Code.ensure_loaded?(TeamCityESpecFormatter) do
[{TeamCityESpecFormatter, %{}}]
else
[
{ESpec.Formatters.Json, %{out_path: "results.json"}},
{ESpec.Formatters.Html, %{out_path: "results.html"}},
{ESpec.Formatters.Doc, %{details: true, out_path: "results.txt"}},
{ESpec.Formatters.Doc, %{details: true, diff_enabled?: false, out_path: "results-no-diff.txt"}},
{ESpec.CustomFormatter, %{a: 1, b: 2}},
]
end)
end
mix espce Run Configurations ManuallyRun > Edit Configurations...

Click +
Select "Elixir Mix ESpec"

Fill in the "mix espec arguments" with the argument(s) to pass to mix espec. Normally, this will be list of *_spec.exs files, relative to the "Working directory".
NOTE: Unlike mix test, mix espec does not support directories as arguments.
(Optionally) fill in "elixir arguments" with the arguments to elixir before it runs mix test.
(Optionally) fill in "erl arguments"with the arguments toerlbefore it runselixir`.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined you can either Run or Debug the mix especs
mix test task
mix testsWhile you can create Elixir Mix ESpec run configurations manually using the Run > Edit Configurations... menu, it is probably more convenient to use the context menu.
mix espec Run Configurations from contextThe context menu must know that the the directory, file, or line you are right-clicking is a test. It does this by checking if the current directory or an ancestor is marked as a Test Sources Root and contains or is a *_spec.exs file(s)
espec directory is marked as a Test Sources Rootespec directory is green. If it is, it is likely a Test Sources Root. This color may differ in different themes, so to be sure you can check the context menutest directory.If "Unmark as Test Sources Root" is shown, then the directory is already configured correctly, and create from context will work.

If "Test Sources Root" is shown, then the directory need to be configured by clicking that entry

mix espec Run Configurations from directoryRight-click the directory in the Project pane
Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.

Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.mix espec Run Configurations from fileAlternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.Finally, you can use the editor tabs
Right-click the editor tab for the test file you want to run

Click "Run Mix ESpec", which will both create the Run Configuration and Run it.
mix espec Run Configurations from lineIf you want to be able to run a single test, you can create a Run Configuration for a line in that test
Right-click a line in the test file

Click "Run Mix ESpec", which will both create the Run Configuration and Run it.
Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.mix test The mix test task gets a special type of Run Configuration, Elixir Mix ExUnit. Using this Run Configuration type instead, of the basic Elixir Mix Run Configuration will cause the IDE to attach a special formatter to mix test, so that you get the standard graphical tree of Test Results
The Run pane will show Test Results. If there is a compilation error before or during mix test, it will be shown as a test failure. If the compilation failure is in a _test.exs file can it can be inferred from the stacktrace, the compilation error will show up as a test failure in that specific module.
doctest names are rearranged to emphasize the function being tested: "test doc at MODULE.FUNCTION/ARITY (COUNT)" becomes "MODULE.FUNCTION/ARITY doc (COUNT)". If MODULE is the same as the test case without the Test suffix, then MODULE is stripped too and the test name becomes only FUNCTION/ARITY doc (COUNT).
mix test Run Configurations ManuallyRun > Edit Configurations...

Click +
Select "Elixir Mix ExUnit"

Fill in the "mix test arguments" with the argument(s) to pass to mix test. Normally, this will be a directory like test, relative to the "Working directory"
(Optionally) fill in "elixir arguments" with the arguments to elixir before it runs mix test.
(Optionally) fill in "erl arguments"with the arguments toerlbefore it runselixir`.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined you can either Run or Debug the mix tests
mix test task
mix testsWhile you can create Elixir Mix ExUnit run configurations manually using the Run > Edit Configurations... menu, it is probably more convenient to use the context menu.
mix test Run Configurations from contextThe context menu must know that the the directory, file, or line you are right-clicking is a test. It does this by checking if the current directory or an ancestor is marked as a Test Sources Root.
test directory is marked as a Test Sources Roottest directory is green. If it is, it is likely a Test Sources Root. This color may differ in different themes, so to be sure you can check the context menutest directory.If "Unmark as Test Sources Root" is shown, then the directory is already configured correctly, and create from context will work.

If "Test Sources Root" is shown, then the directory need to be configured by clicking that entry

mix test Run Configurations from directoryRight-click the directory in the Project pane
Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.

Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.mix test Run Configurations from fileAlternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.Finally, you can use the editor tabs
Right-click the editor tab for the test file you want to run

Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.
mix test Run Configurations from lineIf you want to be able to run a single test, you can create a Run Configuration for a line in that test
Right-click a line in the test file

Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.
Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it..beam Files.beam files are the compiled version of modules on the BEAM virtual machine used by Elixir and Erlang. They are the equivalent of .class files in Java.
.beam files are not detected purely by their file extension: the BEAM file format starts with a magic number, FOR1, that is checked for before decompiling.
.beam files have 2 editors registered: decompiled Text and BEAM Chunks

If the .beam module was compiled with the compressed compiler directive, which in Erlang looks like
-compile([compressed])
and in Elixir looks like
@compile [:compressed]
then the outer file format is GZip (which is detected by checking for the gzip magic number, 1f 8b, at the start of the file) and the .beam will be (stream) decompressed before the .beam header is checked and the chunks decoded.
.beam files are composed of binary chunks. Each chunk is formatted
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | Name (ASCII Characters) | |||
| 4 | Length (`unsigned-big-integer`) | |||
| 8+ | Chunk-Specific | |||
This format is generically referred to as Type-Length-Value
The BEAM Chunks editor tab is subdivided into further tabs, one for each chunk in the .beam file.

The tabs are listed in the order that the chunks occur in the .beam file.
Atom / AtU8The Atom chunk holds LATIN-1 encoded atoms while AtU8 chunk holds UTF-8 atoms. There will only be one of these atom-related chunks in any given .beam file. AtU8 is used in newer versions of OTP that support UTF-8 atoms. AtU8 was introduced in OTP 20.
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | atom count (`unsigned-big-integer`) | |||
| 4 | length1 (`unsigned-byte`) | bytes (for length1) | ||
| 4+length1+...+lengthn-1 | lengthn (`unsigned-byte`) | bytes (for lengthn) | ||
The Atom/AtU8 tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Index | 1-based to match Erlang convention. In the Code chunk, atom(0) is reserved to always translate to nil | Derived |
| Byte Count | The byte count for the atom's bytes | Raw |
| Characters | From encoding the bytes as LATIN-1 for Atom chunk or UTF-8 for AtU8 chunk | Derived |

AttrThe Attr chunk holds the module attributes, but only those that are persisted. Erlang module attributes are persisted by default, but in Elixir module attributes need to be marked as persisted with Module.register_attribute/3
The Attr chunk uses External Term Format (term_to_binary's output) to encode a proplist, which is similar to, but not quite the same an Elixir Keyword list
All modules will have a :vsn attribute that is either set explicitly or defaults to the MD5 of the module.
The Attr tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Key | Attribute name | Raw |
| Value | Attribute value. Note: The value always appears as a list as read from the binary format. I don't know why. | Raw |

CInfThe CInf chunk is the Compilation Information for the Erlang or Erlang Core compiler. Even Elixir modules have it because Elixir code passes through this part of the Erlang Core compiler
The CInf chunk uses External Term Format (term_to_binary's output) to encode a proplist, which is similar to, but not quite the same an Elixir Keyword list
The CInf tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Key | Option name | Raw |
| Value | Inspected value | Raw |

CodeThe Code chunk contains the byte code for the module.
It is encoded in BEAM Compact Term Encoding, which differs from the binary format produced by term_to_binary.
The Code tab shows a read-only editor with one byte code operation on each line. For ease of reading, operations are grouped by function and then label block with indentation indicating scope.

By default as many references to other chunks and references to other parts of Code chunk are inlined to ease understanding. If you want to see the raw byte code operations, you can turn off the various inliners.
####### Controls
| Control | On | Off |
|---|---|---|
| Inline Atoms |
atom(0) is inlined as nil
|
atom(N) if "Inline Integers" is Off
|
atom(n) looks up index `n` in `Atom`/`AtU8` chunk and inlines its `inspect`ed version
|
N if "Inline Integers" is On and the argument supports "Inline Integers"
| |
| Inline Functions |
literal(n) looks up index n in FunT chunk and inlines the name if the argument supports "Inline Functions"
|
literal(n) if "Inline Integers" is Off
|
n if "Inline Integers" is On and the argument supports "Inline Integers"
| ||
| Inline Imports |
literal(n) looks up index n in ImpT and inlines it as a function reference: &module.name/arity if argument supports "Inline Functions"
|
literal(n) if "Inline Integers" Is Off
|
n if "Inline Integers" is On and the argument supports "Inline Integers"
| ||
| Inline Integers | atom(n) and literal(n) inline as n if argument supports "Inline Integers" | atom(n), integer(n), and literal(n) |
integer(n) inlines as n | ||
| Inline Labels | label(n) inlines as n if argument supports "Inline Labels" | label(n) |
| Inline Lines | line(literal(n)) looks up index `n` in the "Line Reference" table in the `Lines` chunk. The Line Reference contains a file name index and line. The file name index is looked up in the "File Name" table in the `Lines` chunk. The line from the Line Reference and the File name from the "File Name" table are inlined as `line(file_name: file_name, line: line)`. | line operations are left as is |
| Inline Literals | literal(n) looks up index n in LitT chunk and inlines its `inspect`ed version if the argument supports "Inline Literals" | literal(n) |
| Inline Local Calls | label(n) finds label(n) in Code chunk, then searches back for the previous func_info operation, then inlines it as a function reference: &module.name/arity if argument supports "Inline Local Calls" | label(n) |
| Inline Strings | Looks up bit_length and byte_offset into `StrT` chunk as their CharList value if supported by operation as value to string argument name | bit_length and byte_offsetarguments are left as is |
| Show Argument Names | Adds keyword argument names before each argument value | Leaves values as positional arguments |
If any of the inliners are incorrect or you have an argument name that makes more sense, please open an issue.
DbgiThe Dbgi chunk contains Debug Info. It was introduced in OTP 20 as a replacement for the Abst chunk. While the Abst chunk was required to contain the Erlang AST, the Dbgi format can contain the debug info for other languages, such as Elixir quoted form AST.
Because the format is language neutral, the format is a set of nested, versioned formats. The outer most layer is
{:debug_info_v1, backend, metadata | :none}
For :debug_info_v1, Elixir's backend is :elixir_erl. The metadata for :elixir_erl is further versioned: {:elixir_v1, map, specs}.
map contains the bulk of the data.
| Key | Value |
|---|---|
:attributes | Attributes similar to the Attr chunk, but at the Elixir, instead of Core Erlang level. Usually they match with the exception that attributes doesn't contain vsn when Attr contains the MD5 version |
:compile_opts | Compilation options similar to CInf chunk's options key, but at for Elixir, instead of Core Erlang level. |
:definitions | The Elixir quoted AST for reach function clause. |
:file | The name of the file the module was generated from. |
:line | The line in :file where the module was defined, such as the line defmodule occurred. |
:module | The name of the module as an atom |
:unreachable | Unreachable functions |
The Dbgi tab appearance varies based on whether it was created with Erlang or Elixir, reflecting the fact that the Dbgi format is dependent on the backend that wrote it.
####### Elixir (:elixir_erl backend)
The Dbgi tab show the single value map entries: :file, :line, and :module.

For the multi-value keys: :attributes, :compile_opts, and :definitions, there are individual tabs.

######## Attributes
The Attributes tab has the same format as the Attrs chunk.

######## Compile Options
The Compile Options tab is usually empty, much like the CInf options key for Erlang.

######## Definitions
The Definitions tab is split between a tree of Module, Function/Arity and clauses.

Clicking on a clause will show only that clause, but clicking on a higher level in the tree will show all clauses in the function or the entire Module.

The AST stored in the definitions tab and the process of converting it back to code is not format preserves, so it will not look precisely like the source code as the AST has undergone some macro expansion before its put in the Dbgi chunk. As common idioms are understood, reversals will be add to the renderer.
######## Type Specifications
The Type Specifications tab is split between a tree of the Module, Module Attribute, and type specifications.

Clicking on a type specification will show only that type specification, but clicking on a higher in the tree will show all type specifications for the same module attribute or the entire Module.

####### Erlang (:erlang_abstract_code backend)
The Dbgi tab has Abstract Code and Compile Options tabs.

######## Abstract Code
The Abstract Code tab is split between a tree of Attributes, Functions, Function/Arity, and clauses.

Clicking on a clause will show only that clause, but clicking on a higher level in the tree will show all clauses in the function or the entire Module.

The abstract code stored in the :erlang_abstract_code backend format is the Erlang Abstract Format. Instead of converting the Erlang Abstract Format back to Erlang, which would require IntelliJ Erlang to highlight and annotate and for you to be used to reading Erlang, the Erlang Abstract Format is translated back to Elixir. Using the BEAM Chunk Dbgi viewer can be a way to convert compiled Erlang code to Elixir source automatically.
ExDcThe ExDc chunk stores ExDoc. Not the rendered HTML from the ex_doc package, but the the @doc, @moduledoc, and @typedoc attribute values that work even without ex_doc installed. This chunk is what is consulted when the h helper is used in iex.
The ExDc chunk is the encoded with term_to_binary. The term format is a versioned as {version, versioned_format}. The current version tag is :elixir_docs_v1 and the versioned_format is a Keyword.t with keys matching the Code.get_docs/2 tags :callback_docs, :docs, :moduledoc, and :type_docs keys.
Like Dbgi, the ExDc tab is split between a tree to navigate and an editor to show the decompiled value.

Click on a node in the tree will show all docs at that level and any descendants.
| Node | Description |
|---|---|
| Root | All docs |
| Module | @moduledoc |
| Types | All @typedocs |
| Types child | A specific @typedoc |
| Callbacks | All @callback @docs |
| Callbacks child | A specific @callback's @doc |
| Functions/Macros | All @docs for functions/macros |
| Functions/Macros child | A specific function/macro's @doc |
ExpTThe ExpT chunk is the Export Table. The name "Export" derives from the Erlang module attribute -export, which is used to "export" functions from a module. It is the equivalent of making a function or macro public with def and defmacro as opposed to making it private with defp and defmacrop in Elixir.
The BEAM format and the ExpT chunk, being made for Erlang, has no concept of macros. It only understands functions, so Elixir macros, like __using__/1 called by use are compiled to plain Erlang functions with MACRO- prefixed to their name and an extra argument (the __CALLER__ environment) as the first argument, which increases the arity, yielding a full MFA of MACRO-__using__/2 as seen above.
The ExpT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Atom Index | Index into the Atom or AtU8 chunk for the function's name | Raw |
| Name | The atom referenced by "Atom Index" | Derived |
| Arity | The arity (argument count) of the function | Raw |
| Label | Label index in the Code chunk where the function is defined. This label is usually immediately after the func_info operation and before the first pattern match or guard operation. | Raw |

ImpTThe ImpT chunk is the Import Table. It DOES NOT encode just the Erlang -import attributes or Elixir import macro calls: it tracks any external function or macro called from another module. call_ext_* operations in the Code chunk don't store the Module and Function (MF) of the function they will call directly in the bytecode, instead, one of the arguments is an index into the ImpT chunk. This way, all external calls are normalized into the ImpT chunk instead of being denormalized to the call site. The arity still appears at the call site to help with checking the argument count.
You may notice that erlang.byte_size/1 is included in the table. This is because even BIFs are referenced by MFA and not a pre-assigned number as would be the case for system calls in operating systems like Linux. BEAM is like an Operation System, but not in all ways.
The ImpT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Index | 0-based index used by references in the Code chunk. | Derived |
| Module Atom Index | Index into the Atom or AtU8 chunk for the Module's name | Raw |
| Module Atom | The atom referenced by "Module Atom Index". | Derived |
| Function Atom Index | Index into the Atom or AtU8 chunk for the functon's name | Raw |
| Function Atom | The atom referened by "Function Atom Index". | Derived |

LitTThe LitT chunk contains literals loaded as arguments in Code chunk.
Confusingly, in the Code chunk sometimes the literal(N) term is used to encode integer N, an index into another chunk, or an actual index into the LitT. How literal terms are handled is completely dependent on the specific operation, so without having outside knowledge about the bytecode operation arguments for BEAM, the best way to figure out if literal terms are an integer or an index is to toggle the various controls in the Code tab to see if literal with no inlining turns into a LitT literal, FunT function reference, ImpT function reference, or integer.
The LitT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| # | 0-based index used by references in the Code chunk. | Derived |
| Term | The equivalent of raw |> binary_to_term() |> inspect() | Raw |

LineThe Line chunk encodes both the file name and line number for each line(literal(n)) operation in the Code chunk. The n in line(literal(n)) is an index in to the Line References table in the Line chunk. This is used in Phoenix view modules to show where code from templates comes from.
The Line chunk is composed of 2 subsections: (1) Line References and (2) File Names. First there is a header setting up the number of each entry to expect.
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | emulator version (`unsigned-big-integer`) | |||
| 4 | flags (`unsigned-big-integer`) | |||
| 8 | Line Instruction Count (`unsigned-big-integer`) | |||
| 12 | Line Reference Count (`unsigned-big-integer`) | |||
| 16 | File Name Count (`unsigned-big-integer`) | |||
####### Line References
This uses the Compact Term Format used for the Code chunk. The format ends up producing {file_name_index, line} pairs using the following algorithm:
| Term | Interpretation |
|---|---|
atom(n) | Change file_name_index to n |
integer(n) | Add {file_name_index, n} to end of Line References |
####### File Names
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | Byte Count (`unsigned-big-integer`) | Bytes | ||
The Line tab has one subtab for each subsection in the tab. Each subsection has its own table.

LocTThe LocT chunk is the dual to the ExpT chunk: it contains all private functions and macros.
You'll notice entries like -__struct__/1-fun-0-, starts with - and have a / suffix with fun in it. This naming scheme is used for anonymous functions such as those defined with fn or the capture operator (&) in Elixir. Much like how macros don't really exist and use a MACRO- suffix, anonymous functions/lambdas don't exist, and instead use a distinct naming scheme -<PARENT_FUNCTION>/*fun*. Unlike MACRO-, which is an Elixir invention, anonymous functions/lambdas really being local named functions with derived names is also done in pure Erlang modules. Erlang's anonymous functions are defined with fun, which is where the fun part of the naming scheme comes from.
The LocT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Atom Index | Index into the Atom or AtU8 chunk for the function's name | Raw |
| Name | The atom referenced by "Atom Index" | Derived |
| Arity | The arity (argument count) of the function | Raw |
| Label | Label index in the Code chunk where the function is defined. This label is usually immediately after the func_info operation and before the first pattern match or guard operation. | Raw |

StrTThe StrT chunk contains all Erlang strings (that is, Elixir charlists) used in the Code chunk.
The StrT chunk contains a single contiguous pool. These strings are used for byte code operations like bs_put_string. Not all strings appear in StrT. Some strings, including most Elixir strings (Erlang binaries) appear in the LitT chunk that holds literals. I'm not sure how the compiler determines whether to use StrT or LitT. I think it all depends on the byte code operation.
Instead of encoding the start and length of each string in the chunk itself, the start and length for any given string is passed as arguments to the byte code operations in the Code chunk. By doing this, shared substrings can be efficiently encoded in StrT.

.beam files, such as those in the Elixir SDK, the Elixir SDK's Internal Erlang SDK, and in your project's build directory will be decompiled to equivalent def and defmacro calls. The bodies will not be decompiled, only the call definition head and placeholder parameters. These decompiled call definition heads are enough to allow Go To Declaration, the Structure pane, and Completion to work with the decompiled .beam files.
It turns out that in the .beam binary format there are no macros. This makes sense since the BEAM format was made for Erlang, which does not have macros, and only has functions. Elixir marks its macros in the compiled .beam by prefixing them with MACRO-.
There are 2 chunks in the BEAM format for function references: ExpT, which is for exports (because in Erlang module they are from -export), which are the public functions and macros; and LocT, which is for locals (anything not exported in Erlang), which are private functions and macros.
| BEAM Chunk | Atom Prefix | Macro |
|---|---|---|
ExpT | MACRO- | defmacro |
ExpT | N/A | def |
LocT | MACRO- | defmacrop |
LocT | N/A | defp |
defp with / in nameMuch like there are no macros in BEAM, there are no anonymous functions either. Any anonymous function (using fn in Elixir or fun in Erlang) ends up being a named function in the LocT chunk. Anonymous functions names start with -, then the parent function's name, a / and a unique number for that scope.
As an example, Kernel has
defp unquote(:"-MACRO-binding/2-fun-0-")(p0, p1, p2, p3) do
# body not decompiled
end
which is generated from the for in
defmacro binding(context \\ nil) do
in_match? = Macro.Env.in_match?(__CALLER__)
for {v, c} <- __CALLER__.vars, c == context do
{v, wrap_binding(in_match?, {v, [generated: true], c})}
end
end
Functions and macros can have names that aren't valid identifier names, so the decompiler has special handlers to detect these invalid identifiers and escape them to make decompiled code that is parsable as valid Elixir.
| Handler | Name/Arity | Decompiled | Reason |
| Infix Operator | !=/2 | left != right | Infix operators are defined in infix position |
!==/2 | left !== right | ||
&&/2 | left && right | ||
&&&/2 | left &&& right | ||
*/2 | left * right | ||
=+/2 | left + right | ||
=++/2 | left ++ right | ||
-/2 | left - right | ||
--/2 | left -- right | ||
->/2 | left -> right | ||
../2 | left .. right | ||
//2 | left / right | ||
::/2 | left :: right | ||
</2 | left < right | ||
<-/2 | left <- right | ||
<<</2 | left <<< right | ||
<<~/2 | left <<~ right | ||
<=/2 | left <= right | ||
<>/2 | left <> right | ||
<|>/2 | left <|> right | ||
<~/2 | left <~ right | ||
<~>/2 | left <~> right | ||
=/2 | left = right | ||
==/2 | left == right | ||
===/2 | left === right | ||
=>/2 | left => right | ||
=~/2 | left =~ right | ||
>/2 | left > right | ||
>=/2 | left >= right | ||
>>>/2 | left >>> right | ||
\\/2 | left \\\\ right | ||
^/2 | left ^ right | ||
^^^/2 | left ^^^ right | ||
and/2 | left and right | ||
in/2 | left in right | ||
or/2 | left or right | ||
|>/2 | left |> right | ||
||/2 | left || right | ||
|||/2 | left ||| right | ||
~=/2 | left ~= right | ||
~>/2 | left ~> right | ||
~>>/2 | left ~>> right | ||
| Prefix Operator | +/1 | (+value) | To prevent precedence errors, unary prefix operators, which also have binary infix operators of the same name need to be defined inside parentheses |
-/1 | (-value) | ||
| Unquoted | %/2 | unquote(:%)(p0, p1) | Special forms need to defined as atom passed to unquote, as special forms are handled before macros defining the calls are applied |
%{}/1 | unquote(:%{})(p0) | ||
&/1 | unquote(:&)(p0) | ||
./2 | unquote(:.)(p0, p1) | ||
<<>>/1 | unquote(:<<>>)(p0) | ||
do/n | unquote(:do)(p0, ...) | Keywords need to be escaped | |
fn/1 | unquote(:fn)(p0) | Special forms need to defined as atom passed to unquote, as special forms are handled before macros defining the calls are applied | |
unquote/1 | unquote(:unquote)(p0) | ||
unquote_splicing/1 | unquote(:unquote_splicing)(p0) | ||
{}/n | unquote(:{})(p0, ...) | ||
Capitalized/n | unquote(:Capitalized)(p0, ...) | Part of the Corba libraries in OTP have functions starting with a capital letter, which would be parsed as an Alias in Elixir if not unquoted. | |
#text#/1 | unquote(:"#text#")(p0) |
Part of the XML libraries in OTP have functions
that start with or contain `#`, which would parse as a comment in Elixir if not unquoted in a double quoted
atom.
| |
| Default | name/n | name(p0, ...) | If no specialized handler is required, functions and macros are defined normally with pN for each parameter in the Nth position |
When you start typing an Alias, completion will look in three locations:
alias aliased names in the current file
Suffix for alias Prefix.SuffixMultipleAliasA or MultipleAliasB for alias Prefix.{MultipleAliasA, MultipleAliasB}As for alias Prefix.Suffix, as: AsPrefix.Suffix from defmodule Prefix.SuffixMyProtocol from defprotocol MyProtocolMyProtocol.MyStruct
defimpl MyProtocol, for: MyStructdefimpl MyProtocol nested under defmodule MyStructSuffix.Nested for alias Prefix.Suffix where Prefix.Suffix.Nested is an indexed module, implementation or protocol name.MultipleAliasA.Nested for alias Prefix.{MultipleAliasA, MultipleAliasB} where Prefix.MultipleAliasA.Nested alias Prefix.{MultipleAliasA, MultipleAliasB} is an indexed module, implementation or protocol name.As.Nested for alias Prefix.Suffix, as: As where Prefix.Suffix.Nested is an indexed module, implementation, or protocol name.{ }When you start typing inside { } for alias Prefix.{} or import Prefix.{}, completion will look for nested modules under Prefix and then remove the Prefix., so completion will look like Suffix.
Completion uses the same presentation as Structure, so you can tell whether the name is function/macro (Time), whether it is public/private (Visibility) and the Module where it is defined. Between the icons and the Modules is the name itself, which is highlighted in bold, the parameters for the call definition follow, so that you can preview the patterns required for the different clauses.

Qualified functions and macro calls will complete using those functions and macros defined in the qualifying Module (defmodule), Implementation (defimpl) or Protocol (defprotocol). Completion starts as shown as . is typed after a qualifying Alias.
Function and macro calls that are unqualified are completed from the index of all function and macro definitions, both public and priva
Truncated — view the full README on GitHub.
(top 30 of 56)
Kotlin
47.2%
Elixir
32.9%
Java
18.3%
Elixir plugin for JetBrain's IntelliJ Platform (including Rubymine)
1,897
stars
6,370
commits
Kotlin
primary language
Sep 9, 2026
updated
This is a plugin that adds support for Elixir to JetBrains IDEs.
The plugin works both in the rich IDEs that allow alternative language SDK selection and small IDEs that are language specific. The rich IDEs work best for IntelliJ Elixir because only in the rich IDEs can have an Elixir SDK set as the Project SDK. In all small IDEs, the native language SDK is always there, which makes anything that uses the SDK, such as running elixir, erl, or mix more complicated both internally and externally in the configuration you have to setup.
The plugin is free to use in all JetBrains IDEs. The Cost column in the below table is what JetBrains charges for the IDE itself. IntelliJ Elixir is maintained by @KronicDeth who does not get any of the subscription money. If you want to support the plugin itself, make a donation.
| IDE | Rich/Small | Languages | Cost | Trial | License | Source |
|---|---|---|---|---|---|---|
| IntelliJ IDEA Community Edition | Rich | Java | Free | N/A | Apache 2.0 | JetBrains/intellij-community |
| IntelliJ IDEA Ultimate Edition | Rich | Java | Subscription | 30-days | Commercial | N/A |
| AppCode | Small | Objective-C | Subscription | 30-days | Commercial | N/A |
| CLion | Small | C/C++ | Subscription | 30-days | Commercial | N/A |
| DataGrip | Small | SQL | Subscription | 30-days | Commercial | N/A |
| GoLand | Small | Go | Subscription | 30-days | Commercial | N/A |
| PHPStorm | Small | PHP | Subscription | 30-days | Commercial | N/A |
| PyCharm Community Edition | Small | Python | Free | N/A | Apache 2.0 | JetBrains/intellij-community subdirectory |
| PyCharm Professional Edition | Small | Python | Subscription | N/A | Commercial | N/A |
| Rider | Small | .NET | Subscription | N/A | Commercial | N/A |
| RubyMine | Small | Ruby | Subscription | 30-days (90-day for whole team) | Commercial | N/A |
| WebStorm | Small | JavaScript | Subscription | 30-days | Commercial | N/A |
Once you have your IDE of choice installed, you can install this plugin
| Feature | Rich | Small | Alternative |
|---|---|---|---|
| Project | Yes | No | 1. Open directory 2. Setup the SDK |
| Project Structure | Automatic | Manual | |
| Project Settings | Yes | No | |
| Module Settings | Yes | No | |
| New Elixir File | Yes | Yes | |
| Syntax Highlighting and Semantic Annotation | Yes | Yes | |
| Grammar Parsing | Yes | Yes | |
| Inspections | Yes | Yes | |
| Quick Fixes | Yes | Yes | |
| Code Folding | Yes | Yes | |
| Commenter | Yes | Yes | |
| Debugger | Yes | Yes | |
| Delimiters | Yes | Yes | |
| Embedded Elixir (EEx) Templates | Yes | Yes | |
| Building/Compiling | Yes | No | Build/compile as part mix run configurations only |
| Live Templates | Yes | Yes | |
| Documentation | Yes | Yes | |
| Run Configurations | Yes | Yes | |
| Completion | Yes | Yes | |
| Decompilation | Yes | Yes | |
| Go To Declaration | Yes | Yes | |
| Formatting | Yes | Yes | |
| Go To Related | Yes | Yes | |
| Go To Symbol | Yes | Yes | |
| Go To Test | Yes | Yes | |
| Go To Test Subject | Yes | Yes | |
| Find Usage | Yes | Yes | |
| Live Embedded Elixir (LEEx) Templates | Yes | Yes | |
| HEEx Templates | Yes | Yes | |
| Refactor | Yes | Yes | |
| SDK | Yes | Yes | |
| Structure | Yes | Yes |
If you've already created a mix project, you can load it as an Elixir project into the plugin.
File > New > Project From Existing Sources...
Select the root directory of your project.
Select "Import project from external model"
Select Mix

Click Next
Select a Project SDK directory by clicking Configure.
The plugin will automatically find the newest version of Elixir installed. (NOTE: SDK detection only works for Linux, homebrew installs on OSX, and Windows. Open an issue with information about Elixir install locations on your operating system and package manager to have SDK detection added for it.)
If the automatic detection doesn't find your Elixir SDK or you want to use an older version, manually select select
the directory above the bin directory containing elixir, elixirc, iex, and mix. (On Windows it is the
directory containing elixir.bat, elixirc.bat, iex.bat, and mix.bat.)
Click Finish after you select SDK name from the Project SDK list.
The "Mix project root" will be filled in with the selected directory.
(Optional) Uncheck "Fetch dependencies with mix" if you don't want to run mix deps.get when importing the project
mix local.hex --force and mix deps.get will be run.Click Next
All directories with mix.exs files will be selected as "Mix projects to import". To import just the main project and not its dependencies, click Unselect All.
Check the box next to the project root to use only its mix.exs. (It will likely be the first checkbox at the top.)
Click Finish
If you've already created a (non-mix) project, you can load it as an Elixir project into the plugin.
mix.exs. Uncheck any project roots that you don't want added.bin directory containing elixir, elixirc, iex, and mix.If you want to create a basic (non-mix) Elixir project with a lib directory, perform the following steps.
File > New > Project

Select Elixir from the project type menu on the left
Click Next

Select a Project SDK directory by clicking Configure.

Select a Project SDK directory by clicking Configure.
The plugin will automatically find the newest version of Elixir installed.
/usr/local/Cellar/elixir)/nix/store)/usr/local/lib/elixir/nix/store)C:\Program Files\Elixir)C:\Program Files (x86)\Elixir)If the automatic detection doesn't find your Elixir SDK or you want to use an older version, manually select select the directory above the bin directory containing elixir, elixirc, iex, and mix. If the bin, lib, or src directory is incorrectly selected, it will be corrected to the parent directory.
Click Next after you select SDK name from the Project SDK list.
Change the Project name to the name your want for the project

(Optionally) change the Project location if the directory does not match what you want
(Optionally) expand More Settings to change the Module name, Content root, Module file location, and/or Project format. The defaults derived from the Project name and Project location should work for most projects.
Click Finish
Choose whether to open in a New Window or in This Window.


.elixir_ls (Elixir Language Server cache)assets/node_modules/phoenix (Phoenix symlink back to deps)assets/node_modules/phoenix_html (Phoenix HTML symlink back to deps)cover (Test coverage output)deps (Dependencies -- re-exposed as Libraries for Go-to-Declaration)doc (Documentation generated by ex_doc)logs (Conventional log directory)libweb (legacy Phoenix layout compatibility)spec (ESpec)test
The Project Settings include
When setting up Elixir and Erlang SDKs for the first time:
The plugin supports running Elixir, Mix, and IEx commands within WSL. This includes:
\\wsl$\Ubuntu\...) and Windows drive paths are automatically converted to POSIX paths when executing commands in WSL./mnt/c drive to access Windows files from WSL, but the performance is terrible.
The Module Settings include Marking directories as

Module paths list the output directories when compiling code in the module. There is a an "Output path" for dev
MIX_ENV and "Test output path" for the test MIX_ENV.

Module dependencies include the SDK and the module's own sources. Mix dependencies from deps/ are automatically detected and registered as IDE Libraries by processes that run in the background and monitor file changes:
deps/ and _build/ (after mix deps.get, mix compile or folder deletions).deps/APP/lib and class roots from _build/ENV/lib/APP/ebin.mix deps.get, try running mix compile to generate _build/ output, then wait a few seconds for the file watcher to pick up changes. You can also use Tools → Elixir → Install Mix Dependencies.The status bar can display an Elixir SDK status widget in the bottom-right corner of the IDE. It shows the current SDK version and warns about configuration problems. It will only display when you have an Elixir file open in the editor.
Enabling the widget:
Widget states:
| Display | Meaning |
|---|---|
Elixir: 1.17.3 | SDK configured correctly |
Elixir: 1.17.3 ! | Classpath issues (missing ebin paths or Erlang SDK classpath entries) |
Elixir: 1.17.3 ⚠ OTP | Elixir SDK was compiled against a different OTP major than the paired Erlang SDK |
Elixir: 1.17.3 ⚙ | Folder marks are suboptimal (e.g., test/ not marked as Test Sources) |
Elixir: SDK Error | Module .iml files reference an SDK that no longer exists |
Elixir: Issues | SDK is invalid or missing its Erlang SDK dependency |
No Elixir SDK | No Elixir SDK configured for the project |
Click the widget to open a popup with quick actions:
mix deps.get for the projectlib/ → Sources, test/ → Test Sources, deps/ → Excluded, etc.) and fix dangling module SDK referencesNotifications: When the widget detects a problem, it fires a notification with actionable buttons:
Notifications auto-expire when the issue is resolved (e.g., after running "Reconfigure Now"). Rapid project structure changes (such as importing multiple modules) are debounced to avoid notification spam.
Reconfigure Elixir Module Setup is also available from Tools → Elixir → Reconfigure Elixir Module Setup and via the action search (Ctrl+Shift+A / ⌘⇧A). It is additive -- it applies missing canonical marks without removing any user-customized source roots or exclusions.
Right-click a directory (such as lib or test in the standard mix new layout)
Select New > Elixir File.

Enter an Alias for the Module name, such as MyModule or MyNamespace.MyModule.
Select a Kind of Elixir File to use a different template.

An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created:
defmodule MyNamespace.MyModule do
@moduledoc false
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. It will have a start/2 function that calls MyNamespace.MyModule.Supervisor.start_link/0.
defmodule MyNamespace.MyModule do
@moduledoc false
use Application
def start(_type, _args) do
MyNamespace.MyModule.Supervisor.start_link()
end
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. It will have a start_link/1 function that calls Supervisor.start_link/0 and init/1 that sets
up the child specs. It assumes a MyWorker child that should be supervised :one_for_one.
defmodule MyNamespace.MyModule.Supervisor do
@moduledoc false
use Supervisor
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg)
end
def init(arg) do
children = [
worker(MyWorker, [arg], restart: :temporary)
]
supervise(children, strategy: :one_for_one)
end
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. It will have a start_link/2 function that calls GenServer.start_link/3 and the minimal
callback implementations for init/1, handle_call/3, and handle_cast/2.
The Elixir use GenServer supplies these callbacks, so this template is for when you want to change the callbacks, but
would like the stubs to get started without having to look them up in the documentation.
defmodule MyNamespace.MyModule do
@moduledoc false
use GenServer
def start_link(state, opts) do
GenServer.start_link(__MODULE__, state, opts)
end
def init(_opts) do
{:ok, %{}}
end
def handle_call(_msg, _from, state) do
{:reply, :ok, state}
end
def handle_cast(_msg, state) do
{:noreply, state}
end
end
An underscored file will be created in an underscored directory lib/my_namespace/my_module.ex) with the given module
name with be created. The minimal callback implementations for init/1, handle_event/2, and handle_call/2,
handle_info/2.
The Elixir use GenEvent supplies these callbacks, so this template is for when you want to change the callbacks, but
would like the stubs to get started without having to look them up in the documentation.
defmodule MyNamespace.MyModule do
@moduledoc false
use GenEvent
# Callbacks
def init(_opts) do
{:ok, %{}}
end
def handle_event(_msg, state) do
{:ok, state}
end
def handle_call(_msg, state) do
{:ok, :ok, state}
end
def handle_info(_msg, state) do
{:ok, state}
end
end
Syntax highlighting of lexer tokens and semantic annotating of parser elements can be customized in in the Color Settings page for Elixir (Preferences > Editor > Color & Fonts > Elixir).
| Text Attribute Key Display Name | Tokens/Elements | Scheme | |||
|---|---|---|---|---|---|
| Default | Darcula | ||||
| Alias | String |
|
| ||
| Atom |
|
|
| ||
| Braces and Operators | Bit |
|
|
| |
| Braces and Operators | Braces |
|
|
| |
| Braces and Operators | Brackets |
|
|
| |
| Braces and Operators | Character Token | ? |
|
| |
| Braces and Operators | Comma | , |
|
| |
| Braces and Operators | Dot | . |
|
| |
| Braces and Operators | Interpolation |
|
|
| |
| Braces and Operators | Maps and Structs | Maps |
|
|
|
| Braces and Operators | Maps and Structs | Maps |
|
|
|
| Braces and Operators | Operation Sign |
|
|
| |
| Braces and Operators | Parentheses |
|
|
| |
| Braces and Operators | Semicolon | ; |
|
| |
| Calls | Function | inspect |
*Only the Italic attribute
|
*Only the Italic attribute
| |
| Calls | Macro | inspect |
*Only the Bold and Italic attributes
|
*Only the Bold and Italic attributes
| |
| Calls | Predefined |
|
*Only the Foreground attribute
|
*Only the Foreground attribute
| |
| Comment | # Numbers |
|
| ||
| Keywords | end |
|
| ||
| Module Attributes | @custom_attr |
|
| ||
| Module Attributes | Documentation | @doc |
|
| |
| Module Attributes | Documentation | Text | Simple module docstring |
|
|
| Module Attributes | Types | Callback | func |
|
|
| Module Attributes | Types | Specification | func |
|
|
| Module Attributes | Types | Type | parameterized |
|
|
| Module Attributes | Types | Type Parameter | type_parameter |
|
|
| Numbers | Base Prefix | Non-Decimal |
|
|
|
| Numbers | Base Prefix | Obsolete Non-Decimal |
|
|
|
| Numbers | Decimal Exponent, Mark, and Separator |
|
|
| |
| Numbers | Digits | Invalid |
|
|
|
| Numbers | Digits | Valid |
|
|
|
| Textual | Character List |
'This is a list'
|
|
| |
| Textual | Escape Sequence |
\x{12}
|
|
| |
| Textual | Sigil |
|
|
| |
| Textual | String |
"Hello world"
|
|
| |
| Variables | Ignored |
_
|
|
| |
| Variables | Parameter |
|
|
| |
| Variables | Variable |
pid
|
|
| |
Built on top of highlighted tokens above, the parser understands the following parts of Elixir grammar as valid or allows the grammar because they contain correctable errors:
())()) and matched expressions.true, false, and nil).do blocks that have either (1) a positional argument and keyword
arguments OR (2) two or more positional arguments with optional keyword arguments..() with either no arguments; a no parentheses arguments expression as an argument; keywords
as an argument; positional argument(s); or positional arguments followed by keywords as arguments.Alias.function, :atom.function, etc) and local function calls (function) with...
Alias.function)Alias.function key: value)Alias.function Inner.function positional, key: value)Alias.function positional, key: value)Alias.function 1 + 2)Alias.function())Alias.function(Inner.function positional, key: value)Alias.function(key: value))Alias.function(positional, key: value))def unquote(variable)(positional))variable[key])function do end)Inspections mark sections of code with warnings and errors. They can be customized from the Preferences > Inspections > Elixir.

Detects when compiler will throw unexpected comma. Parentheses are required to solve ambiguity in nested calls.
Function calls with multiple arguments without parentheses cannot take as arguments functions with multiple arguments
without parentheses because which functional gets which arguments is unclear as in the following example:
outer_function first_outer_argument,
# second argument is another function call without parentheses, but with multiple arguments
inner_function first_inner_argument,
ambiguous_keyword_key: ambiguous_keyword_value
To fix the ambiguity if first_inner_keyword_key: first_inner_keyword_value should be associated, add parentheses
around the inner function's arguments:
# keywords are for inner function
outer_function first_outer_argument
inner_function(
first_inner_argument
ambiguous_keyword_key: ambiguous_keyword_value
)
# keywords are for outer function
outer_function first_outer_argument
inner_function(
first_inner_argument
),
ambiguous_keyword_key: ambiguous_keyword_value
Detects when compiler will throw unexpected parenthesis. If you are making a function call, do not insert spaces in between the function name and the opening parentheses.
Function calls with space between the function name and the parentheses cannot distinguish between function calls with
parentheses, but with an accidental space before the ( and function calls without parentheses where the first
positional argument is in parentheses.
function ()
To fix the ambiguity remove the space or add outer parentheses without the space if the first argument should be ():
# extra space, no arguments to function
function()
# first argument is `()`
function(())
function (key: value)
Keywords inside parentheses is not valid, so the only way to fix this is to remove the space
function(key: value)
function (first_positional, second_positional)
A list of positional arguments in parenthenses is not valid, so the only way to fix this is to remove the space
function(first_positional, second_positional)
:) used in type spec instead of type operator (::)Type specifications separate the name from the definition using ::.
@type name: definition
Replace the : with ::
@type name :: definition
one.(
one,
two positional, key: value,
three
)
Keywords can only appear at the end of an argument list, so either surround the no parentheses expression argument with parentheses, or move the the keywords to the end of the list if it wasn't meant to be a no parentheses expression.
one.(
one
two(positional, key: value),
three
)
OR
one.(
one,
two,
three,
key: value
)
=) used in type spec instead of type operator (::)Type specifications separate the name from the definition using ::.
@type name = definition
Replace the = with ::
@type name :: definition
Quick Fixes are actions IntelliJ can take to change your code to correct errors (accessed with Alt+Enter by default).
: to :: in type specsIf a type specification uses a single : instead of ::, then hit Alt+Enter on the : to change it to :: and fix the type spec.
= to :: in type specsIf a type specification uses = instead of ::, then hit Alt+Enter on the = to change it to :: and fix the type spec.
If a set of parentheses is marked as ambiguous then the space before it can be removed to disambiguate the parentheses with Alt+Enter. (Will vary based on keymap.)
You can collapse (fold) pre-defined regions of your Elixir code to make it easier to quickly scroll through files or hide details you don't care about right now.
| Expanded | Collapsed | Folded By Default? |
|---|---|---|
do end | do: ... | No |
-> and right operand | -> ... | No |
@doc VALUE | @doc "..." | No |
@moduledoc VALUE | @moduledoc "..." | No |
@typedoc VALUE | @typedoc "..." | No |
| alias ALIAS1 alias ALIAS1 | alias ... | Yes |
| import ALIAS1 import ALIAS2 | import ... | Yes |
| require ALIAS1 require ALIAS2 | require ... | Yes |
| use ALIAS1 use ALIAS2 | use ALIAS1 | Yes |
@for | FOR in defimpl PROTOCOL, for: FOR | Yes |
@protocol | PROTOCOL in defimpl PROTOCOL, for: FOR | Yes |
| @MODULE_ATTRIBUTE | VALUE in @MODULE_ATTRIBUTE VALUE | No |
You can comment or uncomment the current line or selected block of source. By selecting a block of source first you can quickly comment out and entire function if you're trying to track down a compiling or testing error that's not giving a helpful line number.
Using the menus
Cmd+/.If you'd like to run mix credo use can run it as a global Inspection.
You'll be presented with a "Run 'Credo'" dialog

deps to "Custom scope"The Inspections Result Tool Pane will open and show results as each file is processed.
Click the ▶ to expand the Credo section to show all warnings

Click an entry for the details of an individual warning with a code highlighting.

The view will show the parts of the file that aren't annotated as collapsed with the discontinuous line number indicating the jumps.
If you click on + collapse markers, you can expand the collapsed sections to see the full context

Or you can hover over the collapsed section to see a tooltip preview of the expansion

| Preferences > Editor > Inspections | Preferences > Editor > Inspections > Credo | Editor | Inspections | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Elixir > Credo | Include Explanation | Highlight | Message | Explanation in tooltip |
mix credo Runs
| Highlight | Message |
mix credo Runs
| Action | ||
| Per File | Per Issue | Working Directory | Inspect Code | Run Inspection By Name | |||||||
| ☑ | ☑ | Yes | Yes | Yes | 1 | 1 | Yes | Yes | 1 | Yes | Yes |
| ☑ | ☐ | Yes | Yes | No | 1 | 0 | Yes | Yes | 1 | Yes | Yes |
| ☐ | ⁿ/ₐ | No | No | No | 0 | 0 | Yes | Yes | 1 | No | Yes |
If you want to limit the performance impact of the credo annotator because mix credo spikes your CPU, you can limit the number of mix credo runs to 1 per open file by disabling the Explanation tooltip
If you don't want the annotator to run at all on open editors, then you can disable the paired inspection
Once the annotator is disabled, you can still run the inspection in batch mode
IntelliJ Elixir allows for graphical debugging of *.ex files using line breakpoints.
*.ex filesAfter you have configured a run configuration for your project, you can launch it in debug mode by pressing Ctrl+D.
| Action | Keyword Shortcut |
|---|---|
| Toggle Breakpoint | Cmd+F8 |
| Resume Program | Alt+Cmd+R |
| Step Over | F8 |
| Step Into | F7 |
| View breakpoint details/all breakpoints | Shift+Cmd+F8 |
By default, the debugger will scan all the load paths and build path for .beam files and the corresponding modules will be interpreted which causes the Module's Erlang abstract code chunk to be interpreted in Erlang instead of the bytecode chunk being executed in the C parts of the BEAM. This interpretation is much slower than execution, so by default all of the Elixir standard library and the common modules installed in Phoenix projects are excluded from being interpreted when the debugger starts. The modules can be still be stepped into or have breakpoints explicitly set.

You can customize these module patterns as an application setting.






If you want to customize the modules to ignore on a per-Run-Configuration basis, you can set an environment variable in the Run Configuration.
| Variable | Example | Description |
|---|---|---|
| INTELLIJ_ELIXIR_DEBUG_BLACKLIST | iconv,some | Excluding modules from debugger |
Notice: If you want non Elixir. module in blacklist, write it with: :. This rule applies only to module atoms.
When a breakpoint is set, the editor displays a breakpoint icon in the gutter area to the left of the affected source code. A breakpoint icon denotes status of a breakpoint, and provides useful information about its type, location, and action.
The icons serve as convenient shortcuts for managing breakpoints. Clicking an icon removes the breakpoint. Successive use of Alt - click on an icon toggles its state between enabled and disabled. The settings of a breakpoint are shown in a tooltip when a mouse pointer hovers over a breakpoint icon in the gutter area of the editor.
| Status | Icon | Description |
|---|---|---|
| Enabled | ![]() | Indicates the debugger will stop at this line when the breakpoint is hit. |
| Disabled | ![]() | Indicates that nothing happens when the breakpoint is hit. |
| Conditionally Disabled | ![]() | This state is assigned to breakpoints when they depend on another breakpoint to be activated. |
When the button
is pressed in the toolbar of the Debug tool window, all the breakpoints in a project are muted, and their icons become grey:
.
To view the list of all breakpoints and their properties, do one of the following:
Shift+Cmd+F8
To view properties of a single breakpoint

To configure actions, suspend policy and dependencies of a breakpoint
Shift+Cmd+F8*DBG* 'Elixir.IntellijElixir.DebugServer' got cast {breakpoint_reached, PID} will appear in the console.A line breakpoint is a breakpoint assigned to a specific line in the source code.
Line breakpoints can be set on executable lines. Comments, declarations and empty lines are not valid locations for the line breakpoints. Line break points can be set in .ex and .eex files.

.eex line breaks will only work on Elixir code that is used in Phoenix view modules.

.eex breakpoints only work if a .beam file using the template's relative can be found. This means that the Phoenix view module .beam file must exist in _build prior to setting a breakpoint. Run the Run Configuration once, before debugging to complete the build if setting a breakpoint does not work.
Cmd+F8Cmd+Down. The caret will be placed at the line marked with the breakpoint in question.When you temporarily disable or enable a breakpoint, its icon changes from
to
and vice versa.
Do one of the following:
Cmd+F8.
on the toolbarCtrl+DOR
Debug quick menu
Ctrl+Alt+DEnterIt takes awhile, once the debugged process is started to configure the debugger in BEAM. To ensure that breakpoints are setup before allow the debugged code to run, the debugger blocks until setup is complete.
The debugged process will wait for the debugger to attach

Breakpoints will be set
The debugger will mark modules to be interpreted
.beam files

.beam files will be interpreted unless they match the Module Filter Pattern

The debugger attaches (so it can receive breakpoint events) and allows the debugged process to continue.

Up or Down to change framesWhen changing frames or jumping to definitions, you can lose track of where the debugger is paused. To get back to the current execution point, do one of the following:
Alt+F10
on the stepping toolbar of the Debug tool window.
Binaries show each byte at the byte's offset.

Bitstrings show each byte with any partial byte annotated with its bitwidth.

Boolean variables are rendered as their value.

Charlists show the integer values because they're treated as lists

Functions don't have literal representation, so the inspect form starting with #Fun<...> is shown

Lists render differently based on whether the list is improper or not. Improper lists show the head and tail while proper lists show their element by offset.

Maps render differently based on the key type. If the map uses all atom keys, the key will equal the value in the nested children while non-atom keys are shown as entries at a specific offset with the key and value. This is done, so that complex keys that have subterms can be expanded or collapsed, which is not possible for the simpler atom rendering.

Floats and integers are rendered as literals.

Pids are broken up into their hidden node, id, and serial`.

Strings show their literal value and unicode is fully supported.

Tuples show their elements at their offsets.

While Elixir allows rebinding variable names, Erlang does not, so when viewed in the Variables pane, rebound variables will have an @VERSION after their name indicating which rebinding of a the variable is.
When stopped at a breakpoint, you can use the Evaluate button (it looks like a simple pocket calculator) to open an editor to type code to be executed in the current stack frame.

The evaluator supports the full syntax.

The result of evaluating the code with be shown as the value of result below the entered "Expression".


Errors in the code will report back as a result tuple with an :EXIT tag. This reflects that the error has crashed the process that was evaluating the code. Thankfully, due to how how the interpreter is written, this does not lose the current stack frame and stepping or other evaluation can continue.
| Action | Icon | Shortcut | Description |
|---|---|---|---|
| Show Execution Point | ![]() | Alt+F10 | Click this button to highlight the current execution point in the editor and show the corresponding stack frame in the Frames pane. |
| Step Over | ![]() | F8 | Click this button to execute the program until the next line in the current function or file, skipping the function referenced at the current execution point (if any). If the current line is the last one in the function, execution steps to the line executed right after this function. |
| Step Into | ![]() | F7 | Click this button to have the debugger step into the function called at the current execution point. |
| Step Out | ![]() | Shift+F8 | Click this button to have the debugger step out of the current function, to the line executed right after it. |
The right-delimiter will be automatically inserted when the left delimiter is typed. In some cases, to prevent false positives, the the delimiter is only completed if when used for sigils.
| Preceded By | Left | Right |
|---|---|---|
do | end | |
fn | end | |
[ | ] | |
{ | } | |
( | ) | |
' | ' | |
''' | ''' | |
" | " | |
""" | """ | |
<< | >> | |
~<sigil-name> | < | > |
~<sigil-name> | / | / |
~<sigil-name> | | | | |
All delimiters that are auto-inserted are also matched for highlighting
| Left | Right |
|---|---|
do | end |
fn | end |
[ | ] |
{ | } |
( | ) |
' | ' |
''' | ''' |
" | " |
""" | """ |
<< | >> |
< | > |
/ | / |
| | | |
You'll be presented with a "Run 'Dialyzer based inspections (Elixir)'" dialog
%20Whole%20Project.png?raw=true)
The Inspections Result Tool Pane will open and show results as each file is processed.
Click the ▶ to expand the Credo section to show all warnings

Click an entry for the details of an individual warning with a code highlighting.

Any file with .eex as the final extension will be treated as Embedded Elixir (EEx) templates. To determine the Template Data Language, the .eex extension will be stripped and any remaining extension will be looked up to get the File Type and its associated Language. For example, *.txt.eex will be EEx with Plain Text (.txt) as the Data Template Language. Likewise, *.html.eex will be EEx with HTML as the Data Template Language. There's no need to register *.txt.eex or *.html.eex or any other *.DATA_TEMPLATE_LANGUAGE_EXTENSION.eex pattern explicitly: the nested extension will be looked up using the normal extension setup.


If you need more file-by-file configuration of the Template Data Language than can be achieved with a file extension/pattern, IntelliJ IDEA (Community or Ultimate Edition) has support for setting the Template Data Language on a specific path.
See JetBrains Documentation for more details.
Any file with .leex as the final extension will be treated as Live Embedded Elixir (LEEx) templates. To determine the Template Data Language, the .leex extension will be stripped and any remaining extension will be looked up to get the File Type and its associated Language. For example, *.txt.leex will be EEx with Plain Text (.txt) as the Data Template Language. Likewise, *.html.leex will be EEx with HTML as the Data Template Language. There's no need to register *.txt.leex or *.html.leex or any other *.DATA_TEMPLATE_LANGUAGE_EXTENSION.leex pattern explicitly: the nested extension will be looked up using the normal extension setup.

If you need more file-by-file configuration of the Template Data Language than can be achieved with a file extension/pattern, IntelliJ IDEA (Community or Ultimate Edition) has support for setting the Template Data Language on a specific path.
See JetBrains Documentation for more details.
Any file with .heex as the final extension will be treated as HEEx - Phoenix LiveView's HTML-aware, component-friendly template language. Unlike EEx/LEEx, the Template Data Language is always HTML; it isn't guessed from the rest of the file name, since HEEx's own compiler (Phoenix.LiveView.HTMLEngine) always renders to HTML.
~H sigils in .ex files use the same HEEx language, with separate HTML and Elixir PSI for the sigil's contents, once enabled - see ~H Sigil HEEx Language Injection Support below.
<.component> and <Module.component> tags navigate (Go To Declaration) to the def/defp that defines them, whether the component is defined in the same view module, brought in with an explicit import, or brought in via use MyAppWeb, :html (including a use nested inside it, such as use Phoenix.Component). Find Usages and Rename on the def/defp include its tags, Rename also works with the caret on the tag, and Quick Docs on a tag shows the function's documentation. All of this applies in .heex files and inside ~H sigils. A dotted name that is not valid component syntax is flagged the same way an unknown HTML tag is.
HEEx's special attributes (:let, :if, :for, :key, :type) and phx-* bindings are recognized on HTML tags, and <:slot> tags are not validated as HTML elements.

mix compile instead of elixirc directly)--no-docs elixirc flag)--no-debug-info elixirc flag)--warnings-as-errors elixirc flag)--ignore-module-conflict elixirc flag)
If a file has errors and warnings, they are group together in Build Messages under that file.

You can jump to errors and warnings in the Build Messages
Highlight the error or warning you want to jump to source
Do one of the following
Right-Click the error or warning
Select Jump to Source from the context menu

OR
You can also turn on Autoscroll to Source, which will Jump To Source whenever you Click or select an error or warning.


If you enable Warnings as Errors in the settings, then the Warnings will be treated as Errors by elixirc and mix and the Build Messages will show the Warnings as Errors.

If only warnings remain in the source.

With Warnings as Errors On, all the Warnings will appear as Errors and still fail the build

With Warnings as Errors Off, the Warnings will appear as Warnings and the build will succeed




Live Templates are snippets of code that can be inserted quickly and have placeholder locations that the cursor will automatically jump to when using the template. Whenever you start typing, Live Templates will start matching against the shortcuts. A template can be selected with Tab.
Live Templates can be customized in Preferences > Editor > Live Templates > Elixir.
| Shortcut | Code |
|---|---|
@doc
|
|
case
|
|
cond
|
|
def
|
|
def,
|
|
defi
|
|
defm
|
|
defmac
|
|
defmacp
|
|
defover
|
|
defp
|
|
defpro
|
|
defs
|
|
do
|
|
doc
|
|
fn
|
|
for
|
|
if
|
|
ife
|
|
ii
|
|
mdoc
|
|
rec
|
|
test
|
|
try
|
|
Documentation in @moduledoc, @typedoc or @doc module attribute values are marked as Markdown language, so that the JetBrains supplied Markdown support works in documentation.
Code in code blocks (indented 4 spaces) are marked as Elixir language, so all the normal language support works in code examples and doctests.
This includes highlighting reads and writes of variables

and Go To Definition for calls




Markdown documentation in @moduledoc, @typedoc or @doc can be toggled back and forth from the editable Markdown view and the rendered HTML view.
In the rendered view, aliases are clickable and navigatable.
Clicking the links will show the documentation for module.

Using Go To Definition (Cmd+Click) on the link will take you to the module definition.




You can get documentation for functions and macros that have a @doc, aliases of modules that have a @moduledoc or types that have a @typedoc in a pop-up using Quick Documentation.
Place the cursor over the name of callback being referenced.
Display the documentation with either:
@doc
@doc@moduledocPlace the cursor over the name of a type being used.
Display the documentation with either:
@typedoc
Quick Documentation also works for Erlang modules and functions referenced via atom syntax
(e.g. :math, :lists.any/2). The plugin reads documentation from the EEP-48 Docs chunk
embedded in .beam files, or from external .chunk files at <app>/doc/chunks/<module>.chunk.
For this to work, your Erlang/OTP installation must have been built with documentation.
Checking if docs are available:
If hovering over :lists or :math shows "No Documentation found", your OTP was built without
docs. Many mise/asdf/kerl installations omit docs by default.
Installing Erlang with documentation via mise:
KERL_BUILD_DOCS=yes MISE_ERLANG_COMPILE=true mise install erlang@X.Y.Z
Replace X.Y.Z with your desired OTP version (e.g. 27.3.3). The key variables are:
KERL_BUILD_DOCS=yes -- tells kerl to build documentation (including .chunk files)MISE_ERLANG_COMPILE=true -- forces a source build (pre-built binaries may not include docs)Installing Erlang with documentation via asdf:
KERL_BUILD_DOCS=yes asdf install erlang X.Y.Z
After reinstalling Erlang with docs, restart the IDE so it re-indexes the .beam files.
OTP 23 and earlier: You may need to add
CFLAGS="-O2 -g -Wno-error=implicit-function-declaration"to the install command to avoid compilation errors on newer toolchains.
Distillery's mix release produces a CLI for running the release.
Build the release: mix release
==> Release successfully built!
You can run it in one of the following ways:
Interactive: _build/ENV/rel/NAME/bin/NAME console
Foreground: _build/ENV/rel/NAME/bin/NAME foreground
Daemon: _build/ENV/rel/NAME/bin/NAME start
Run > Edit Configurations...

Click +
Select "Distillery Release CLI"

Fill in the "Release CLI Path" with the full path to the _build/ENV/rel/NAME/bin/NAME path produed by mix release above.
Fill in the "Release CLI arguments".
console runs a shell with the release loaded similar to iex -S mix.foreground to runs the release without a shell, like mix or mix run.
The available commands are controlled by your release config rel/config.exs that Distillery uses.(Optionally) fill in "erl arguments" with arguments to erl before it runs elixir.
This is the same as the ERL_OPTS environment variable supported by Distillery.
(Optionally) fill in "elixir -extra arguments" with arguments to pass to elixir before it run the release.
This is the same as the EXTRA_OPTS environment variable supported by Distillery.
(Optionally) change the Code Loading Mode
This is the same as the CODE_LOADING_MODE environment variable supported by Distillery.
rel/config.exs. Don't set CODE_LOADING_MODE environment variable.embedded - load all code immediately on boot. Set CODE_LOADING_MODE=embedded.interactive - load code on-demand as it is needed/referenced. Set CODE_LOADING_MODE=interactive.(Optionally) set the "Log Directory"
This is the same as the RUNNER_LOG_DIR environment variable supported by Distillery.
(Optionally) change "Replace OS Vars"
This is the same as the REPLACE_OS_VARS environment variable supported by Distillery.
rel/config.exs. Don't set REPLACE_OS_VARS environment variable.false - don't replace "${A_VAR_NAME}" in the generated configuration with A_VAR environment variable at runtime. Set REPLACE_OS_VARS=false.true - replace "${A_VAR_NAME}" in the generated configuration with A_VAR environment variable at runtime. Set REPLACE_OS_VARS=true.(Optionally) set "sys.config File"
This is the same the SYS_CONFIG_PATH environment variable supported by Distillery.
(Optionally) set "Release Config Directory".
This is the same as the RELEASE_CONFIG_DIR environment variable supported by Distillery.
(Optionally) set "Pipe directory".
This is the same as the PIPE_DIR environment variable supported by Distillery.
(Optionally) set "Use Pseudo-terminal (PTY).
If checked use PTY for interactive shells. Automatically on when "Release CLI Arguments" starts with one of the known interactive commands (attach, console, console_boot, console_clean, or remote_console).
Fill in the "Working directory.
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
_build/ENV/rel/NAME/bin/NAMEiex commands.:debugger application to your release
rel/config.exsrelease NAME block, in the set :applications block add :debugger:
--- a/rel/config.exs
+++ b/rel/config.exs
@@ -41,6 +41,8 @@ end
release :intellij_elixir do
set version: current_version(:intellij_elixir)
set applications: [
+ # needed for IntelliJ Elixir debugger
+ :debugger,
:runtime_tools
]
end
mix testsAlthough it is exceedingly rare, as most Elixir projects use mix, it is supported to run/debug elixir directly, such as when doing elixir script.exs.
Run > Edit Configurations...

Click +
Select "Elixir"

Fill in the "elixir arguments".
(Optionally) fill in "erl arguments" with arguments to erl before it runs elixir.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug elixir
Click the Run arrow in the Toolbar to run elixir.

The Run pane will open, showing the results of elixir.
elixiriex run configurations allow you to run iex with IntelliJ Elixir attached. It is most useful when debugging, but it also allows you save customizations in the configuration when it is more complicated than just iex.
Run > Edit Configurations...

Click +
Select "IEx"

(Optionally) fill in "iex arguments" with arguments to iex.
(Optionally) full in "erl arguments" with arguments to erl before it runs iex.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug the iex configuration.
Click the Run arrow in the Toolbar to run iex

iex.Much like rake tasks in Rubymine, this plugin can run mix tasks.
Run > Edit Configurations...

Click +
Select "Elixir Mix"

Fill in the "mix arguments" starting with the name of the mix task followed by any arguments to that task.
(Optionally) fill in "elixir arguments" with arguments to elixir before it runs mix.
(Optionally) fill in "erl arguments" with arguments to erl before it runs elixir.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug the Mix Task
Click the Run arrow in the Toolbar to run the mix task

The Run pane will open, showing the results of the mix task.
If there is an error with a FILE:LINE stack stack_frame, it will be a clickable link that will take you to that location

mix taskIf you want to run iex in the context of the project, you need to run iex -S mix, but if you don't want to have to worry about forgetting whether it's -s or -S or if it is mix -S iex or iex -S mix, you can use an IEx Mix configuration.
Run > Edit Configurations...

Click +
Select "IEx Mix"

(Optionally) fill in "mix arguments", such as phx.server if you want to launch Phoenix inside of iex.
(Optionally) fill in "iex arguments" with arguments to iex before -S mix.
(Optionally) full in "erl arguments" with arguments to erl before it runs iex.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined, you can either Run or Debug iex -S mix
iex -S mixiex -S mix.mix espec The mix espec task gets a special type of Run Configuration, Elixir Mix Espec. Using this Run Configuration type instead, of the basic Elixir Mix Run Configuration will cause the IDE to attach a special formatter to mix espec, so that you get the standard graphical tree of Test Results.
The Run pane will show Test Results. If there is a compilation error before or during mix espec, it will be shown as a test failure. If the compilation failure is in a _spec.exs file can it can be inferred from the stacktrace, the compilation error will show up as a test failure in that specific module.
If you override the default formatters you will need to add the following code to your spec_helper.exs.
If you override formatters similar to below
ESpec.configure fn(config) ->
config.formatters ...
ESpec.configure fn(config) ->
config.formatters [
{ESpec.Formatters.Json, %{out_path: "results.json"}},
{ESpec.Formatters.Html, %{out_path: "results.html"}},
{ESpec.Formatters.Doc, %{details: true, out_path: "results.txt"}},
{ESpec.Formatters.Doc, %{details: true, diff_enabled?: false, out_path: "results-no-diff.txt"}},
{ESpec.CustomFormatter, %{a: 1, b: 2}},
]
end
Replace them with code that checks for the graphical formatter TeamCityESpecFormatter and uses only it when available.
ESpec.configure fn(config) ->
config.formatters(if Code.ensure_loaded?(TeamCityESpecFormatter) do
[{TeamCityESpecFormatter, %{}}]
else
...
end)
end
ESpec.configure fn(config) ->
config.formatters(if Code.ensure_loaded?(TeamCityESpecFormatter) do
[{TeamCityESpecFormatter, %{}}]
else
[
{ESpec.Formatters.Json, %{out_path: "results.json"}},
{ESpec.Formatters.Html, %{out_path: "results.html"}},
{ESpec.Formatters.Doc, %{details: true, out_path: "results.txt"}},
{ESpec.Formatters.Doc, %{details: true, diff_enabled?: false, out_path: "results-no-diff.txt"}},
{ESpec.CustomFormatter, %{a: 1, b: 2}},
]
end)
end
mix espce Run Configurations ManuallyRun > Edit Configurations...

Click +
Select "Elixir Mix ESpec"

Fill in the "mix espec arguments" with the argument(s) to pass to mix espec. Normally, this will be list of *_spec.exs files, relative to the "Working directory".
NOTE: Unlike mix test, mix espec does not support directories as arguments.
(Optionally) fill in "elixir arguments" with the arguments to elixir before it runs mix test.
(Optionally) fill in "erl arguments"with the arguments toerlbefore it runselixir`.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined you can either Run or Debug the mix especs
mix test task
mix testsWhile you can create Elixir Mix ESpec run configurations manually using the Run > Edit Configurations... menu, it is probably more convenient to use the context menu.
mix espec Run Configurations from contextThe context menu must know that the the directory, file, or line you are right-clicking is a test. It does this by checking if the current directory or an ancestor is marked as a Test Sources Root and contains or is a *_spec.exs file(s)
espec directory is marked as a Test Sources Rootespec directory is green. If it is, it is likely a Test Sources Root. This color may differ in different themes, so to be sure you can check the context menutest directory.If "Unmark as Test Sources Root" is shown, then the directory is already configured correctly, and create from context will work.

If "Test Sources Root" is shown, then the directory need to be configured by clicking that entry

mix espec Run Configurations from directoryRight-click the directory in the Project pane
Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.

Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.mix espec Run Configurations from fileAlternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.Finally, you can use the editor tabs
Right-click the editor tab for the test file you want to run

Click "Run Mix ESpec", which will both create the Run Configuration and Run it.
mix espec Run Configurations from lineIf you want to be able to run a single test, you can create a Run Configuration for a line in that test
Right-click a line in the test file

Click "Run Mix ESpec", which will both create the Run Configuration and Run it.
Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.mix test The mix test task gets a special type of Run Configuration, Elixir Mix ExUnit. Using this Run Configuration type instead, of the basic Elixir Mix Run Configuration will cause the IDE to attach a special formatter to mix test, so that you get the standard graphical tree of Test Results
The Run pane will show Test Results. If there is a compilation error before or during mix test, it will be shown as a test failure. If the compilation failure is in a _test.exs file can it can be inferred from the stacktrace, the compilation error will show up as a test failure in that specific module.
doctest names are rearranged to emphasize the function being tested: "test doc at MODULE.FUNCTION/ARITY (COUNT)" becomes "MODULE.FUNCTION/ARITY doc (COUNT)". If MODULE is the same as the test case without the Test suffix, then MODULE is stripped too and the test name becomes only FUNCTION/ARITY doc (COUNT).
mix test Run Configurations ManuallyRun > Edit Configurations...

Click +
Select "Elixir Mix ExUnit"

Fill in the "mix test arguments" with the argument(s) to pass to mix test. Normally, this will be a directory like test, relative to the "Working directory"
(Optionally) fill in "elixir arguments" with the arguments to elixir before it runs mix test.
(Optionally) fill in "erl arguments"with the arguments toerlbefore it runselixir`.
Fill in the "Working directory"
... button(Optionally) click the ... button on the "Environment variables" line to add environment variables.
Click "OK" to save the Run Configuration and close the dialog
With the Run Configuration defined you can either Run or Debug the mix tests
mix test task
mix testsWhile you can create Elixir Mix ExUnit run configurations manually using the Run > Edit Configurations... menu, it is probably more convenient to use the context menu.
mix test Run Configurations from contextThe context menu must know that the the directory, file, or line you are right-clicking is a test. It does this by checking if the current directory or an ancestor is marked as a Test Sources Root.
test directory is marked as a Test Sources Roottest directory is green. If it is, it is likely a Test Sources Root. This color may differ in different themes, so to be sure you can check the context menutest directory.If "Unmark as Test Sources Root" is shown, then the directory is already configured correctly, and create from context will work.

If "Test Sources Root" is shown, then the directory need to be configured by clicking that entry

mix test Run Configurations from directoryRight-click the directory in the Project pane
Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.

Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.mix test Run Configurations from fileAlternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it.Finally, you can use the editor tabs
Right-click the editor tab for the test file you want to run

Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.
mix test Run Configurations from lineIf you want to be able to run a single test, you can create a Run Configuration for a line in that test
Right-click a line in the test file

Click "Run Mix ExUnit", which will both create the Run Configuration and Run it.
Alternatively, you can use keyboard shortcuts
Ctrl+Shift+R will create the Run Configuration and Run it..beam Files.beam files are the compiled version of modules on the BEAM virtual machine used by Elixir and Erlang. They are the equivalent of .class files in Java.
.beam files are not detected purely by their file extension: the BEAM file format starts with a magic number, FOR1, that is checked for before decompiling.
.beam files have 2 editors registered: decompiled Text and BEAM Chunks

If the .beam module was compiled with the compressed compiler directive, which in Erlang looks like
-compile([compressed])
and in Elixir looks like
@compile [:compressed]
then the outer file format is GZip (which is detected by checking for the gzip magic number, 1f 8b, at the start of the file) and the .beam will be (stream) decompressed before the .beam header is checked and the chunks decoded.
.beam files are composed of binary chunks. Each chunk is formatted
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | Name (ASCII Characters) | |||
| 4 | Length (`unsigned-big-integer`) | |||
| 8+ | Chunk-Specific | |||
This format is generically referred to as Type-Length-Value
The BEAM Chunks editor tab is subdivided into further tabs, one for each chunk in the .beam file.

The tabs are listed in the order that the chunks occur in the .beam file.
Atom / AtU8The Atom chunk holds LATIN-1 encoded atoms while AtU8 chunk holds UTF-8 atoms. There will only be one of these atom-related chunks in any given .beam file. AtU8 is used in newer versions of OTP that support UTF-8 atoms. AtU8 was introduced in OTP 20.
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | atom count (`unsigned-big-integer`) | |||
| 4 | length1 (`unsigned-byte`) | bytes (for length1) | ||
| 4+length1+...+lengthn-1 | lengthn (`unsigned-byte`) | bytes (for lengthn) | ||
The Atom/AtU8 tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Index | 1-based to match Erlang convention. In the Code chunk, atom(0) is reserved to always translate to nil | Derived |
| Byte Count | The byte count for the atom's bytes | Raw |
| Characters | From encoding the bytes as LATIN-1 for Atom chunk or UTF-8 for AtU8 chunk | Derived |

AttrThe Attr chunk holds the module attributes, but only those that are persisted. Erlang module attributes are persisted by default, but in Elixir module attributes need to be marked as persisted with Module.register_attribute/3
The Attr chunk uses External Term Format (term_to_binary's output) to encode a proplist, which is similar to, but not quite the same an Elixir Keyword list
All modules will have a :vsn attribute that is either set explicitly or defaults to the MD5 of the module.
The Attr tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Key | Attribute name | Raw |
| Value | Attribute value. Note: The value always appears as a list as read from the binary format. I don't know why. | Raw |

CInfThe CInf chunk is the Compilation Information for the Erlang or Erlang Core compiler. Even Elixir modules have it because Elixir code passes through this part of the Erlang Core compiler
The CInf chunk uses External Term Format (term_to_binary's output) to encode a proplist, which is similar to, but not quite the same an Elixir Keyword list
The CInf tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Key | Option name | Raw |
| Value | Inspected value | Raw |

CodeThe Code chunk contains the byte code for the module.
It is encoded in BEAM Compact Term Encoding, which differs from the binary format produced by term_to_binary.
The Code tab shows a read-only editor with one byte code operation on each line. For ease of reading, operations are grouped by function and then label block with indentation indicating scope.

By default as many references to other chunks and references to other parts of Code chunk are inlined to ease understanding. If you want to see the raw byte code operations, you can turn off the various inliners.
####### Controls
| Control | On | Off |
|---|---|---|
| Inline Atoms |
atom(0) is inlined as nil
|
atom(N) if "Inline Integers" is Off
|
atom(n) looks up index `n` in `Atom`/`AtU8` chunk and inlines its `inspect`ed version
|
N if "Inline Integers" is On and the argument supports "Inline Integers"
| |
| Inline Functions |
literal(n) looks up index n in FunT chunk and inlines the name if the argument supports "Inline Functions"
|
literal(n) if "Inline Integers" is Off
|
n if "Inline Integers" is On and the argument supports "Inline Integers"
| ||
| Inline Imports |
literal(n) looks up index n in ImpT and inlines it as a function reference: &module.name/arity if argument supports "Inline Functions"
|
literal(n) if "Inline Integers" Is Off
|
n if "Inline Integers" is On and the argument supports "Inline Integers"
| ||
| Inline Integers | atom(n) and literal(n) inline as n if argument supports "Inline Integers" | atom(n), integer(n), and literal(n) |
integer(n) inlines as n | ||
| Inline Labels | label(n) inlines as n if argument supports "Inline Labels" | label(n) |
| Inline Lines | line(literal(n)) looks up index `n` in the "Line Reference" table in the `Lines` chunk. The Line Reference contains a file name index and line. The file name index is looked up in the "File Name" table in the `Lines` chunk. The line from the Line Reference and the File name from the "File Name" table are inlined as `line(file_name: file_name, line: line)`. | line operations are left as is |
| Inline Literals | literal(n) looks up index n in LitT chunk and inlines its `inspect`ed version if the argument supports "Inline Literals" | literal(n) |
| Inline Local Calls | label(n) finds label(n) in Code chunk, then searches back for the previous func_info operation, then inlines it as a function reference: &module.name/arity if argument supports "Inline Local Calls" | label(n) |
| Inline Strings | Looks up bit_length and byte_offset into `StrT` chunk as their CharList value if supported by operation as value to string argument name | bit_length and byte_offsetarguments are left as is |
| Show Argument Names | Adds keyword argument names before each argument value | Leaves values as positional arguments |
If any of the inliners are incorrect or you have an argument name that makes more sense, please open an issue.
DbgiThe Dbgi chunk contains Debug Info. It was introduced in OTP 20 as a replacement for the Abst chunk. While the Abst chunk was required to contain the Erlang AST, the Dbgi format can contain the debug info for other languages, such as Elixir quoted form AST.
Because the format is language neutral, the format is a set of nested, versioned formats. The outer most layer is
{:debug_info_v1, backend, metadata | :none}
For :debug_info_v1, Elixir's backend is :elixir_erl. The metadata for :elixir_erl is further versioned: {:elixir_v1, map, specs}.
map contains the bulk of the data.
| Key | Value |
|---|---|
:attributes | Attributes similar to the Attr chunk, but at the Elixir, instead of Core Erlang level. Usually they match with the exception that attributes doesn't contain vsn when Attr contains the MD5 version |
:compile_opts | Compilation options similar to CInf chunk's options key, but at for Elixir, instead of Core Erlang level. |
:definitions | The Elixir quoted AST for reach function clause. |
:file | The name of the file the module was generated from. |
:line | The line in :file where the module was defined, such as the line defmodule occurred. |
:module | The name of the module as an atom |
:unreachable | Unreachable functions |
The Dbgi tab appearance varies based on whether it was created with Erlang or Elixir, reflecting the fact that the Dbgi format is dependent on the backend that wrote it.
####### Elixir (:elixir_erl backend)
The Dbgi tab show the single value map entries: :file, :line, and :module.

For the multi-value keys: :attributes, :compile_opts, and :definitions, there are individual tabs.

######## Attributes
The Attributes tab has the same format as the Attrs chunk.

######## Compile Options
The Compile Options tab is usually empty, much like the CInf options key for Erlang.

######## Definitions
The Definitions tab is split between a tree of Module, Function/Arity and clauses.

Clicking on a clause will show only that clause, but clicking on a higher level in the tree will show all clauses in the function or the entire Module.

The AST stored in the definitions tab and the process of converting it back to code is not format preserves, so it will not look precisely like the source code as the AST has undergone some macro expansion before its put in the Dbgi chunk. As common idioms are understood, reversals will be add to the renderer.
######## Type Specifications
The Type Specifications tab is split between a tree of the Module, Module Attribute, and type specifications.

Clicking on a type specification will show only that type specification, but clicking on a higher in the tree will show all type specifications for the same module attribute or the entire Module.

####### Erlang (:erlang_abstract_code backend)
The Dbgi tab has Abstract Code and Compile Options tabs.

######## Abstract Code
The Abstract Code tab is split between a tree of Attributes, Functions, Function/Arity, and clauses.

Clicking on a clause will show only that clause, but clicking on a higher level in the tree will show all clauses in the function or the entire Module.

The abstract code stored in the :erlang_abstract_code backend format is the Erlang Abstract Format. Instead of converting the Erlang Abstract Format back to Erlang, which would require IntelliJ Erlang to highlight and annotate and for you to be used to reading Erlang, the Erlang Abstract Format is translated back to Elixir. Using the BEAM Chunk Dbgi viewer can be a way to convert compiled Erlang code to Elixir source automatically.
ExDcThe ExDc chunk stores ExDoc. Not the rendered HTML from the ex_doc package, but the the @doc, @moduledoc, and @typedoc attribute values that work even without ex_doc installed. This chunk is what is consulted when the h helper is used in iex.
The ExDc chunk is the encoded with term_to_binary. The term format is a versioned as {version, versioned_format}. The current version tag is :elixir_docs_v1 and the versioned_format is a Keyword.t with keys matching the Code.get_docs/2 tags :callback_docs, :docs, :moduledoc, and :type_docs keys.
Like Dbgi, the ExDc tab is split between a tree to navigate and an editor to show the decompiled value.

Click on a node in the tree will show all docs at that level and any descendants.
| Node | Description |
|---|---|
| Root | All docs |
| Module | @moduledoc |
| Types | All @typedocs |
| Types child | A specific @typedoc |
| Callbacks | All @callback @docs |
| Callbacks child | A specific @callback's @doc |
| Functions/Macros | All @docs for functions/macros |
| Functions/Macros child | A specific function/macro's @doc |
ExpTThe ExpT chunk is the Export Table. The name "Export" derives from the Erlang module attribute -export, which is used to "export" functions from a module. It is the equivalent of making a function or macro public with def and defmacro as opposed to making it private with defp and defmacrop in Elixir.
The BEAM format and the ExpT chunk, being made for Erlang, has no concept of macros. It only understands functions, so Elixir macros, like __using__/1 called by use are compiled to plain Erlang functions with MACRO- prefixed to their name and an extra argument (the __CALLER__ environment) as the first argument, which increases the arity, yielding a full MFA of MACRO-__using__/2 as seen above.
The ExpT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Atom Index | Index into the Atom or AtU8 chunk for the function's name | Raw |
| Name | The atom referenced by "Atom Index" | Derived |
| Arity | The arity (argument count) of the function | Raw |
| Label | Label index in the Code chunk where the function is defined. This label is usually immediately after the func_info operation and before the first pattern match or guard operation. | Raw |

ImpTThe ImpT chunk is the Import Table. It DOES NOT encode just the Erlang -import attributes or Elixir import macro calls: it tracks any external function or macro called from another module. call_ext_* operations in the Code chunk don't store the Module and Function (MF) of the function they will call directly in the bytecode, instead, one of the arguments is an index into the ImpT chunk. This way, all external calls are normalized into the ImpT chunk instead of being denormalized to the call site. The arity still appears at the call site to help with checking the argument count.
You may notice that erlang.byte_size/1 is included in the table. This is because even BIFs are referenced by MFA and not a pre-assigned number as would be the case for system calls in operating systems like Linux. BEAM is like an Operation System, but not in all ways.
The ImpT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Index | 0-based index used by references in the Code chunk. | Derived |
| Module Atom Index | Index into the Atom or AtU8 chunk for the Module's name | Raw |
| Module Atom | The atom referenced by "Module Atom Index". | Derived |
| Function Atom Index | Index into the Atom or AtU8 chunk for the functon's name | Raw |
| Function Atom | The atom referened by "Function Atom Index". | Derived |

LitTThe LitT chunk contains literals loaded as arguments in Code chunk.
Confusingly, in the Code chunk sometimes the literal(N) term is used to encode integer N, an index into another chunk, or an actual index into the LitT. How literal terms are handled is completely dependent on the specific operation, so without having outside knowledge about the bytecode operation arguments for BEAM, the best way to figure out if literal terms are an integer or an index is to toggle the various controls in the Code tab to see if literal with no inlining turns into a LitT literal, FunT function reference, ImpT function reference, or integer.
The LitT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| # | 0-based index used by references in the Code chunk. | Derived |
| Term | The equivalent of raw |> binary_to_term() |> inspect() | Raw |

LineThe Line chunk encodes both the file name and line number for each line(literal(n)) operation in the Code chunk. The n in line(literal(n)) is an index in to the Line References table in the Line chunk. This is used in Phoenix view modules to show where code from templates comes from.
The Line chunk is composed of 2 subsections: (1) Line References and (2) File Names. First there is a header setting up the number of each entry to expect.
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | emulator version (`unsigned-big-integer`) | |||
| 4 | flags (`unsigned-big-integer`) | |||
| 8 | Line Instruction Count (`unsigned-big-integer`) | |||
| 12 | Line Reference Count (`unsigned-big-integer`) | |||
| 16 | File Name Count (`unsigned-big-integer`) | |||
####### Line References
This uses the Compact Term Format used for the Code chunk. The format ends up producing {file_name_index, line} pairs using the following algorithm:
| Term | Interpretation |
|---|---|
atom(n) | Change file_name_index to n |
integer(n) | Add {file_name_index, n} to end of Line References |
####### File Names
| Offset | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 0 | Byte Count (`unsigned-big-integer`) | Bytes | ||
The Line tab has one subtab for each subsection in the tab. Each subsection has its own table.

LocTThe LocT chunk is the dual to the ExpT chunk: it contains all private functions and macros.
You'll notice entries like -__struct__/1-fun-0-, starts with - and have a / suffix with fun in it. This naming scheme is used for anonymous functions such as those defined with fn or the capture operator (&) in Elixir. Much like how macros don't really exist and use a MACRO- suffix, anonymous functions/lambdas don't exist, and instead use a distinct naming scheme -<PARENT_FUNCTION>/*fun*. Unlike MACRO-, which is an Elixir invention, anonymous functions/lambdas really being local named functions with derived names is also done in pure Erlang modules. Erlang's anonymous functions are defined with fun, which is where the fun part of the naming scheme comes from.
The LocT tab shows a table with the columns
| Column | Description | Source |
|---|---|---|
| Atom Index | Index into the Atom or AtU8 chunk for the function's name | Raw |
| Name | The atom referenced by "Atom Index" | Derived |
| Arity | The arity (argument count) of the function | Raw |
| Label | Label index in the Code chunk where the function is defined. This label is usually immediately after the func_info operation and before the first pattern match or guard operation. | Raw |

StrTThe StrT chunk contains all Erlang strings (that is, Elixir charlists) used in the Code chunk.
The StrT chunk contains a single contiguous pool. These strings are used for byte code operations like bs_put_string. Not all strings appear in StrT. Some strings, including most Elixir strings (Erlang binaries) appear in the LitT chunk that holds literals. I'm not sure how the compiler determines whether to use StrT or LitT. I think it all depends on the byte code operation.
Instead of encoding the start and length of each string in the chunk itself, the start and length for any given string is passed as arguments to the byte code operations in the Code chunk. By doing this, shared substrings can be efficiently encoded in StrT.

.beam files, such as those in the Elixir SDK, the Elixir SDK's Internal Erlang SDK, and in your project's build directory will be decompiled to equivalent def and defmacro calls. The bodies will not be decompiled, only the call definition head and placeholder parameters. These decompiled call definition heads are enough to allow Go To Declaration, the Structure pane, and Completion to work with the decompiled .beam files.
It turns out that in the .beam binary format there are no macros. This makes sense since the BEAM format was made for Erlang, which does not have macros, and only has functions. Elixir marks its macros in the compiled .beam by prefixing them with MACRO-.
There are 2 chunks in the BEAM format for function references: ExpT, which is for exports (because in Erlang module they are from -export), which are the public functions and macros; and LocT, which is for locals (anything not exported in Erlang), which are private functions and macros.
| BEAM Chunk | Atom Prefix | Macro |
|---|---|---|
ExpT | MACRO- | defmacro |
ExpT | N/A | def |
LocT | MACRO- | defmacrop |
LocT | N/A | defp |
defp with / in nameMuch like there are no macros in BEAM, there are no anonymous functions either. Any anonymous function (using fn in Elixir or fun in Erlang) ends up being a named function in the LocT chunk. Anonymous functions names start with -, then the parent function's name, a / and a unique number for that scope.
As an example, Kernel has
defp unquote(:"-MACRO-binding/2-fun-0-")(p0, p1, p2, p3) do
# body not decompiled
end
which is generated from the for in
defmacro binding(context \\ nil) do
in_match? = Macro.Env.in_match?(__CALLER__)
for {v, c} <- __CALLER__.vars, c == context do
{v, wrap_binding(in_match?, {v, [generated: true], c})}
end
end
Functions and macros can have names that aren't valid identifier names, so the decompiler has special handlers to detect these invalid identifiers and escape them to make decompiled code that is parsable as valid Elixir.
| Handler | Name/Arity | Decompiled | Reason |
| Infix Operator | !=/2 | left != right | Infix operators are defined in infix position |
!==/2 | left !== right | ||
&&/2 | left && right | ||
&&&/2 | left &&& right | ||
*/2 | left * right | ||
=+/2 | left + right | ||
=++/2 | left ++ right | ||
-/2 | left - right | ||
--/2 | left -- right | ||
->/2 | left -> right | ||
../2 | left .. right | ||
//2 | left / right | ||
::/2 | left :: right | ||
</2 | left < right | ||
<-/2 | left <- right | ||
<<</2 | left <<< right | ||
<<~/2 | left <<~ right | ||
<=/2 | left <= right | ||
<>/2 | left <> right | ||
<|>/2 | left <|> right | ||
<~/2 | left <~ right | ||
<~>/2 | left <~> right | ||
=/2 | left = right | ||
==/2 | left == right | ||
===/2 | left === right | ||
=>/2 | left => right | ||
=~/2 | left =~ right | ||
>/2 | left > right | ||
>=/2 | left >= right | ||
>>>/2 | left >>> right | ||
\\/2 | left \\\\ right | ||
^/2 | left ^ right | ||
^^^/2 | left ^^^ right | ||
and/2 | left and right | ||
in/2 | left in right | ||
or/2 | left or right | ||
|>/2 | left |> right | ||
||/2 | left || right | ||
|||/2 | left ||| right | ||
~=/2 | left ~= right | ||
~>/2 | left ~> right | ||
~>>/2 | left ~>> right | ||
| Prefix Operator | +/1 | (+value) | To prevent precedence errors, unary prefix operators, which also have binary infix operators of the same name need to be defined inside parentheses |
-/1 | (-value) | ||
| Unquoted | %/2 | unquote(:%)(p0, p1) | Special forms need to defined as atom passed to unquote, as special forms are handled before macros defining the calls are applied |
%{}/1 | unquote(:%{})(p0) | ||
&/1 | unquote(:&)(p0) | ||
./2 | unquote(:.)(p0, p1) | ||
<<>>/1 | unquote(:<<>>)(p0) | ||
do/n | unquote(:do)(p0, ...) | Keywords need to be escaped | |
fn/1 | unquote(:fn)(p0) | Special forms need to defined as atom passed to unquote, as special forms are handled before macros defining the calls are applied | |
unquote/1 | unquote(:unquote)(p0) | ||
unquote_splicing/1 | unquote(:unquote_splicing)(p0) | ||
{}/n | unquote(:{})(p0, ...) | ||
Capitalized/n | unquote(:Capitalized)(p0, ...) | Part of the Corba libraries in OTP have functions starting with a capital letter, which would be parsed as an Alias in Elixir if not unquoted. | |
#text#/1 | unquote(:"#text#")(p0) |
Part of the XML libraries in OTP have functions
that start with or contain `#`, which would parse as a comment in Elixir if not unquoted in a double quoted
atom.
| |
| Default | name/n | name(p0, ...) | If no specialized handler is required, functions and macros are defined normally with pN for each parameter in the Nth position |
When you start typing an Alias, completion will look in three locations:
alias aliased names in the current file
Suffix for alias Prefix.SuffixMultipleAliasA or MultipleAliasB for alias Prefix.{MultipleAliasA, MultipleAliasB}As for alias Prefix.Suffix, as: AsPrefix.Suffix from defmodule Prefix.SuffixMyProtocol from defprotocol MyProtocolMyProtocol.MyStruct
defimpl MyProtocol, for: MyStructdefimpl MyProtocol nested under defmodule MyStructSuffix.Nested for alias Prefix.Suffix where Prefix.Suffix.Nested is an indexed module, implementation or protocol name.MultipleAliasA.Nested for alias Prefix.{MultipleAliasA, MultipleAliasB} where Prefix.MultipleAliasA.Nested alias Prefix.{MultipleAliasA, MultipleAliasB} is an indexed module, implementation or protocol name.As.Nested for alias Prefix.Suffix, as: As where Prefix.Suffix.Nested is an indexed module, implementation, or protocol name.{ }When you start typing inside { } for alias Prefix.{} or import Prefix.{}, completion will look for nested modules under Prefix and then remove the Prefix., so completion will look like Suffix.
Completion uses the same presentation as Structure, so you can tell whether the name is function/macro (Time), whether it is public/private (Visibility) and the Module where it is defined. Between the icons and the Modules is the name itself, which is highlighted in bold, the parameters for the call definition follow, so that you can preview the patterns required for the different clauses.

Qualified functions and macro calls will complete using those functions and macros defined in the qualifying Module (defmodule), Implementation (defimpl) or Protocol (defprotocol). Completion starts as shown as . is typed after a qualifying Alias.
Function and macro calls that are unqualified are completed from the index of all function and macro definitions, both public and priva
Truncated — view the full README on GitHub.
(top 30 of 56)
Kotlin
47.2%
Elixir
32.9%
Java
18.3%