IntelliJ Elixir Plugin

repository·main·Indexed 23 days ago

https://github.com/kronicdeth/intellij-elixir

A plugin providing Elixir and Erlang support for JetBrains IDEs, featuring syntax highlighting, semantic analysis, debugging, and project management. It supports both 'Rich' and 'Small' IDEs, offers integration for Mix projects, and includes specialized support for Windows Subsystem for Linux (WSL). Key features include an SDK status widget, automated dependency detection, and file templates for modules, Applications, Supervisors, GenServers, and GenEvents.

Tokens
27.4K
Snippets
17
Records
142
Agent score
84%

What's inside intellij-elixir

  1. Supported JetBrains IDEs for IntelliJ Elixir

    main

    The IntelliJ Elixir plugin is compatible with a wide range of JetBrains IDEs. It is categorized into two types:

    1. Rich IDEs: These allow you to set an Elixir SDK as the Project SDK. This is the recommended way to use the plugin as it simplifies the configuration for running elixir, erl, or mix.
    2. Small IDEs: These are language-specific IDEs where the native language SDK is always present. Using Elixir in these IDEs requires more complex manual configuration for SDK-dependent tools.

    The plugin is free to use in all JetBrains IDEs, regardless of the IDE's own licensing model.

  2. Understand the StrT chunk for strings

    main

    The StrT chunk contains a contiguous pool of Erlang strings (Elixir charlists) used in the Code chunk for byte code operations like bs_put_string.

    Key details:

    • Not all strings appear in StrT. Most Elixir strings (Erlang binaries) are stored in the LitT (literals) chunk.
    • The chunk does not encode the start and length of each string internally; instead, the start and length are passed as arguments to the byte code operations in the Code chunk. This allows for efficient encoding of shared substrings.
  3. Manage Module Sources, Paths, and Dependencies

    main

    Sources

    Mark directories as:

    • Excluded
    • Sources
    • Tests

    Paths

    Configures output directories for compilation:

    • Output path: For dev MIX_ENV.
    • Test output path: For test MIX_ENV.

    Dependencies

    Mix dependencies from deps/ are automatically detected and registered as IDE Libraries via background file system monitoring of deps/ and _build/.

    • Automatic Detection: Triggered by mix deps.get, mix compile, or folder deletions.
    • Structure: Creates an IDE Library with source roots from deps/APP/lib and class roots from _build/ENV/lib/APP/ebin.
    • Troubleshooting: If dependencies don't appear after mix deps.get, run mix compile to generate _build/ output, then wait for the file watcher. Alternatively, use Tools → Elixir → Install Mix Dependencies.
  4. Compare Rich vs Small IDEs for Elixir development

    main

    When choosing an IDE for Elixir development with this plugin, consider the distinction between 'Rich' and 'Small' IDEs:

    • Rich IDEs (e.g., IntelliJ IDEA Community/Ultimate) allow you to define the Elixir SDK as the primary Project SDK. This provides the most seamless experience for internal and external tool integration.
    • Small IDEs (e.g., PyCharm, WebStorm, GoLand, etc.) are language-specific. Because the native language SDK is always prioritized, you will need to perform additional configuration steps to ensure tools like elixir, erl, and mix work correctly.
  5. Access documentation via the ExDc chunk

    main

    The ExDc chunk stores documentation attributes (@doc, @moduledoc, and @typedoc) extracted from the BEAM file. This is the same data consulted by the h helper in iex.

    The ExDc tab uses a tree to navigate documentation levels. Selecting a node shows documentation for that level and its descendants:

    NodeDescription
    RootAll docs
    Module@moduledoc
    TypesAll @typedocs
    Types childA specific @typedoc
    CallbacksAll @callback @docs
    Callbacks childA specific @callback's @doc
    Functions/MacrosAll @docs for functions/macros
    Functions/Macros childA specific function/macro's @doc
  6. Understand Structure tool window element icons

    main

    Elements in the Structure tool window are identified by icons representing their lifecycle and visibility:

    Time (Lifecycle)

    • Compile Time: The element is used or checked at compile time and may not be accessible at runtime (e.g., macros).
    • Runtime: The element is usable at runtime (e.g., functions).

    Visibility

    • Public: The element is accessible outside its defining module.
    • Private: The element is only accessible within its defining module (macros defining private elements often end in p).
  7. Inspect `.beam` files and BEAM Chunks

    main

    .beam files are compiled modules for the BEAM virtual machine. The IDE provides two ways to view them: Decompiled Text and BEAM Chunks.

    Decompression

    If a module was compiled with the compressed directive, the IDE automatically detects the GZip magic number (1f 8b) and decompresses the file before inspecting the header.

    Elixir usage:

    @compile [:compressed]

    Erlang usage:

    -compile([compressed])

    BEAM Chunks Editor

    The BEAM Chunks editor allows you to inspect the binary chunks of the module. Each chunk follows a Type-Length-Value format:

    • Offset 0-3: Name (ASCII Characters)
    • Offset 4-7: Length (unsigned-big-integer)
    • Offset 8+: Chunk-specific data

    Key Chunk Types

    • Atom / AtU8: Holds atoms. AtU8 (introduced in OTP 20) holds UTF-8 atoms, while Atom holds LATIN-1. The editor provides a table showing the Index, Byte Count, and Characters.
    • Attr: Holds persisted module attributes. In Elixir, attributes must be marked as persisted using Module.register_attribute/3 to appear here. This chunk uses External Term Format to encode a proplist. All modules include a :vsn attribute.
  8. Examine variables and program state during debugging

    main

    When the program is suspended at a breakpoint, you can inspect the state of the application.

    Processes and Frames

    • Processes: Use the "Thread" drop-down in the Debugger processes panel to see current processes in the local node. Note that only the current process is suspended.
    • Frames: Use the Frames panel to navigate the call stack. Use the arrow keys or click a stack frame to move up/down. To return to the exact line where the debugger is currently paused, use Alt+F10 (Show Execution Point).

    Variable Representations

    Variables are rendered based on their Elixir/Erlang types:

    • Binaries: Shows each byte at its offset.
    • Bitstrings: Shows bytes with partial byte bitwidth annotations.
    • Charlists: Shows integer values (treated as lists).
    • Functions: Shows the inspect form (e.g., #Fun<...>).
    • Lists: Proper lists show elements by offset; improper lists show head and tail.
    • Maps: Atom keys are rendered as key = value; non-atom keys are shown as entries at a specific offset to allow expansion of complex keys.
    • Pids: Broken down into node, id, and serial.
    • Rebound Variables: Variables rebound in Elixir but not Erlang are marked with @VERSION (e.g., var@VERSION).
  9. Fix Keywords Not At End

    main

    In Elixir, keywords must appear at the end of an argument list. The 'Keywords Not At End' inspection detects when keywords appear before other positional arguments in a list.

    To fix this, either wrap the preceding expression in parentheses or move the keywords to the very end of the argument list.

  10. Identify anonymous functions in decompiled code

    main

    Anonymous functions (defined with fn in Elixir or fun in Erlang) do not exist as distinct entities in the BEAM format. Instead, they are represented as named functions in the LocT chunk.

    Their names follow a specific pattern: they start with a hyphen -, followed by the parent function's name, a forward slash /, and a unique number for that scope (e.g., -<PARENT_FUNCTION>/<NUMBER>-fun-<INDEX>-).

    Example of a decompiled anonymous function name: defp unquote(:"-MACRO-binding/2-fun-0-")(p0, p1, p2, p3)

    defp unquote(:"-MACRO-binding/2-fun-0-")(p0, p1, p2, p3) do
      # body not decompiled
    end
  11. Fix Ambiguous Parentheses

    main

    The 'Ambiguous parentheses' inspection detects spaces between a function name and its opening parentheses. This prevents the compiler from distinguishing between a function call with parentheses and a function call without parentheses where the first argument is a parenthetical group.

    Common scenarios:

    • Empty Parentheses: function () should be function().
    • Keywords in Parentheses: function (key: value) should be function(key: value).
    • Positional arguments in Parentheses: function (arg1, arg2) should be function(arg1, arg2).
    # Ambiguous
    function ()
    
    # Fixed (no arguments)
    function()
    
    # Fixed (first argument is an empty tuple)
    function(())
  12. Identify Overridable functions in the Structure View

    main

    In the IntelliJ Elixir Structure View, functions marked as overridable via defoverridable are visually distinguished.

    • defoverridable declaration: Marked with an Overridable icon. It acts as a parent element in the structure tree.
    • Overridable functions: The specific functions being made overridable appear as children of the defoverridable element. They are displayed with the NAME/ARITY format and include the Overridable icon to distinguish them from standard functions.