-
~Esigils now get EEx highlighting and completion.~Hinjected HEEx and~Linjected EEx, but Phoenix.HTML's own~Efell through to no injection at all, leaving the template as plain string text. It is EEx, like~L, and shares the same experimental HTML-injection setting. Refs #1257.
-
- File and line are now linked inside an inspected stack trace, where a frame's location sits in
a keyword list -
[file: 'lib/gald/phase.ex', line: 75]- rather than a formatted(app) path:line:frame. A path inspected as a~csigil reads the same as a plain charlist. Refs #510. - Stack traces are now linked in the Terminal tool window too, for
iex -S mix,mix testandmix phx.server, and in every console the IDE builds for a project with an Elixir module or SDK. - Erlang, HEEx and LEEx locations in a trace are now linked, not just
.ex,.exsand.eex. - An Erlang SDK now exposes its own sources from
lib/<app>-<version>/src, so an OTP frame such asgen_server.erlis navigable. Existing SDKs pick the roots up on the next refresh. - External Libraries now shows an SDK application's source beside its beams.
- File and line are now linked inside an inspected stack trace, where a frame's location sits in
a keyword list -
-
- SDK auto-detection now covers many more install layouts. Newly found:
/usr/lib64(Gentoo, and Erlang on Fedora);/usr/share/elixir/<version>(Fedora, RHEL), each version offered separately; Homebrew on Apple Silicon, on Linux and WSL, and installs since 2024-09-22 that nest the home inlib/elixir; version-pinned formulae such aserlang@25througherlang@28; and theelixir-installscript on macOS. Every platform now scans the same sources. Refs #312, #3670. - Selecting an install prefix in the SDK chooser now finds the home inside it - a prefix such as
a Homebrew version directory,
/usror/usr/localholds the SDK inlib/<tool>and only symlinks inbin, so the directory a user naturally picks was rejected as invalid.
- SDK auto-detection now covers many more install layouts. Newly found:
-
- Elixir 1.13 through 1.20 are now fully supported. Code written for any Elixir from 1.13.4 to 1.20.2 parses without spurious errors, including syntax whose meaning changed between releases.
.beamfiles compiled by OTP 24 through 29 decompile to valid Elixir.
-
.heexfiles are now recognized as HEEx, with dedicated syntax highlighting for{@assigns}/{expressions},<% %>/<%= %>tags, and<%!-- ... --%>HEEx comments.~Hsigils now use the same HEEx language as.heexfiles, with separate HTML and Elixir PSI roots, instead of being treated as plain HTML text. Opt-in via Settings → Languages & Frameworks → Elixir → Experimental → "Enable ~H Sigil HEEx language injection".<.component>and<Module.component>tags now resolve to theirdef/defpdefinition - local components, components brought in via an explicitimport, and components brought in viause MyAppWeb, :htmlall navigate to their definition. A dotted name that is not valid component syntax is flagged byHtmlUnknownTagInspectionlike any other unknown tag.- Find Usages and Rename on a
def/defpinclude its HEEx component tags, in.heexfiles and inside~Hsigils; a renamed tag keeps its./module-alias prefix. - Rename works with the caret on a component tag, not only on the
def/defp. - Quick Docs and hover on a component tag show the function's documentation, including inside
a
~Hsigil. - Find Usages finds a plain Elixir call embedded in a
~Hsigil, e.g.{some_function()}.
-
- The
+++and---operators are now parsed. Fixes #2755.
- The
-
- A parameter of an
fnthat has no->yet no longer reports "Use scope for parameter not found before reaching file scope". The scope search stopped at a definition clause, a delegation, a call with a do block or keyword, or a->clause, but not at the anonymous function around them - sofn p end, which is whatfn p -> ... endlooks like part-way through being typed, searched past thefnto the file and was reported. Fixes #2491.
- A parameter of an
-
- A variable whose scope search reaches the top of the file no longer reports "Don't know how to
find variable use scope". The search enumerates the ancestor types it understands and raises an
error report for anything else, so a
<-outside any comprehension - a syntax error, but also a state code passes through while being typed - reached the file itself and was reported. Fixes #3358.
- A variable whose scope search reaches the top of the file no longer reports "Don't know how to
find variable use scope". The search enumerates the ancestor types it understands and raises an
error report for anything else, so a
-
- Resolving
__MODULE__inside a module thatuses another no longer reports a non-idempotent computation. The reference was cached under one key per__MODULE__call, and the platform's parameterized cache never consults the parameter when it looks a value up, so the reference built for oneusecall was handed back to callers that passed a different one - and the two computations disagreed on their dependencies. Completion and Go to Declaration were the usual triggers. Fixes #2205, #3320, #3467, #3477, #3495, #3517, #3592 and #3593.
- Resolving
-
- An
fnwhose body has no->no longer reports "Don't know how to check if variable". The walk that decides whether a name is a variable enumerates the ancestor types it understands and raises an error report for anything else, and an anonymous function missing its clause arrow reaches it while the code is still being typed. Fixes #2376.
- An
-
- A qualified call in a
@typeparameter list no longer raises an error report.@type date(spec.date_type)reached two annotator branches that between them knew every other shape a parameter can take but not a dot-qualified one, so typing it reported "Cannot extract type type parameter name set" and, on the same keystroke, "Cannot highlight types and type parameter declarations". A qualified call names a type in another module rather than declaring a parameter, so it is now left out of the parameter-name set and painted the way a type usage is. Fixes #1835.
- A qualified call in a
-
- HTML inside a
~Lor~Esigil is now highlighted. The injected fragment carried no file extension, so EEx's template-data language fell through to plain text and only the Elixir inside<%= %>was coloured. Still behind the experimental sigil-injection setting. Fixes #1827.
- HTML inside a
-
- Quick Documentation now works on a captured function.
&Callee.multiply/2showed nothing whereCallee.multiply(2, 3)showed its@doc, because a captured name carries no reference of its own - it lives on the enclosing capture operator, the only element that also spans the/arity. - Go To Definition on a capture of a decompiled function now navigates.
&:queue.new/0went nowhere while:queue.new()opened the decompiled module, because the capture dropped every result that was not already source PSI. - Captures are now covered by the Go To Definition, Find Usages, arity and protocol-function suites as well, which previously exercised only ordinary calls. Refs #1810.
- Quick Documentation now works on a captured function.
-
- A module attribute declared in a
__using__macro'squoteblock resolves again. Since 24.0.0 a symbol was only built for a declaration whose enclosing modular could be named, and a declaration insidequoteis enclosed by thequotecall rather than by a module. The scope walk found the declaration and the result was then discarded, so Go To Declaration, Find Usages and completion silently stopped working for every attribute injected throughuse. Refs #1783, #1292.
- A module attribute declared in a
-
- "Align operands of pipe operator (|)" no longer staircases a multi-operand union.
a | b | cparses as nested pipe operations, and each level built its own alignment, so every level anchored its operands one column-step further right than the level above - turning the setting off was the only way to get them lined up. A nested level now shares the alignment of the pipe operation it sits in, matching whatmix formatproduces. Fixes #1787.
- "Align operands of pipe operator (|)" no longer staircases a multi-operand union.
-
- Renaming a variable no longer rewrites an inner binding that shadows it. The shared usage
search matched by name and scope alone, so an
fnparameter,caseclause pattern orforgenerator reusing an outer name was renamed with it. Fixes #1479. - Renaming from a rebinding inside an
fnorcasebody now covers the whole variable.
- Renaming a variable no longer rewrites an inner binding that shadows it. The shared usage
search matched by name and scope alone, so an
-
- "Remove space between function name and parentheses" now removes the space instead of
throwing. The space stopped being a child of the element the quick fix searched when the
grammar gained
noParenthesesOneArgumentin 2015. Fixes #3107.
- "Remove space between function name and parentheses" now removes the space instead of
throwing. The space stopped being a child of the element the quick fix searched when the
grammar gained
-
- A
@specwhose head has no function name no longer crashes the structure view. Heads like@spec foo.() :: termand@spec bar not in baz :: termparse to a call with no name, so the malformed spec is now left out of the tree instead of throwing. Fixes #1564.
- A
-
- Ctrl+Click and highlighting no longer throw on a function the decompiler could not recreate. Refs #3309.
-
- A dep with
allow_pre:no longer reports an unknown Mix dep option. It is a Hex flag that cannot change where a dep is checked out. Fixes #2487.
- A dep with
-
- "Cannot find enclosing Modular" no longer fires for a
definside a map-heldquote, or for a@callbackbehind an infix operator. The walk that finds a definition's enclosing module treated lists and tuples as transparent but not a map's internals, so aquoteheld as a map value stopped it; and it knew=and|>but no other infix operator, so an@callbackon the right of||stopped it too. Every infix operator is transparent now, since a binary operator is never itself a module. Refs #1695 and #1438.
- "Cannot find enclosing Modular" no longer fires for a
-
defstruct do ... endanddefexception do ... endno longer crash the structure view. Both were gated on the arity Elixir resolves, which counts adoblock as an argument even though nothing is actually written inside the call - so the structure view then asserted on an argument list that was empty. Fixes #2107 and #1095.- A piped
defdelegatewith adoblock no longer throws while resolving variables. Same resolved-arity-vs-written-arity mismatch as above, reached throughdefdelegate's own gate.
-
- Auto-Indent Lines and paste no longer throw on a map update with a literal on the left, such
as
%{%{a} | k: 1}.
- Auto-Indent Lines and paste no longer throw on a map update with a literal on the left, such
as
-
- Large integer literals in a decompiled BEAM now show their real value, and the Code tab no longer throws on some files.
-
- Configuring an Elixir SDK no longer crashes PhpStorm, WebStorm and the other small IDEs when another plugin registers a root type of its own.
-
- A trailing comma in a call's argument list no longer breaks the rest of the file. Typing
foo(a,)on the way to the next argument used to discard the enclosing definition and everything after it, taking parameter hints with it. Applies tofoo(a,),Mod.fun(a,)andfun.(a,). - Parameter hints no longer describe functions you are not calling.
Enum.reducelisted eight signatures, includingreduce_while's. Each arity of the called function still gets its own hint. - Parameter hints now appear when a completion is accepted. Accepting a name from the completion popup left the caret between the inserted parentheses with no hint there.
- Pressing Enter after a delimiter now indents the caret where an element goes rather than back
to the enclosing statement: after
[,{,%{,%S{,<<andFoo.{, after a comma, after->in a clause, after therescue,after,elseandcatchkeywords, and inside empty parentheses. Every column is the onemix formatproduces. Refs #799.
- A trailing comma in a call's argument list no longer breaks the rest of the file. Typing
-
- A console path written with backslashes now links on Windows. A compile error prints the path the way the platform writes it, while the virtual file system holds forward slashes, and the two were compared without normalising either - so on the one platform where every console path looks like that, the frame resolved to nothing.
-
- Variable completion no longer shows a parameter's name twice. A parameter that is not bound by a match is its own enclosing match, so the match appended after the name only repeated it. Variables bound by a match still show that match, which is what makes the tail text useful. Fixes #496.
-
- Homebrew Erlang SDKs no longer report an unknown version. The version was read from the
home path after the Homebrew layout adjustment had been applied, so every Homebrew Erlang was
keyed on the literal string
erlanginstead of its version, leaving them unsorted and unlabelled in the SDK list.
- Homebrew Erlang SDKs no longer report an unknown version. The version was read from the
home path after the Homebrew layout adjustment had been applied, so every Homebrew Erlang was
keyed on the literal string
-
\{and\}in HEEx are now literal braces instead of affecting{...}expression nesting.- A component tag's attribute after a
{...}-valued one no longer gets swallowed into that value, e.g.<.tag one={""} class=""/>now parsesclassas its own attribute. {inside<script>/<style>stays literal after an embedded<% %>tag, and</script>/</style>still close the tag.- A self-closing
<script .../>or<style .../>no longer swallows the rest of the file as script or style content. <% %>tags that produce no output (comments,<% if %>) no longer inject placeholder text into the HTML tree.- Other HTML-based template languages are no longer affected by HEEx's outer-language patcher, which ran for every HTML-data template language in the IDE.
- HEEx's special attributes and
phx-bindings are no longer reported as "not allowed here" on HTML tags::let,:if,:for,:key,:typeand everyphx-*binding. <:slot>tags are no longer validated as HTML elements.<:col>was checked against HTML's<col>and<:action>reported as an unknown tag.- A component tag that doesn't resolve no longer shows "Cannot resolve symbol".
- Components brought into scope by a
usenested insideuse MyAppWeb, :htmlnow resolve, such asPhoenix.Component's<.link>and<.live_title>:useresolution now follows a__using__whose body ends in a list of fragments, aquotebound to a variable, or anotheruse. - Rename and Find Usages on a component tag could miss the tag, varying between IDE sessions:
at a tag's offset the Elixir root of a
.heexfile or~Hsigil could win over the HTML root. - An unqualified call inside a
~Hsigil now resolves to adefin the surrounding module.
-
- Reformat Code no longer changes what a capture expression means. Since Elixir 1.15,
&1and& 1are different expressions; the formatter was inserting that space, silently turning&(&1 + &2)into code meaning&(&(1 + &2)). & 1now parses the way the containing module/project's configured Elixir SDK parses it. The plugin previously always used the pre-1.15 meaning.- On Elixir 1.15 and higher:
&applied to the whole following expression. - On Elixir 1.14 and lower: the single capture argument
&1.
- On Elixir 1.15 and higher:
- The stepped-range atom
:..//no longer shows a parse error. It was previously read as:..followed by a stray//. - Decompiled
.beamfiles no longer show a parse error when acondorcaseclause condition ends inend. This fixes decompilation ofMix.Project,Mix.Release, andMix.Taskon Elixir 1.20+.
- Reformat Code no longer changes what a capture expression means. Since Elixir 1.15,
-
- New Elixir projects get the right compiler output directory. Leaving
--appblank in the New Project wizard configured_build/dev/lib/ebinrather than_build/dev/lib/<app>/ebin; it now falls back to the project name, asmix newdoes. --supis no longer offered for umbrella projects, wheremix newsilently ignores it.- New umbrella projects no longer get a stray empty
lib/or an output directory that never exists. An umbrella root has no application of its own. - New Elixir projects and modules are no longer reported as having "an outdated format". They
were written with compiler-output exclusion switched on - the setting the converter exists to
remove - and Elixir compiles to
_build, so it never applied. - Umbrella sub-apps now get their
lib/andtest/marked.mix newwrites an app from an external process, so the IDE could hold a stale view omitting itsmix.exs. The wizard, Reconfigure Elixir Module Setup and the module-setup check now refresh first.
- New Elixir projects get the right compiler output directory. Leaving
-
- Variables bound by a macro call in a match now resolve.
session(id, user) = rawbinds all of the macro's arguments, but every use of them was reported as unresolved: only threeKernelnames were recognised as binding, and any other parenthesised call was assumed to be a function, whose arguments are values. The call is now resolved to decide whether it is a macro. Calls that resolve to a function are unaffected, as are macros that cannot be resolved at all.
- Variables bound by a macro call in a match now resolve.
-
- Saved run configurations no longer record the module twice. Every Elixir run configuration
wrote a second
moduleelement on top of the one the IDE already writes, which the platform logs as "Module serialized more than one time". - "Include system environment variables" is now remembered. The setting was accepted in the run configuration editor but silently discarded when the configuration was saved.
- A run configuration whose module is not loaded yet keeps it. Reading a saved configuration before its module exists no longer clears the module instead of leaving the IDE to resolve it.
- Saved run configurations no longer record the module twice. Every Elixir run configuration
wrote a second
-
- The IDE no longer starts every WSL distro that hosts a registered Erlang or Elixir SDK, and will no longer freeze if WSL is slow or hangs while starting. Checking whether two WSL-hosted SDKs are the same no longer touches the distro's filesystem.
- #3950 @sh41
- The formatter's
Blockis clean under IntelliJ's inspections - 18 warnings down to 3. Four nullable-node dereferences now state theAbstractBlocknon-null invariant,ContainerBlockListReducerstops declaring anIndentnon-null that callers pass as null, and the interpolation overload no longer takes two parameters that were always the same constants.
- The formatter's
-
- The quoter daemon is now started on every test run, not just the first in a checkout. Its files
made
startQuoterlook up to date, but the daemon it starts is stopped again when the build ends, so from the second run onwards the task was skipped while its marker still reportedquoter.available=trueand every test that quotes failed on a PID timeout. The task is now always out of date and never cached. - The quoter's Erlang distribution and
epmdnow bind loopback rather than every interface. Both listeners bound the wildcard address despite the nodes being local, which is what made Windows Firewall prompt for the release'serl.exeand the IDE'sjava.exe, once per worktree. Anepmdthat is already running is used as it is; only a fresh start asks for loopback.
- The quoter daemon is now started on every test run, not just the first in a checkout. Its files
made
-
epmdis now started from the Erlang SDK rather than the quoter's own bundled ERTS. A distributed node startsepmdfrom the ERTS of whatever release is starting, detached, so the one the quoter left behind ran from undercache/inside the checkout. On Windows a running executable pins its own directory, which made deleting a checkout or a git worktree fail with "Device or resource busy" long after the build finished. The build now startsepmdfrom the SDK first and passes-start_epmd false, falling back to the previous behaviour when no SDKepmdis found.
-
- Quoter tests now respect the quoting rules for the Elixir version under test. Six quoted-form divergences are gated on the release that introduced each, so the same fixtures pass on every Elixir/OTP pair CI tests.
- A quoter that fails to build no longer blocks the whole test suite. Tests that require the
quoter fail immediately with the recorded reason; all other tests run as normal.
-PquoterRequired=truerestores the hard stop for anyone debugging the quoter itself. - CI gets the quoter cache paths from the build (a
quoterCachePathstask) instead of re-deriving them in bash, so the workflow can no longer disagree with Gradle about where the cache lives. - The quoter is pinned to a commit of
sh41/intellij_elixirwhich can be built without warnings on all Elixir versions 1.13-1.20, pending merge/tagging of the PR. - The quoter builds with an explicit
MIX_ENV=prod, and only prod dependencies are fetched. An exportedMIX_ENVstill overrides it.
-
- All Elixir/OTP test legs now pass and are required for merge. Getting there also gated two
more quoted-form divergences by version (1.20's
__block__line metadata, pre-1.17 parens metadata) and froze copies of stdlib files that newer Elixirs deleted, so the parsing corpus no longer shifts underneath the tests.
- All Elixir/OTP test legs now pass and are required for merge. Getting there also gated two
more quoted-form divergences by version (1.20's
-
testUIbuilds and runs again.kodein-di-jvmandkotlinx-coroutines-core-jvmwere silently dropped from its dependencies by an earlier commit; both are genuinely required.- The released plugin now compiles against 2025.3, its declared minimum supported version, instead of a newer platform - closing a gap where only the post-release Plugin Verifier check stood between an incompatibility and a real user's install.
- The shipped plugin no longer accidentally bundles its own copy of
kotlin-stdlib. - Bumped
gradle-wrapperto 9.7.0 (with its distribution checksum now verified),org.jetbrains.changelogto 2.5.0, and migratedcom.github.ben-manes.versionsto the maintainedio.github.ben-manes.versionsid at 0.61.0. Dropped the unusedqodanaversion catalog entry (qodana.yml's linter tag is the only declaration now, bumped to 2026.2).
-
- Bumped
kodein-di-jvmto 7.33.0 andgradle-wrapperto 9.7.1.
- Bumped
24.0.1 - 2026-08-09
- @sh41
- The plugin's "What's New" now shows the last six releases instead of the whole history. Each version is listed with its release date, so upgrading after skipping a few releases shows what you missed rather than one undifferentiated list.
- @sh41
- Adding an Elixir SDK registers it again. #3888 - the SDK list stayed empty and "Elixir Facet SDK is not defined" persisted, because setting up the SDK's paths read the SDK table without a read lock and the resulting error aborted registration before the SDK reached the list.
- A newly added Elixir SDK is paired with the Erlang SDK you picked, rather than whichever one happened to be registered first. The chosen SDK is not committed until the dialog is applied, and the pairing was resolved without consulting the dialog's uncommitted SDKs.
- The "Configure from mise" action no longer goes missing from the SDK banner. The startup scan request could be dropped before its collector was listening, leaving the project with no tool manager scan at all.
- #3901 - @sh41
- Semantic syntax highlighting now appears in decompiled
.beamfiles. Function names, types and the rest were being coloured by the annotators and then discarded before they reached the editor, so decompiled code showed only the plain lexer colours. v24.0.0 announced this as working; it was not. - Opening a decompiled
.beamfile no longer reports "PsiFile's context does not match the context of the editor", which reached users as an IDE error report.
- Semantic syntax highlighting now appears in decompiled
- @sh41
CHANGELOG.mdis now in Keep a Changelog format and is the single source of the plugin change notes, rendered by the Gradle Changelog Plugin.resources/META-INF/changelog.html, which held a separately hand-written copy, is removed - it had gone stale, so 24.0.0 shipped with v23.9.0 as its newest section.- Every pull request now needs a
CHANGELOG.mdentry, checked bychangelog.yml: it must land under## [Unreleased], be a list item, and use a known group. Apply theno-changeloglabel when there is genuinely nothing to record. - The Tag Release workflow validates the tag shape against the
prereleaseinput, that a release is cut frommain, that the tag is new and increases, and that it matchespluginVersion. - Plugin verifier reports are now uploaded whenever a verification leg fails, including on pull
requests. Previously the upload was gated on an input that is always empty for
pull_request, so the reports were unobtainable on exactly the runs that needed them and the evidence had to be dug out of a raw job log. Green runs still upload nothing unless a caller asks. - Everything scoped to an Elixir/OTP pair is now keyed on the pair:
MIX_HOMEjoinsMIX_ARCHIVES, and the quoter's_build/depsjoin both. Two OTPs for one Elixir previously shared a build tree and overwrote each other, and switching versions re-downloaded hex and rebar. The first build per pair after this change reinstalls and rebuilds once. - CI now covers OTP 25, and runs a decompiler sweep against OTP 28 for the first time. Two
beam.additionalpairs were added,1.13.4 / 25.3.2.21and1.18.4 / 28.4, chosen to cover OTP majors rather than Elixir minors: most of the decompiled surface is Erlang and the BEAM chunk formats track OTP. OTP 25 is now a supported pair - measured locally at 6 560 tests, 0 failures, so its leg is required and any later change that breaks it fails the pipeline. OTP 28 stays informational: its 291 failures are all pre-existing Elixir-keyed quoting cases, with no OTP-28 decompiler failures. - Switching Elixir/OTP versions now re-runs the tests. The versions reach the test JVM as environment
variables, which Gradle could not see, so
mise use erlang@X elixir@Yfollowed bycheckreported the previous pair's results - the task was up to date, and the build cache would even restore those results after acleanTest. Both test tasks now declare the versions as inputs. - A transient 5xx from JetBrains' artifact CDN no longer loses a whole test leg. Gradle already treats
repository server errors as retryable, but its default budget is three attempts over about three
seconds;
gradle.propertiesnow allows eight from a 3-second doubling backoff, roughly six minutes, for every build - CI and local alike. - CI restores the Elixir quoter cache again - it never had.
actions/cacheidentifies an entry by key and by a version hashed from the literalpath:lines, and the restore and save steps listed different paths, so they addressed different entries under one key. Every leg missed, rebuilt its quoter dependencies from Hex, then failed to save against the entry an earlier run had left there - reported asUnable to reserve cache, which reads as a harmless race between legs. Both steps now take the path list from one place. - The test results comment now leads with failures on the required legs, the only figure that
decides whether a pull request can merge, and reports
tests per legin place of a cross-leg union that read 12,913 for a run in which no leg ran more than 6,659.
24.0.0 - 2026-08-04
- #3866 - @sh41
- Rename (Shift+F6) now works for functions,
name:aritypairs, modules, module attributes, types,@callbacks, protocol functions, and atoms, propagating across every usage site -- including keyword-key usages likeimport ... only:,@compile inline:,@dialyzer, anddefoverridable. - New navigation:
name: arityfunction references support Go-to-Declaration/Find Usages/rename;@callback/@macrocallbackdefinitions navigate to and from their implementations; protocol functions support Find Usages to call sites; Ctrl-click on aGenServer.call/castorProcess.send/send_aftermessage now jumps to the matchinghandle_call/handle_cast/handle_infoclause. - Type variables (e.g.
ain@type box(a) :: {:box, a}or@spec ... when a: term()) now support Go-to-Declaration, Find Usages, and rename, scoped to the enclosing type/spec. - Decompiled
.beamfiles now navigate like source. Ctrl-click and Find Usages work from and into decompiled definitions and their call/spec sites, and semantic syntax highlighting now runs over decompiled text. - Completion improvements: function-capture names (
&map_it/0) filtered by arity; function names inside MFA-style atom references ({Module, :function, args}); module-attribute references; rebound/shadowed variables and implicitly-importedKernelnames no longer offered twice. - Two new inspections: "Type variable used once" flags a type/spec variable referenced only once; "Unresolvable type" flags type names in specs that don't resolve to any type definition.
- Rename (Shift+F6) now works for functions,
- #3868 - @sh41
- Decompiled BEAM files now show real
@type/@typep/@opaquedefinitions instead of collapsing them all toterm(). For example, Erlang's:queuenow decompiles its opaque type with its real parameters and body. - Map comprehension generators (
m_generate, OTP 26+) in decompiled code now render as a proper{key, value} <- mappattern instead of an unparseable expression.
- Decompiled BEAM files now show real
- #3873 - @sh41
- Small IDE (RubyMine, PyCharm, etc.) users can now configure Elixir SDKs from mise. Tool manager support -- including the Settings → Elixir → Tool Managers opt-in page -- is now available in small IDEs, not just IntelliJ IDEA. When mise has an Elixir installed for a module, a "Configure from mise" button appears on the "SDK is not defined" editor banner and next to that module's SDK selector in Settings → Elixir. One click creates the Elixir and Erlang SDKs and selects them for that module.
- Settings → Elixir now guides SDK setup. The page explains that SDKs are added on the SDKs / Internal Erlang SDKs child pages and links directly to them. Each module's SDK selector shows a live status line (resolved Elixir and Erlang SDK names, or what is wrong -- no SDK, invalid SDK, missing Erlang SDK), matching the status bar widget's wording. In small IDEs the page now lists all modules, including projects originally created in IntelliJ IDEA, which previously had no way to pick a per-module SDK in small IDEs.
- #3856 - @sh41
- Mise users: the plugin now watches your mise config files and re-detects SDKs automatically. New opt-in per-project settings page (Settings → Elixir → Tool Managers) controls which tool managers are active. Running
mise trustor editing.mise.tomltriggers a re-scan without restarting the IDE. Errors like "untrusted config" are now shown in notifications instead of silently ignored. - SDK setup on project open is more reliable. The initial SDK notification scan now waits for the IDE's internal project model to finish loading, preventing a race where newly registered SDKs could disappear moments after being added.
- Mise users: the plugin now watches your mise config files and re-detects SDKs automatically. New opt-in per-project settings page (Settings → Elixir → Tool Managers) controls which tool managers are active. Running
- #3846 - @sh41
- You'll now be warned if your Elixir SDK was compiled against a different OTP version than your configured Erlang SDK. Warnings appear in Project Structure (Additional Data panel), the status bar widget balloon, and the "Refresh All Elixir SDK Paths" summary. You can suppress the warning per-SDK if the mismatch is intentional.
- #3845 - @sh41
- SDK version detection is faster and works without a working Elixir installation. The plugin now reads the version directly from the
elixir.appfile instead of runningelixir --short-version. SDK names show the OTP major version (e.g.mise Elixir 1.15.7-otp-26). Mise detection now honours.tool-versionsand parent-directory configs viamise ls --current.
- SDK version detection is faster and works without a working Elixir installation. The plugin now reads the version directly from the
- #3851 - @sh41
- The External Libraries tree is easier to navigate. SDK library roots now show their OTP app name (e.g. "phoenix" instead of a raw path), and
.exsource files appear alongside.beamfiles.
- The External Libraries tree is easier to navigate. SDK library roots now show their OTP app name (e.g. "phoenix" instead of a raw path), and
- #3861 - @sh41
- Run/Debug gutter icons now work in WSL-hosted projects. Icons indicating test status also propagate from individual
test/describeblocks up to the enclosingdescribeanddefmodule, so you can see the status of the entire group from the gutter. The plugin reads simpletest_load_filters/test_pathsfrommix.exsfor correct test file detection.
- Run/Debug gutter icons now work in WSL-hosted projects. Icons indicating test status also propagate from individual
- #3858 - @sh41
- Autocomplete now includes functions from BEAM-only (Erlang) dependencies. Decompiled exported functions appear in unqualified completion with
/aritytail text and a proper icon. - BEAM viewer improvement (View → Tool Windows → BEAM Viewer): the StrT (string table) tab now shows individual strings with their lengths and auto-sizes columns.
- Autocomplete now includes functions from BEAM-only (Erlang) dependencies. Decompiled exported functions appear in unqualified completion with
- #3852 - @sh41
- Fewer "unknown AST node" warnings when decompiling OTP 26+ BEAM files. Map comprehension nodes (
m_generate,mc) introduced in OTP 26 are now decompiled correctly.
- Fewer "unknown AST node" warnings when decompiling OTP 26+ BEAM files. Map comprehension nodes (
- #3836 - @joshuataylor
- Mix settings and Mix deps checking reworked, Experimental Settings added, and mise SDK detection improved.
- #3843 - @sh41
- The New Project Wizard now configures the Elixir SDK correctly. SDK type handling was also split into single-responsibility objects.
- #3891 - @makoto-developer
- README typo fixes (
Subcription,referneced,Configuations,Wih) - a first contribution, thank you!
- README typo fixes (
- Elixir settings consolidated -- renamed "Experimental Settings" to "Elixir Settings" and moved all settings into the top-level Elixir configurable (no more separate child page). - @joshuataylor
- Mix deps checker setting -- added an "Enable automatic Mix deps checking" toggle (default enabled) under Elixir Settings. When disabled, no deps check runs on project open or file changes. The checker also skips with a debug log when no Elixir SDK is configured, instead of showing the unhelpful "Mix deps check failed" notification. - @joshuataylor
- Erlang SDK prompt for mise Elixir SDKs -- when adding a mise-detected Elixir SDK without an Erlang SDK registered, a chooser dialog now lists valid mise-installed Erlang SDKs (sorted newest first, broken installations filtered out). The selected Erlang SDK is registered and linked automatically. - @joshuataylor
- Status bar widget -- when no Elixir SDK is configured, the widget popup now shows a "Detected Elixir SDKs" section listing valid mise installations. Clicking one registers the Elixir SDK, prompts for Erlang if needed, and sets it as the project SDK. - @joshuataylor
- When an Elixir SDK is added via Project Structure and no Erlang SDK is explicitly set,
configureInternalErlangSdknow falls back to any Erlang SDK already registered inProjectJdkTable. - @joshuataylor
- #3873 - @sh41
- The "Setup Elixir Module SDK" / "Setup Elixir Facet SDK" editor banner links now work in small IDEs. They previously called a Project Structure API that is a silent no-op in RubyMine and other small IDEs; they now open the Elixir SDK settings.
- Settings → Elixir → SDKs / Internal Erlang SDKs no longer show an empty list when SDKs exist. The shared SDK model was initialised without a project, which loads nothing from the SDK table.
- Removing an SDK in Settings → Elixir → SDKs no longer throws and the removed SDK no longer lingers as a "ghost" entry in the per-module SDK chooser.
- SDKs registered outside the settings dialog (e.g. via "Configure from mise") now appear in Settings without restarting the IDE. The settings model refreshes when the SDK table changes.
- #3849 - @sh41
- Ctrl-click on
div,rem,is_niletc. now navigates to Elixir source instead of landing on a.beamfile. The resolver now sorts source results before decompiled ones. describe/testblocks resolve correctly when usingExUnit.CaseTemplate. Previously these showed?icons in the structure view and wouldn't navigate. Now the plugin followsuse MyApp.ConnCasechains to find the underlyingExUnit.Casemacros.- Go-to-Declaration is less noisy in multi-module projects. Resolver scope narrowed from the entire project to the current module's dependencies, reducing false matches from unrelated modules and/or SDKs.
- Navigation works from VCS diff views. Synthetic files (e.g. the diff editor) now resolve to their real on-disk counterparts.
- Ctrl-click on
- #3850 - @sh41
- Go-to-Declaration on the
Stringmodule (and other modules with\u{…}in their docs) now works. Previously the lexer mishandled Unicode escape sequences inside~S"""…"""heredocs, corrupting the parse tree for the rest of the file and causing navigation to fall through to.beam.
- Go-to-Declaration on the
- #3839 - @sh41
- IDE no longer freezes when dependencies change. The dependency sync system has been rewritten from a legacy thread-based watcher to a coroutine pipeline. Changing
mix.locknow re-scans only the affected project root instead of all roots. Notifications show which module is affected.
- IDE no longer freezes when dependencies change. The dependency sync system has been rewritten from a legacy thread-based watcher to a coroutine pipeline. Changing
- #3848 - @sh41
- Dialyzer and Credo inspections now analyse the latest code. Open documents are saved before the inspection runs, so on-disk state matches what you see in the editor.
- Umbrella sub-app path dependencies are now classified correctly during mix sync (previously could appear under the wrong module).
- Debugger no longer risks a threading crash when gathering SDK paths at session start.
- #3855 - @sh41
- Run configurations are more stable on 2025.3+. Internal threading contracts are now enforced on the code paths that build Mix/Elixir/IEx command lines, preventing potential crashes when launching run configs.
- #3841 - @sh41
- Decompiling
.beamfiles built by newer OTP no longer fails on unknown instructions. AddsTypedRegister(OTP 25+) and opcodes 177-191 (OTP 25-29) to the BEAM decompiler.
- Decompiling
- #3837 - @sh41
- Elixir code blocks inside
@doc/@moduledocheredocs inject correctly. Credo and Dialyzer also gained module guards.
- Elixir code blocks inside
- #3844 - @sh41
- An Elixir SDK keeps a stable identity across restarts, keyed on its Erlang home path rather than a derived variant name.
- #3834 - @joshuataylor
- Rethrow
ProcessCanceledExceptionin spell checkingSplitterinstead of swallowing it. - Fix intention preview for
ConvertMatchToTypeOperation.
- Rethrow
- #3835 - @joshuataylor
ProgressManager.checkCanceled()added towhileloops in@RequiresReadLockmethods.- Replace
Dispatchers.EDTwithDispatchers.UIinElixirSdkStatusWidget(EDT holds write-intent lock unnecessarily for pure UI updates). - Use no-arg
AnActionconstructor inRefreshAllElixirSdksActionto fix DevKit inspection.
- No user-visible behaviour changes. These improve long-term stability and IDE responsiveness.
- #3858 - @sh41
@RequiresReadLock/@RequiresBackgroundThreadannotations added across PSI, structure view, navigation, SDK, mix, and utility layers.ProgressManager.checkCanceled()added in large-set iteration loops (improves cancellation responsiveness during long operations).- Numerous deprecated-API replacements for forward compatibility with future IDE versions.
- Shadowed extension functions renamed to avoid GrammarKit member conflicts.
- No user-visible changes. These affect contributors and CI infrastructure only.
- #3855 - @sh41
- GitHub Actions now summarizes the first ≤10 failed tests in the job summary.
- Reverted to upstream
setup-beamaction (win25 runner issues resolved upstream).
- #3850, #3845 - @sh41
- CONTRIBUTING.md: corrected JFlex regeneration instructions;
ElixirFlexLexer.javamoved fromsrc/togen/; documented 253 API constraints for debugger and terminal console.
- CONTRIBUTING.md: corrected JFlex regeneration instructions;
- #3863 - @sh41
- Plugin artifact uploaded unpacked so downloading from GitHub Actions produces a ready-to-install zip without double-wrapping.
- #3879 - @sh41
- Consolidated internal usage of
JpsProjectLoadingManager(SDK-sync API) to a single, documented call site.
- Consolidated internal usage of
- #3877, #3878 - @joshuataylor
- Verified compatibility against IntelliJ IDEA 2026.2 EAP (262.8665.258); dependencies pinned to bundled platform versions.
- #3875 - @sh41
- CI test results now published via
workflow_runinstead of the same-run job.
- CI test results now published via
- #3874, #3864 - @sh41
- Routine dependency bumps, including
org.jetbrains.qodanato 2026.1.3.
- Routine dependency bumps, including
- #3882 - @sh41
- Plugin verifier: explicitly declare the dependency on
intellij.testRunner.pluginso verification doesn't fail on its absence.
- Plugin verifier: explicitly declare the dependency on
- #3847 - @joshuataylor
vendorNameand copyright headers updated to the maintainer's current name.
- #3854 - @joshuataylor
- Canary releases built by a single workflow.
- #3862 - @sh41
- Dependency and Gradle bumps; added the
dependencyUpdatestask plugin.
- Dependency and Gradle bumps; added the
- #3884 - @sh41
setup-envdefaults to the minimum supported platform (2025.3.6).
- #3886 - @dependabot
- Bump
actions/checkoutfrom 7.0.0 to 7.0.1.
- Bump
- #3890 - @joshuataylor
- Worked around a
publish-unit-test-result-actionfailure.
- Worked around a
- #3895 - @sh41
- Test Results checks no longer fail the run when tests fail.
23.8.2 - 2026-05-16
- #3822 - @sh41
- Moved 108 hand-written
.kt/.javafiles fromgen/tosrc/-- these files were vulnerable to silent overwrite by parser regeneration. Regeneratinggen/previously clobbered them with GrammarKit stubs, causingStackOverflowErrorat runtime when hand-written interface names matched BNF rule names (visitor generates self-recursive methods). - Renamed 3 PSI interfaces to avoid GrammarKit visitor collisions:
Heredoc->HeredocLiteral,HeredocLine->HeredocLineable,SigilHeredoc->SigilHeredocLiteral. - Consolidated ambiguous
ElixirPsiImplUtiloverloads (processDeclarations,getNameIdentifier,getReference) using Java 21 pattern matching switch expressions so that hand edits are no longer needed in generated files after parser regeneration. - Marked
gen/as generated sources in Gradle (idea.module.generatedSourceDirs) to suppress inspections on GrammarKit-generated PSI classes. CONTRIBUTING.mdupdated with comprehensive GrammarKit usage conventions: rule names vs interface names, visitor collision avoidance,extends/mixin/fakerules,ElixirPsiImplUtilmethod resolution and ambiguity pitfalls, source layout (gen/vssrc/), CRLF conversion, and testing workflow after BNF changes.
- Moved 108 hand-written
- #3821 - @sh41
- MFA tuple reference resolution: Go-to-Declaration, Find Usages, and hover documentation now work on
:functionatoms inside{Module, :function, arity}MFA tuples. Covers Supervisor child specs,@doc delegate_to:attributes, and general MFA references throughout Elixir code. - Supports both Elixir modules (
{Enum, :map, 2}) and Erlang modules ({:math, :sqrt, 1}). Arity can be an integer literal, an args list ([arg1, arg2]), or a dynamic expression/wildcard (resolves withisValidResult=falsefor navigation support without error highlighting). - Implemented via
MfaTupleReferenceContributor+MfaTupleReferenceProvider+MfaFunctionReference(poly-variant, usesResolveCache).ElixirAtomMixinregistered asHintedReferenceHostsoPsiReferenceServicepicks up contributed references. Soft reference -- unresolvable MFA tuples produce no error highlighting. - New
UnresolvableModuleQualifierinspection (enabledByDefault=true, error level) flags unresolvable module qualifiers in qualified calls. Highlights the qualifier itself, not the entire call expression. - False-positive guards: dynamic qualifiers (module attributes, variables, function call results, bracket access, chained calls), injected doc code fragments (
@doc/@moduledocheredocs), non-source roots, PhoenixRouter.Helpers(verifies parent Router module exists, handles both FQN and aliased forms), and opaqueusecalls where__using__/1macro cannot be traced (e.g.ExUnit.CaseTemplate). - Resolver source-over-decompiled preference consolidated into
Resolver.preferred(). Previous order:valid -> same-module. New order:valid -> source -> same-module. RedundantpreferSource()call removed fromTargetElementEvaluator.getTargetCandidates().Nested.ktordering updated to match.
- MFA tuple reference resolution: Go-to-Declaration, Find Usages, and hover documentation now work on
- #3833 - @joshuataylor
- Added
@RequiresReadLockannotations to PSI-accessing methods across 10 files (CallDefinitionClause,Definition,Implementation,Module,Protocol,ElixirPsiImplUtil,PsiElementImpl,PsiNamedElementImpl,CallImpl,CanonicallyNamedImpl). Enables theThreadingConcurrencyinspection to statically detect callers that don't hold the read lock.
- Added
- #3832 - @joshuataylor
BulkDecompilation-- wrappedModuleManager.modules,ModuleRootManager.sdk/.contentRoots, andProjectRootManager.projectSdkaccess inreadAction {}. Previously accessed onDispatchers.Defaultwithout a read lock, causing crashes on 2025.2+.DirectoryConfigurator-- wrappedModuleManager.getInstance(otpAppProject).modulesinReadAction.nonBlockinginsideTask.Backgroundablefor newly attached umbrella sub-projects.
- #3831 - @joshuataylor
- Dialyzer inspection -- use explicit
runReadActionblocks around PSI and module-model access instead ofisReadActionNeeded = true. The previous approach (isReadActionNeeded = true) held the read lock for the entire inspection run includingwaitFor()on the Dialyzer process, blocking writes. Now the read lock is acquired only for the PSI/model access windows. - Status bar SDK widget -- restructured
updateWidget()to rundetectSdkStatus()on the widget's coroutine scope (background thread with read action) and dispatch UI updates viaDispatchers.EDT. Previously ran SDK/module-model queries without a read lock, crashing on 2025.2+. - Reference search (
ReferencesSearch.java) -- wrapped PSI access (Implementation.is,Module.is,getName,getLanguage) inReadAction.nonBlocking().executeSynchronously().processQueryruns on a pooled thread without a read lock. OtpApp.kt-- mergedelixirFile()andappList()PSI traversal into a single read action. PreviouslyappList()ran heavy PSI walking (.modulars(),.macroChildCallList(),nameArityInterval()) outside the read action returned bycomputeReadAction.DepsWatcherandDepsCheckerService-- wrappedProjectRootManager,ModuleManager, andModuleRootManageraccess inReadAction.nonBlocking. These run onAlarm(POOLED_THREAD)callbacks without a read lock.- Mix
Watcher-- wrappedModuleRootManager.getInstance(module).contentRootsinReadAction.nonBlocking. Runs insideTask.Backgroundablewithout a read lock. - Credo inspection -- wrapped
ModuleManager.getInstance(project).modulesmodel access in theworkingDirectorySet()method with a read action. The inspection usesisReadActionNeeded = falseand module-model access was unprotected. ReconfigureModuleSetupAction-- wrappedModuleManager.getInstance(project).modulesandElixirSdkType.mostSpecificSdk(project)inupdate()withReadAction.nonBlocking.ActionUpdateThread.BGTdoes not guarantee a read lock since 2024.2.- Downgraded non-critical decompiler/documentation-provider errors from
Logger.error()tologger.warn().Logger.error()creates aThrowableand shows an IDE error notification in internal mode -- too noisy for situations where code simply doesn't recognise an element (unknown Erlang AST nodes from newer OTP versions, missing decompiled functions, unhandled element types).
- Dialyzer inspection -- use explicit
- #3830 - @joshuataylor
- Bumped IntelliJ 2026.1.x target to 2026.1.2.
- #3832 - @joshuataylor
- Bumped Gradle to 9.5.1.
- Bumped intellij-platform plugin to 2.16.0.
- #3831 - @joshuataylor
- Bumped JetBrains JDK to
jetbrains-21.0.10-b1163.110.
- Bumped JetBrains JDK to
23.5.0 - 2026-05-15
- #3817 - @sh41
- All Elixir settings panels (Credo, Dialyzer, SDKs, Experimental Settings) grouped under a single "Elixir" parent configurable in both full IDEs and small IDEs (RubyMine, PyCharm, etc.). Dropped redundant "Elixir" prefixes from child panel display names.
- Top-level configurable selection refactored to a service-provider strategy (
TopLevelElixirConfigurableFactory) with small IDE and rich platform implementations, replacing theisSmallIdeclass detection approach. - Settings search indexing via
ElixirSearchableOptionContributor-- typing "credo", "dialyzer", "elixir", or "liveview" in the Settings search box now surfaces the relevant Elixir settings pages. - Credo inspection -- umbrella project support:
resolveCredoWorkingDirectorywalks up fromapps/<app>content roots to the umbrella root so Credo runs once per umbrella root instead of once per app. Working directories deduplicated by ancestor path. - Credo inspection -- execution failure surfacing: failures (missing SDK, missing Credo dependency, compilation errors) now appear as inspection problems on
mix.exsand as aggregated IDE notifications, instead of being silently dropped. - Credo inspection -- partial result preservation: when a Credo run emits some findings before hitting a fatal error, the findings are kept and a warning notification is shown alongside them.
- Flycheck output parsing extracted and hardened: two-phase approach (record split, then location parse) correctly handles line+column, line-only, and file-level findings. Invalid paths and out-of-range offsets logged at debug level instead of crashing the inspection run.
--mute-exit-statusadded to Credo command line so lint-level exit codes are not treated as execution failures.
- #3817 - @sh41
- Credo inspection read-action lock churn: consolidated 4-6 separate
runReadAction(Computable { })calls per output line into a singlerunReadAction {}block per finding. Under 2025.3+ writer-preference locking, the repeated lock acquire/release blocked the EDT when a write action was pending. Partially fixes #3790. - Credo "Configure credo" notification action used internal
ShowSettingsUtilImplAPI -- replaced withShowSettingsUtil.getInstance()and addedproject.isDisposedguard. - Credo and Dialyzer configurable IDs (
"Credo","Dialyzer") collided with display names -- separated into distinctlanguage.elixir.credo/language.elixir.dialyzerIDs. - Java-style
//comment inplugin.xmlreplaced with proper XML<!-- -->comment.
- Credo inspection read-action lock churn: consolidated 4-6 separate
23.4.0 - 2026-05-15
- #3820 - @sh41
- Status bar SDK widget -- module SDK inconsistency detection: detects dangling SDK references (module
.imlreferences an SDK name that no longer exists in the JDK table, e.g. project cloned between WSL distributions) and SDK mismatches (module uses a different SDK than the project SDK). Notifications fire reactively with deduplication and include specific navigation instructions. - Status bar SDK widget -- folder mark validation: detects misconfigured source/test/excluded folder marks on Mix project modules. Uses debounced
rootsChanged()viaMutableSharedFlow+ 2-second debounce to prevent thrashing during bulk operations. Tracks active notifications and expires them when the issue resolves. - "Reconfigure Elixir Module Setup" action (
ReconfigureModuleSetupAction): additively applies canonical folder marks and fixes dangling/mismatched SDK entries. Preserves user-customised source roots, skips non-existent directories and non-Elixir modules. Available via Tools menu and status bar widget popup. CANONICAL_FOLDER_MARKSunification: New Project Wizard-created projects now get the full mark set (lib/,web/,spec/,test/, plus 7 exclusions). Previously NPW projects missedspec/(ESpec),web/(pre-Phoenix 1.3), and all exclusions.ProjectModuleSetupValidator: inspects every Mix module's content entries against canonical marks and returns a list of discrepancies.- Module type utilities:
isElixirModule()andgetMixContentRoots()helpers inModuleExtensions.kt. isSmallIdedetection fix:ApplicationInfoproduct codes instead of class detection (class detection broke in 2026.1).
- Status bar SDK widget -- module SDK inconsistency detection: detects dangling SDK references (module
- #3820 - @sh41
- "Run Mix ExUnit" context menu missing on test directories containing non-matching
.exfiles in subdirectories:containsFileWithSuffixrecursive directory walker returnedfalse(stop) on non-matchingElixirFileinstead oftrue(continue), and thePsiDirectorybranch propagated that premature stop. RenamedFinder.kt->ContainsFileWithSuffix.kt. Fixes #3804. - Replaced deprecated
SystemUtils.isWindows/isMac(Apache Commons) with IntelliJSystemInfo/OSutilities across the codebase. - Replaced
commons-lang NotImplementedExceptionwithUnsupportedOperationExceptionacross all uses. - Replaced deprecated
SimpleConfigurable.create(Getter)withSupplieroverload. detectSdkVersionEDT guard: guarded withrunWithModalProgressBlockingto prevent blocking on the EDT. Possibly addresses #2980.
- "Run Mix ExUnit" context menu missing on test directories containing non-matching
23.3.0 - 2026-05-15
- #3819 - @sh41
- Erlang external documentation support: parse external
.chunkfiles (<app>/doc/chunks/<module>.chunk) and normalise doc payload across binary, charlist, and structured-term (application/erlang+html) variants. Covers OTP 23, 26, and 27 packaging layouts. BEAM decompilation now loads external chunk docs into generated mirror source. - Erlang module resolution via atom qualifier syntax:
:math.sqrt(2),:ets.lookup(table, key)and similar calls now resolve correctly.maybeModularNameToModulars()widened fromSet<Call>toSet<PsiNamedElement>to include BEAM-decompiledModuleImplinstances. Unblocks Go-to-Declaration, Find Usages, and autocomplete for all Erlang modules used with atom qualifier syntax. - Syntax highlighting in Quick Documentation code blocks: registered
CodeBlockHtmlProviderandCodeFenceHtmlProviderinMarkdownFlavourDescriptor. Indented code blocks default to Elixir; fenced blocks dispatch by language hint with Elixir fallback.RenderedDocCodeBlockRendererapplies semantic overlays for alias, function call, macro call, and declaration styling. - Erlang atom hover resolution: atom targets routed explicitly in documentation lookup with richer function head presentation using metadata-derived spec signatures.
isDocumentationHostconsolidated into single source of truth inPsiLanguageInjectionHost, used by both the injection host and the markdownInjector.
- Erlang external documentation support: parse external
- #3819 - @sh41
- Quick Documentation (hover/Ctrl+Q) for qualified function calls like
Enum.map(list, fun)andGenServer.call(pid, msg)-- previously showed no docs or fell back to module doc. Four root causes fixed:ElixirRelativeIdentifiernot forwarded ingetCustomDocumentationElement;singleOrNullreturningnullfor multi-clause resolutions (replaced withfirstOrNull);filterIsInstance<Call>()droppingCallDefinitionImplfrom BEAM-only modules; arity-relaxed fallback not filtering by exact function name. Fixes #3636. - Hover docs for BEAM macros with default arguments (e.g.
Logger.info("hello")): addedDocs.documentedByNameFallback()that searches the name'sTreeMapfor the nearest arity.BeamDocsHelpernow dispatches byDefinitionkind (function vs macro). Possibly addresses #3650, #3553, #3552, #3324, #3468, #2691. - Red parser-error squiggles in Elixir code blocks injected into
@doc/@moduledoc/@typedocheredocs suppressed viaDocCodeBlockHighlightErrorFilter-- documentation snippets are inherently partial and should not show errors. @delegate_todoc attributes no longer trigger "Do not know whether to inject Markdown" error log entries.do/end/fnkeywords in injected doc code blocks now styled correctly via a newKeywordannotator scoped to injected fragments.- Erroneous
startInjecting(MarkdownLanguage.INSTANCE)removed frominjectElixirInCodeBlocksInQuote-- the function injects Elixir into indented code blocks; starting a competing Markdown injection was incorrect.
- Quick Documentation (hover/Ctrl+Q) for qualified function calls like
23.2.0 - 2026-05-15
- #3818 - @sh41
- Code completion deduplication: multi-clause functions (e.g.
Enum.map_everywith 5 clauses) now appear once in completion results instead of once per clause head. SharedPreferFunctionHeadlogic selects bare function heads over implementation clauses. - Parameter info deduplication: parameter hints grouped by
(name, arity)-- separate arities still show distinct hints, but multiple clauses of the same arity no longer produce duplicate entries. - Completion prefers source-defined modulars over BEAM stubs when both are available, eliminating duplicate completion entries.
- Transitive alias resolution: when stub-index lookup finds nothing for a module name that is itself a
QualifiableAlias, resolution now follows alias chains transitively. Possibly addresses #1806. DefinitionsScopedSearchcancellation: addedProgressManager.checkCanceled()at loop boundaries and honourProcessor.process()return value for early-exit, preventing hangs during large-project searches.
- Code completion deduplication: multi-clause functions (e.g.
- #3818 - @sh41
- Breaking change: removed
nameArityInAnyModuleglobal fallback from resolver. Previously, whenresolveInScopefound no results, the resolver fell back to a global stub-index search returning every function with a matching name from every module (all markedvalidResult=false). This polluted parameter hints with unrelated modules (e.g. hoveringEnum.map()showed hints fromStream.Reducers,Ecto,Phoenix), caused Go-to-Definition to navigate to wrong-module definitions, and filled the resolution cache with irrelevant results. Calls that were previously "resolved" to functions in unrelated modules will now correctly appear as unresolved references. - Infinite loop in
UnaliasedName.upwhen resolvingQualifiedMultipleAliases-- function overload ordering caused mutual recursion. - Infinite recursion and NPE prevention in PSI resolve/tree-walk paths via
RecursionManager.doPreventingRecursion()and null-safeVISITED_ELEMENT_SETaccess inResolveState. @specline marker grouping checked arity equality before name equality -- specs for different functions with the same arity were incorrectly grouped together.- Gutter icons anchored to leaf
PsiElements per theLineMarkerProvidercontract. Non-leaf elements caused markers to blink or appear in wrong positions after edits. - Removed redundant
computeReadAction/runReadActionwrappers fromCallImplgetters (functionName,moduleName,resolvedPrimaryArity) andPsiNamedElementImplname getters. These trivial PSI reads were called from paths already holding a read lock; under 2025.3+ writer-preference locking, re-acquiring blocks the EDT when a write action is pending. Partially fixes #3790. Elixir.prefix stripping for module name resolution -- stub index stores names without the prefix, soElixir.Enumlookups now match correctly.
- Breaking change: removed
23.1.0 - 2026-05-15
- #3816 - @sh41
- Project-scoped coroutine service (
ElixirCoroutineService) withsupervisedChildScopefor structured, lifecycle-bound concurrency across plugin subsystems. - Debugger runtime (Process, Node, MailBox) migrated from unmanaged
executeOnPooledThreadto structured coroutine scopes with cooperative cancellation viaensureActive(). CleanCancellationExceptionhandling prevents false error reports on shutdown. - Serialised node-facing debugger operations on a dedicated single-lane
nodeDispatcher, preventing concurrent BEAM RPC/network calls and preserving operation ordering. - WSL debugger source resolution: when a BEAM-reported source path is Linux-style and doesn't resolve locally, converts to Windows UNC via WSL compat and retries. Applied to breakpoint hit navigation, stack frame source mapping, and failed-breakpoint presentation. WSL distribution cached on the debugger process to avoid repeated lookups.
mix formatintegration replaced: removed legacyMixFormatExternalFormatProcessor, addedMixFormatFormattingServiceusingAsyncDocumentFormattingService. Improved stderr parsing, notification rendering, and error offset navigation.- CLI ANSI toggle: added
ansiparameter to control ANSI escape codes in subprocess output. Disabled for formatter subprocess to keep parsing deterministic. - Breakpoint availability hot-path optimisation: added
isInsideModule()for cheap boolean module-boundary checks, avoiding full module-name assembly.getModuleName()traversal now stops at file boundaries to avoid directory traversal. - SDK setup modernisation: replaced hand-rolled
invokeAndWait+ boolean-flag pattern inSdkRegistrarwithedtWriteAction {}.registerOrUpdateErlangSdk/registerOrUpdateElixirSdkare now suspend funs. ReplacedProgressManager.runProcessWithProgressSynchronouslyinerlang/Type.setupSdkPathswithrunWithModalProgressBlocking + withContext(Dispatchers.IO). - Removed unnecessary environment picker complexity from dependent SDK creation flow.
- Removed blocking pooled-thread wrappers from SDK lookup; tightened read-action/background boundaries.
- Project-scoped coroutine service (
- #3816 - @sh41
- Debugger node network calls dispatched off EDT via serial
nodeDispatcher, fixing EDT blocking during debug sessions. - VFS blocking read in debugger breakpoint availability checks --
getModuleName()directory traversal triggered blocking disk reads on the VFS, now stops at file boundaries. Partially fixes #3790. - WSL Linux paths not resolving for debugger source navigation -- breakpoint hits, stack frames, and failed-breakpoint messages now resolve correctly when the BEAM reports Linux-style paths on Windows.
- Erlang SDK
detectSdkVersionEDT path guarded withrunWithModalProgressBlockingto prevent unguarded EDT blocking. - Debugger deprecation warning and unused variable warning resolved.
- Debugger node network calls dispatched off EDT via serial
23.0.7 - 2026-05-15
- #3815 - @sh41
- Unicode identifier and atom support in the JFlex lexer -- Elixir supports Unicode identifiers (e.g.
def ΦΤ§ do) and bare Unicode atoms (e.g.:ΦΤ§), but the lexer only recognised ASCII characters. Added Unicode letter/digit support using JFlex POSIX character classes. - Erlang private functions now decompiled as
defpinstead ofdef, using the BEAM export table to classify functions. - Strip
-type/-opaqueprefix from Erlang type signatures, mapping to@typep/@opaque. Elixir.prefix stripping for uniform module name resolution --Enum.map()andElixir.Enum.map()now resolve consistently.- Type rendering: replaced a
TODO()crash inappendTypes()with actual BEAM doc-chunk signature rendering. - Non-blocking bulk decompilation: moved bulk decompile scan off the EDT with run-scoped log deduplication to prevent log floods during library indexing.
- Improved parse error diagnostics: error reports now include the parser error description and failing source line, with top-10 unique error pattern summary in the bulk-decompile run log.
- Unicode identifier and atom support in the JFlex lexer -- Elixir supports Unicode identifiers (e.g.
- #3815 - @sh41
- FD leak fix (WSL/IJent):
InputStreamopened per BEAM file was never closed; after ~20K sequential reads during bulk decompilation, IJent exhausted its vsock FD limit, crashed the gRPC connection, and froze the IDE. Wrapped inuse {}to release FDs immediately. Fixes #3613. - Parenthesise block expressions (
case/if/try) and nested binary literals inside binary element type specs -- previously emitted unparseable Elixir. Fixes #3554, #3555. - Space after word-based unary operators (
notetc.) to prevent keyword-argument misparse. Fixes #3556. - Sanitise Erlang compiler-generated variable names (e.g.
f@_1) containing@-- invalid in Elixir identifiers -- replacing with_. Fixes #3557. - Escape interpolation markers (
#{) in decompiled atom and string values. Fixes #3519. - Strip
\rfrom BEAM documentation chunk strings before inserting into IntelliJDocument. Fixes #3433. - Handle
:elixir_erlDbgi metadata value:none(modules compiled withdebug_info: false) gracefully instead of logging SEVERE errors. Fixes #3454. - Escape
\u{...}sequences in decompiled doc strings to prevent Elixir misinterpreting them as Unicode code-point escapes -- was silently breaking the entireStringmodule mirror mapping. Fixes #3412. - Emit
defprefix on overridden__struct__/1signatures so PSI mirror mapping resolves arity-1 struct functions. Fixes #3596. - Handle Erlang wildcard variable
_inrecord_field-- eliminates ~100 SEVERE log entries per bulk decompile run. Fixes #3403. - Fix backslash escaping order: escape
\before'in Erlang charlist rendering. Fixes #3234. - Add
notto reserved variable keywords. Fixes #2825. - Emit
()for zero-argument anonymous function clauses. Fixes #2916. - Emit source expression for empty map update associations. Fixes #2745.
- Propagate
doBlockflag through match expressions in comprehension generators. Fixes #2907, #2908. - Parenthesise nested bitstring generator expressions and wrap non-literal binary element sizes in
size(). Fixes #3469, #3423. - Render non-integer binary element type specifier values. Fixes #3240.
- Validate alias shape for
Elixir.-prefixed atoms, falling back to quoted atom form for invalid names. Fixes #3197, #3206. - Indexing deadlock prevention:
Cache.from(FileContent)now usesBeam.from(fileContent)instead of reopeningVirtualFile.inputStreamon cache misses. Fixes #2544, #2333. - Escape backslashes in quoted strings. Fixes #2728, #2635.
- Fix decompilation of modules with
Elixir.-prefixed atoms that containdo-endoperands causing parse cascades. Fixes #778, #2769, #3420.
- FD leak fix (WSL/IJent):
23.0.6 - 2026-05-14
- #3814 - @sh41
- 2026.2 compatibility: use deprecated
selectSdkHome2-arg form in SDK setup to compile against both 261 and 262 API. Note: On WSL, the SDK file chooser now opens at the user home directory instead of the WSL distribution root. This will be restored when 261 support is dropped.
- 2026.2 compatibility: use deprecated
- #3814 - @sh41
- Fix unreachable
?: "IU"fallback forplatformTypeGradle property --get()throws on missing properties, so the elvis operator was dead code. Now usesgetOrElse("IU").
- Fix unreachable
- #3814 - @sh41
- CI matrix updated to
windows-2025runners. IDEA EAP version pinned to261.24374.34(Java 25 incompatibility withLATEST-EAP-SNAPSHOT). - Plugin verifier pinned to 1.384 with markdown/HTML/plain report formats and clickable report output.
- CI matrix updated to
23.0.5 - 2026-04-28
- Fixed Umbrella project import crash when root folder and child app share the same name (e.g.,
emqx/root withapps/emqx/child). The quick import path (File->Openonmix.exs) bypassed the wizard's duplicate detection, causingModuleWithNameAlreadyExists. Module names are now disambiguated using the relative path (e.g.,emqxfor the root,emqx-apps-emqxfor the child). - @joshuataylor, (Thanks to @JiaRG for the thorough and reproducible bug report and excellent example umbrella project!)
23.0.4 - 2026-04-26
- 2026.1 compatibility: remove deprecated
OpenProjectTask.copy()call inDirectoryConfigurator. - @joshuataylor - 2026.1 compatibility: use
ActionUtil.performAction()instead of directly invoking@OverrideOnlymethod inInstallMixDependenciesAction. - @joshuataylor - Remove internal API usage: replace
DiagnosticBundle,AbstractMessage, andPlatformUtilswith public alternatives for plugin verification compatibility. - @joshuataylor
- Bump Gradle from 9.3.1 to 9.4.1. - @joshuataylor
- Bump kotlinx-coroutines from 1.9.0 to 1.10.2. - @joshuataylor
- Bump gradle-download from 5.6.0 to 5.7.0. - @joshuataylor
- Bump intellij-platform from 2.12.0 to 2.15.0. - @joshuataylor
- Bump Kotlin to 2.3.21. - @joshuataylor
23.0.3 - 2026-04-26
- #3806 - @sh41
- Fix indexing deadlock by reading BEAM files from
FileContentinstead of reopeningVirtualFileinput stream. Avoids deadlock on WSL/IJ environments during annotation and highlighting passes.
- Fix indexing deadlock by reading BEAM files from
- #3807 - @sh41
- Remove stale library roots on dependency re-sync, fixing libraries XML files getting updated with redundant data endlessly. Fixes #3804.
23.0.2 - 2026-04-17
- #3763 - @sh41
- JPS plugin refactor: fix JPS builder classpath (broken since September 2024), move
HomePathinto IDE module, persist Elixir SDK derived data (mix-home,wsl-unc-path) with adata-versionmarker and migration, and separate JPS builder from shared modules.
- JPS plugin refactor: fix JPS builder classpath (broken since September 2024), move
- #3792 - @sh41
- Mix deps tooling: dedicated
mix depsstatus parsing, deps checker service with debounced notifications and install action, SDK creation/registration flow with explicit Erlang dependency wiring, and Mise plugin integration for auto-install/configure of Elixir/Erlang SDKs. - Non-blocking write actions in UI and formatter to avoid background/EDT exceptions.
- Hardened CLI argument construction across Mix/IEx/ESpec/ExUnit configurations.
- WSL path canonicalization and env var handling improvements.
- 2026.1 compatibility: ignore
ExperimentalPsiDummyBlocktype inElixirDocumentationProvider.
- Mix deps tooling: dedicated
- #3791 - @soomtong
- Update New Project Wizard to use the new
LanguageGeneratorNewProjectWizardAPI with Elixir icon display.
- Update New Project Wizard to use the new
- OTP 28 support: handle
AtU8atom tables and stubless BEAM fallback. - @sh41 - Handle
@moduledoc-styleElixirMatchedAtOperationin markdown injection. - @sh41 - Handle uncompressed literals and improve error reporting in BEAM
Literalsparsing. - @sh41
- Fix broken IDE navigation by specifying base platform rather than module. - @sh41
- Fix process kill on Windows. - @sh41
- Fix compile server classpath for JPS module jars. - @sh41
- Fix WSL path handling: canonicalize WSL paths, convert only known env vars to WSL paths. - @sh41
- Ensure debugger path is added to code paths if unavailable. - @sh41
- Assert read access in all resolvers to avert deadlocks. - @sh41
- Less aggressive SDK version differentiation in
SdkHomeScan. - @sh41
- Bump intellij-platform from 2.11.0 to 2.12.0. - @joshuataylor
23.0.1 - 2026-03-06
- #3787 - @joshuataylor
- Remove internal API usage to improve plugin verification compatibility.
- #3788 - @joshuataylor
- Add separate GitHub Actions job for tagging/releasing, fixing release being overridden on every merge to main.
23.0.0 - 2026-02-09
- WSL Support - @sh41 - see release announcement for full details!
- Windows Subsystem for Linux support enabling Elixir development in WSL from Windows JetBrains IDEs. Originally submitted as #3749, broken into 7 stacked PRs for review. Resolves #1384, #1911, #2499, #3470, #3674, #3746. May also help with #3659, #3716, #3715. See the README WSL section for setup instructions.
- SDK discovery for Elixir and Erlang inside WSL distributions (asdf, mise, kerl, kiex, Homebrew, Nix).
- WSL path conversion supporting both
\\wsl$\and\\wsl.localhost\UNC path formats. - All run configurations work with WSL: Mix tasks, IEx, ExUnit, ESpec, Distillery, external tools.
- WSL-safe process handling with graceful BEAM termination across the WSL boundary.
- WSL-aware SDK naming in UI and status bar widget.
- SDK settings improvements for small-IDE compatibility (RubyMine, WebStorm, etc.).
- #3753 - @sh41
- Windows development infrastructure: platform-aware build services, Gradle tasks, CI caching (~4.5 GB fork-safe cache), and updated CONTRIBUTING.md.
- #3757 - @sh41
- Mix dependency health checks on project open with install action and notifications via shared MixTaskRunner.
- Faster and more robust Mix project import with pre-scan of OTP apps off EDT.
- #3759 - @sh41
- IntelliJ IDE Starter-based UI test infrastructure for automated IDE testing.
- #3761 - @sh41
- Fix deadlock when
/depsdirectory is deleted while the deps watcher is active. - Fix JPS Builder module name handling (broken since September 2024).
- Fix deadlock when
- #3765 - @sh41
- Fix false inspection warnings in
~rregex sigils containing interpolated variables.
- Fix false inspection warnings in
22.0.0 - 2025-12-16
- This release supports 2025.3+ IDEs only (253.xxx).
- Language Injection for literal sigils (
~H,~r, etc.) - @polymorfiq - #3711 - @joshuataylor
- Status Bar Widget showing the current project's Elixir SDK version.
- "Refresh Elixir SDK Classpaths" action to fix SDK classpath issues.
- Compatibility fixes for 2025.1+ (understatement of changes, countless amount of changes to help improve things, thank you so micj) - @sh41
- Extensive EDT threading fixes for 2025.2+ compatibility.
21.0.0 - 2025-05-17
- #3651 - @joshuataylor
- Support Jetbrains 2025.1 and relax until-build.
20.0.1 - 2024-11-29
- #3651 - @joshuataylor
- Support Webstorm 2024.3 (243.21565.180). Thanks to @Kae-Tempest for the report!
20.0.0 - 2024-11-14
- #3651 - @joshuataylor
- Support Jetbrains 2024.3 (243.21565.193)
19.0.1 - 2024-08-20
- #3639 - @joshuataylor
- Support JetBrains Exception Analyzer
- #3640 - @joshuataylor
- Bump intellij platform gradle to 2.0.1 and IdeaVIM to 2.16.0
- #3643 - @joshuataylor
- Fix RubyMine freezing for umbrella projects but showing the new project wizard as a temporary workaround.
19.0.0 - 2024-08-14
- #3619 - @joshuataylor
- Drop support for < 2024.2 IDEs
- #3619 - @joshuataylor
- Support IntelliJ Gradle Plugin 2.0
18.0.1 - 2024-08-05
18.0.0 - 2024-08-03
- #3569 - @ashleysommer, @joshuataylor
- Drop support for < 2024.1 IDEs.
17.0.1 - 2023-12-24
- #3491 - @neominik
- Render code snippets that are not links.
- #3562 - @ashleysommer
- Add the correct ERL and elixir arguments for starting IEx depending on the version of Elixir SDK.
- #3563 - @ashleysommer
- Bundle latest OtpErlang.jar from JInterface v1.14 for OTP v26.
17.0.0 - 2024-01-11
- #3500 - @KronicDeth
- Drop support for <= 2023.2 IDEs.
- #2402 - @joshuataylor
- Support 2023.3 IDEs.
- #3431 - @KronicDeth
- Add
displayNameinplugin.xmlfor configurables for faster menu loading.org.elixir_lang.facet.configurable.Project- "Elixir"org.elixir_lang.facets.sdks.erlang.Configurable- "Internal Erlang SDKs"org.elixir_lang.facets.sdks.elixir.Configurable- "SDKs"
- Add
16.0.0 - 2023-09-12
- The CHANGELOG for v15 can be found in the v16.0.0 tag.