KronicDeth
IntelliJ Elixir - Elixir plugin for JetBrain's IntelliJ Platform
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine)
This is a plugin that adds support for Elixir to JetBrains IntelliJ IDEA platform IDEs (DataGrip, AppCode, IntelliJ IDEA, PHPStorm, PyCharm, Rubymine, WebStorm).
It works with the free, open source Community edition of IntelliJ IDEA in addition to the paid JetBrains IDEs like Ultimate edition of IntelliJ. No feature is locked to a the paid version of the IDEs, but the plugin works best in IntelliJ because only IntelliJ supports projects with different languages than the default (Java for IntelliJ, Ruby for Rubymine, etc).
The plugin itself is free. Once you have your IDE of choice installed, you can install this plugin.
Most Liked
KronicDeth
v4.5.0 was a canary release, so it’s release notes are included in this general release in case you didn’t run the canary
Thanks
- Thanks to Matt Briggs (@mbriggs) and Andrei Dziahel (@develop7) for reporting the
NullPointerExceptionfromorg.elixir_lang.psi.scope.call_definition_clause.Variants.getLookupElementCollection. - Thanks to Sergey Zubtsovskiy (@szubtsovskiy) for reporting
StackOverflowErrors from certain forms ofwithand helping me narrow down specific minimal reproduction cases. - Thanks to @mdhirsch for finding a parser bug (!) after all this time: newlines weren’t allowed before
dofordoblocks. - Thanks to Scott Bennett (sbennett33) for finding a case that triggered the
ElixirNoParenthesesStricterror handling, which revealed thatisVariabledidn’t handle it correctly. - Thanks to Andrei Dziahel (@develop7) for reporting an error that while I couldn’t reproduce did lead to an improved error reporter, so I can figure it out if it happens again.
- Thanks to Danni Friedland (BlueHotDog) for reporting a bug that led me to find that
Prefix.primaryArgumentswasn’t usingNormalizedand still was using basicasserts. - Thanks to Aaron Foster (AnimalRepellentGranules) for reporting that the
doendmatching was too aggressive and incorrectly matched on one liners starting at thedo:.
Changelog
v4.6.0
Enhancements
- #454 - Regression test for #453 - @KronicDeth
- #455 - Regression test for #446 - @KronicDeth
- #464 - Show element class in all error reports - @KronicDeth
- #465 - Regression test for #456 - @KronicDeth
- #466 - Regression test for #458 - @KronicDeth
- #467 - Regression tests for #443 - @KronicDeth
- #474 - Regression test for #462 - @KronicDeth
- #476 - Regression test for #468 - @KronicDeth
- #477 - @KronicDeth
- Regression test for #469
- Highlight Strings and String Heredocs as errors in types.
- #479 - Regression tests for #470 - @KronicDeth
- #483 - @KronicDeth
- Add
Qualified#qualifierby extracting it fromCallDefinitionClauseCompletionProvider. - Add
Modularclass with#forEachCallDefinitionClauseNameIdentifierto enumerate all the identifiers that could be linked to in a modular. - Add
ElixirPsiImplUtil#maybeQualifiedCallToModularby extractingresolveFullyfromCallDefinitionClauseCompletionProvider - Add regression tests for #463.
- Add
- #484 - Regression test for #471 - @KronicDeth
- #485 - Regression test for #472 - @KronicDeth
- #486 - @KronicDeth
- Extract
Arrowinterface for*ArrowOperations. - Regression tests for #480
- Extract
Bug Fixes
- #454 - Return
emptySetwhenlookupElementByPsiElementisnull. - @KronicDeth - #455 - @KronicDeth
-
Don’t do a naked
assertthat there are 2 children because this can fail during error recovery on the operand, instead use theprefix.Normalized.operand()throughprefix.operand().WARNING: This changes the
@NotNullarray so that its sole element changes from@NotNullto@Nullable. It may trigger new bugs.
-
- #461 - Use shipped
GeneratedParserUtilBase.DUMMY_BLOCKbecause theDUMMY_BLOCKMUST match theGeneratedParserUtilBaseto detect dummy blocks inserted for error handling. - @KronicDeth - #465 - Skip over
ElixirNoParenthesesStrictforisVariable- @KronicDeth - #466 - Allow newlines before
doindoBlock- @KronicDeth - #467 - Don’t match
doorfntoendwhen used as a keyword key. - @KronicDeth - #474 - @KronicDeth
- Check if
iterator.atEnd()before callingiterator.getTokenType()to avoidIndexOutOfBoundsexception. - Don’t add current call definition clause being written to completion
- Check if
- #476 -
When#leftOperandwill returnnull(because it’s normalized) if there are left-hand error elements, but when stripping guards we want best-effort to match human expectations, so don’t use normalizednull, but use left, non-error element if it is unique. - @KronicDeth - #477 - Highlight types in
QualifiedNoParenthesesCall- @KronicDeth - #478 - Still not obvious why
namefor aCallDefinitionClauselookup renderer can be longer thanpresentableText, so still log an error, but withLogger.error, so we getname,presentableText, and the originalelement. - @KronicDeth - #479 - @KronicDeth
- Skip
Argumentselements inpreviousParentExpresionto eliminate an unnecessary level of processing declarations since calls will enter their arguments. - Only put new
ENTRANCEinResolveStateinvariable.MultiResolve.resolveResultList, so that caller can override the default value. - Set
ENTRANCEtomatchAncestorinstead of previous expression to eliminate the looping that occurred when a variable was unbound (or a function) because the check forwith(andfor) was expecting theENTRANCEto be the previous child expression instead of thewithclause as a whole (or theArgumentselement as had been the case before 6fcc19b).
- Skip
- #483 - @KronicDeth
- Resolves functions qualified by Aliases that are either direct Module references or one-step aliases.
- Remove
Call#resolvedFunctionNamebecauseimportcan’t rename functions.
- #484 - Don’t type-highlight
BracketOperations as they occur when putting maps or structs in front of lists. - @KronicDeth - #485 - Treat
Enum.eachthe same asEnum.maparounddef- @KronicDeth - #486 - Increase
resolvedFinalArityby1for piping. - @KronicDeth - #498 - @KronicDeth
- Go To Declaration resolves through
import- for
import MyModule- the
importstatement - the call definition clause in the imported Module.
- the
- for
import MyModule, only: [name: arity]- the
importstatement - the call definition clause in the imported Module.
- the
- for
import MyModule, except: [name: arity]if reference is notname/arity.- the
importstatement - the call definition clause in the imported Module.
- the
- for
- Go To Declaration resolves through
v4.5.0
Enhancements
- #452 - @KronicDeth
- Go To Declaration for functions and macros (only those defined in parseable-Elixir source. References to Erlang functions or only those available in
.beamfile, such as the standard library will not resolve.) - Completion for functions and macros (only those defined in parseable-Elixir source. Erlang functions and Elixir function only in compiled
.beamfile, such as the standard library will not complete.)- Completion uses the same presentation as Structure View, so the you can tell whether the name is a function/macro, whether it is public/private, and the Module where it is defined.
- Completed functions/macro insert
()after the name in preparation for Elixir 1.4 where it is an error to have bare function calls. It also makes it more obvious that you inserted a function and not a variable. - Completion works for all functions when a bare identifier is used. For a qualified identifier, only functions/macros under than Module are shown.
- Go To Declaration for functions and macros (only those defined in parseable-Elixir source. References to Erlang functions or only those available in
README Changes
Completion
Function and Macro Calls
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
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.
Unqualified
Function and macro calls that are unqualified are completed from the index of all function and macro definitions, both public and private. (The index contains only those Elixir functions and macro defined in parsable source, such as those in the project or its dependencies. Erlang functions and Elixir functions only in compiled .beam files, such as the standard library will not complete.) Private function and macros are shown, so you can choose them and then make the chosen function or macro public if it is a remote call.
Go To Declaration
Function or Macro
You’ll know if function or macro usage is resolved and Go To Declaration will work if the call is annotated, which in the default themes will show up as italics.
Imported Functions or Macros
- Place the cursor over name of the function or macro call.
- Activate the Go to Declaration action with one of the following:
Cmd+B- Select Navigate > Declaration from the menu.
Cmd+Click
- A Go To Declaration lookup menu will appear, allowing you to jump to either the
importthat imported the function or macro or jumping directly to the function or macro definition clause. Select which declaration you want.- Use arrow keys to select and hit
Enter Click
- Use arrow keys to select and hit
Local Functions or Macros
- Place the cursor over name of the function or macro call.
- Activate the Go to Declaration action with one of the following:
Cmd+B- Select Navigate > Declaration from the menu.
Cmd+Click
- Whether a lookup a Go To Declaration lookup menu appears depends on the number of clauses in the function or macro definition:
- If there is only one clause in the function or macro definition, you’ll jump immediately to that clause
- If there is more than one clause in the function or macro definition, a Go To Declaration lookup menu will appear, allowing you to jump to either the
importthat imported the function or macro or jumping directly to the function or macro definition clause. Select which declaration you want.- Use arrow keys to select and hit
Enter Click
- Use arrow keys to select and hit
Remote Functions or Macros
- Place the cursor over name of the function or macro call that is qualified by an Alias.
- Activate the Go to Declaration action with one of the following:
Cmd+B- Select Navigate > Declaration from the menu.
Cmd+Click
-
- If there is only one clause in the function or macro definition, you’ll jump immediately to that clause
- If there is more than one clause in the function or macro definition, a Go To Declaration lookup menu will appear, allowing you to jump to either the
importthat imported the function or macro or jumping directly to the function or macro definition clause. Select which declaration you want.- Use arrow keys to select and hit
Enter Click
- Use arrow keys to select and hit
Installation
Inside IDE using JetBrains repository
- Preferences
- Plugins
- Browse Repositories
- Select Elixir
- Install plugin
- Apply
- Restart the IDE
Inside IDE using Github releases
In browser
- Go to releases.
- Download the lastest zip.
In IDE
- Preferences
- Plugins
- Install plugin from disk…
- Select the downloaded zip.
- Apply
- Restart the IDE.
KronicDeth
Thanks
- Thanks to Jake Becker (@JakeBecker) for adding Mix ExUnit Run Configurations and create from context.
- Thanks to @andyl for requesting Navigate > Test and Navigate > Test Subject as it was a good complement to Jake Becker’s new Mix ExUnit Run Configurations
- Thanks to Alexander Merkulov (@merqlove) for outline the testing workflow that Jack Becker ended up implementing
- Thanks to @davit55 and Matt Briggs (@mbriggs) for confirming that lookup name could exceed the element text and it wasn’t just an artifact of my development environment
- Thanks to @taorg for a full test case to show how MultipleAliases would need to be supported in
isVariable - Thanks to Josh Taylor (@joshuataylor for posting a screenshot of the bad lookup formatting for variables.
- Thanks to Matt Briggs (@mbriggs) for posting an error about the error reporter
- Thanks to Josh Taylor (@joshuataylor, Matt Briggs (@mbriggs), Cris (@crisonyx), @sngyai, @lucdang, Cris (@crisonyx), Sven Marquardt (@Eiamnacken), and Aaron Eikenberry (@aeikenberry) for reporting that
CallDefinitionClause(Call)needed between error handling to avoid aNullPointerException - Thanks to Matt Briggs (@mbriggs) for posting the source for a reproduction-case for the StackOverflow that was plaguing @andshape, Robert Hencke (@rhencke), Alexander Merkulov (@merqlove), @fieldinrain, Roman (@roman462), Andrei Dziahel (@develop7), and Josué Henrique Ferreira da Silva (@josuehenrique)
- Thanks to Tiziano Puppi (@tizpuppi) for demonstrating how a typing a typo could cause an error in variable use scope.
- Thanks to nsm (@nsmuffin) for posting that working directory being null wasn’t treated the same as not being a directory.
- Thanks to Ryan Scheel (@Havvy for showing how missing a
:in type specs could break the highlighting, which led to the inspections and quick fixes in this release.
Changelog
v4.7.0
Enhancements
-
#523 - Use the
CommonProgramParametersPanelto get the working directory and environment variables the same way the JUnit form does. Replace the custom “Command” input with the “Program arguments” input built into theCommonProgramParametersPanel.CommonProgramParametersPanelexpects to store the “Program Arguments” in a “ProgramParameters” field, so old run configurations will lose their “Command” option value and it will be migrated to the new “ProgramParameters”. - @KronicDeth -
#482 - @JakeBecker, @KronicDeth
- Create / Run Mix ExUnit Run Configurations
- Run Configuration from Directory
- Run Configuration from File
- Run Configuration from LIne
- Run Configurations support Common Program Parameters
- Program Arguments
- Working directory
- Environment variables
- Create / Run Mix ExUnit Run Configurations
-
#531 - @KronicDeth
-
enclosingMacroCallreturns enclosing macro call when parent isElixirDoBlock, so thatendelement goes to the macro call. -
Navigate > Testwill go to the Module that has the same canonical name as the currentdefimpl,defmodule,defprotocol, orquotewith aTestsuffix added -
Navigate > Test Subjectwill go to thedefimpl,defmodule,defprotocol, orquotethat has the same canonical name as the current Module with theTestsuffix removed.
-
- #533 - Regression test for #500 - @KronicDeth
- #545 - Regression test for #517 - @KronicDeth
- #548 - Regression test for #521 - @KronicDeth
-
#549 - @KronicDeth
- Regression test for #525
- If
:is used instead of::for a type specification, mark it as an error with a Quick Fix to convert:to::. - Highlight
=operands the same as::operands in type specifications. - If
=is used instead of::in a type specification, mark it as an error with a Quick Fix to convert=to::.
Bug Fixes
-
#523 - Fix typo:
myRunInModuleChekcBox=>myRunInModuleCheckBox- @KronicDeth - #532 - Don’t log error when name length exceeds presentable text length because it appears to be common for normal users and not a development environment artifact. - @KronicDeth
-
#533 - Check parent of
ElixirMultipleAliasesforisVariablebecauseElixirMultipleAliasescan be hit inisVariablewhenMyAlias.is added on a line above a pre-existing tuple, such as when typing a new qualified call. - @KronicDeth - #534 - Add space between variable and match in lookup element presentation - @KronicDeth
-
#535 - Check
VirtualFileis not null before creating attachment becausePsiFilecan lack aVirtualFileif thePsiFileonly exists in memory. - @KronicDeth -
#537 - Convert
CallDefinitionClause(Call)toCallDefinitionClause.fromCall(Call), so thatnullcan be returned whenCallDefinitionClause.enclosingModular(Call)returnsnull. - @KronicDeth -
#539 - @KronicDeth
- Use
functionNameinstead ofgetNamewhen multiresolving unqualified functions becausegetNamewill return the Alias when called ondefmodule. -
maybeQualifiedCallToModularreturnednullBOTH (1) if the call was unqualified OR (2) if the call was qualified, but its modular could not be resolved, so qualified calls to.beam-only modules, likeFile.read!returnednullbecauseFilecould not be resolved to a modular. RemovemaybeqQualifiedToModularand callqualifiedToModularwhenmyElementis qualified. If the modular isnull, then return an emptyResolveResult[]instead of looking for unqualified matches. - Pass
maxScopetoModulereference.maxScopeis generally the containing file for the element, but when usingModuleto resolveimports, it is theimportcall’s parent element, so that the resolve doesn’t ricochet between thedefmoduleand its child, theimportcall untilStackOverflowError.
- Use
-
#545 - A variable cannot be declared in update arguments, so return
LocalSearchScope.EMPTY, the same as interpolation. - @KronicDeth -
#548 -
ElixirSystemUtil.getProcessOutputalready allowed for an empty, invalidProcessOutputwhen theworkDirwasn’t a directory, so allow it to also benulland return the emptyProcessOutput. - @KronicDeth -
#549 - @KronicDeth
- If a single keyword pair is used for a type spec, treat
:as a type for:: - Limit variable use scope for variables “declared” in module attributes to the module attribute because the variable can’t be declared there and it is really a variable usage without declaration.
- If a single keyword pair is used for a type spec, treat
README Changes
Inspections
Keyword pair colon (:) 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
Match operator (=) 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
Convert : to :: in type specs
If a type specification uses a single : instead of ::, then hit Alt+Enter on the : to change it to :: and fix the type spec.
Convert = to :: in type specs
If a type specification uses = instead of ::, then hit Alt+Enter on the = to change it to :: and fix the type spec.
Run Configurations
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
Creating mix test Run Configurations Manually
- Run > Edit Configurations…
- Click +
- Select “Elixir Mix ExUnit”
- Fill in the “Program arguments” with the argument(s) to pass to
mix test. Normally, this will be a directory liketest, relative to the “Working directory” - Fill in the “Working directory”
- Type the absolute path to the directory.
- Select the path using directory picker by clicking the
...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
- Click the RUn arrow in the Toolbar to run the
mix testtask - The Run pane will open showing the Test Results
While 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.
Creating mix test Run Configurations from context
The 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.
- In the Project pane, ensure your OTP application’s
testdirectory is marked as a Test Sources Root - Check if the
testdirectory 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 menu - Right-click the
testdirectory. - Hover over “Mark Directory As >”
* 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
Creating/Running mix test Run Configurations from directory
- Right-click the directory in the Project pane
- Click “Run Mix ExUnit”, which will both create the Run Configuration and Run it.
- If you want to only create the Run Configuration, select “Create Mix ExUnit” instead
Alternatively, you can use keyboard shortcuts
- Select the directory in the Project pane.
-
Ctrl+Shift+Rwill create the Run Configuration and Run it.
Creating/Running mix test Run Configurations from file
- Right-click the file in the Project pane
- Click “Run Mix ExUnit”, which will both create the Run Configuration and Run it.
- If you want to only create the Run Configuration, select “Create Mix ExUnit” instead
Alternatively, you can use keyboard shortcuts
- Select the directory in the Project pane.
-
Ctrl+Shift+Rwill 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.
- If you want to only create the Run Configuration, select “Create Mix ExUnit” instead
Creating/Running mix test Run Configurations from line
If 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.
- If you want to only create the Run Configuration, select “Create Mix ExUnit” instead
Alternatively, you can use keyboard shortcuts
- Place the cursor on the line you want to test
-
Ctrl+Shift+Rwill create the Run Configuration and Run it.
Installation
Inside IDE using JetBrains repository
- Preferences
- Plugins
- Browse Repositories
- Select Elixir
- Install plugin
- Apply
- Restart the IDE
Inside IDE using Github releases
In browser
- Go to releases.
- Download the lastest zip.
In IDE
- Preferences
- Plugins
- Install plugin from disk…
- Select the downloaded zip.
- Apply
- Restart the IDE.
KronicDeth
With both the contribution of ExUnit Mix Tasks in 4.7.0 and the conversion of the build to Gradle for 5.0.0, I’ve added Jake Becker (@JakeBecker) as a Collaborator to the repo.
Thanks
- Reporting the
AssertionErrorinorg.elixir_lang.code_insight.lookup.element_renderer.CallDefinitionClause.renderElement- @bdarla
- Josué Henrique Ferreira da Silva (@josuehenrique)
- Arjan Molenaar (@amolenaar)
- Chris Desch (@ cdesch)
- Steve (@stevepm)
- Laura Stone (@tyrostone)
- @wee911
- Oleg Samorai (@Samorai)
- Jadenn (@dabaer)
- Jan Stevens (@JanStevens)
- Szymon Jeż (@szymon-jez)
- Reporting examples where
ElixirVariablecould not be determined if it was a variable.- Steve Wagner (@ciroque)
- Marcin Jakubowski (@mjakubowski84)
- Ryan Cammer (@ryancammer)
- Josué Henrique Ferreira da Silva (@josuehenrique) (multiple examples!)
- Matt Briggs (@mbriggs)
- Reporting examples where
ElixirMultipleAliasescould not resolve variable in match.- Дмитрий Дубина (@dmitrydprog
- Nitin Gupta (@nitingupta910) for
- Reporting examples of the annotator highlighting in the wrong file due to the call definition offset always being assumed to be in the same file.
- Wojciech Bednarski (@wbednarski)
- Artem Gordinsky (@ArtemGordinsky)
- Sam Brotherton (@sbrother)
- Alexander Merkulov (@merqlove)
- Reporting Parameter variable style being incorrectly applied
- Matt Briggs (@mbriggs)
- Working with me to figure out that the template tags in the color preview were messed up
- Dmitry Maltsev (@judgedim)
- Reporting mismatch between inspection resource and class due to a copy-and-paste error
- Markus Fischer (@mfn)
- Reporting incorrect highlighting of functions with guards
- Dmitry Maltsev (@judgedim)
- Requesting configurable code-folding for module attributes
- Artem Rizhov (@artemrizhov)
- Pointing out it makes no sense for context menus for ExUnit to appear in non-Elixir projects
- Alexander Merkulov (@merqlove)
- Reporting an example where
MatchedUnqualifiedParenthesesCallcould not be type highlighted- Ryan Scheel (@Havvy)
- Reporting that function complete didn’t take into account the already typed prefix
- Saeed Zarinfam (@zarinfam)
- Requesting completion of Aliases to
.beam-only Module, which is what inspired me to attempt decompilation- Matt Briggs (@mbriggs)
Changelog
v5.0.0
Enhancements
- #574 - @KronicDeth
- Decompile
.beamfiles- Structure view for decompiled
.beamfiles - Index modules, functions, and macros exported by
.beamfiles - Go To Symbol for Modules defined in
.beamfiles (both SDK and deps)- Erlang using atoms (like
:idna) - Elixir using Alias (like
Enum)
- Erlang using atoms (like
- Completion for Modules defined in
.beamfiles (both SDK and deps)- Elixir using Alias (like
Enum)
- Elixir using Alias (like
- Completion for functions and macros exported by
.beamfiles - Syntax highlighting
- Structure view for decompiled
- Decompile
- #579 - Regression test for #575 - @KronicDeth
- #583 - @KronicDeth
- Macros appear before functions in decompiled
.beamfiles- Header for macro and function sections
- Macros appear before functions in decompiled
- #585 - @KronicDeth
- Update
ELIXIR_VERSIONfor1.2.*from1.2.3to1.2.6 - Add
ELIXIR_VERSION1.3.4 - Add
ELIXIR_VERSION1.4.0 - Update
IDEAfor2016.*to2016.3.1 - Show
OtpErlangBitStr(and thereforeOtpErlangBinarycontents when tests fail - Quote binaries as
to_charlistinstead ofto_char_listfor Elixir>= 1.3. Depends on Elixir version of project SDK. - Use
elixirinstead ofjavaVM, so now Erlang and Elixir don’t need to be built on travis-ci, butantand thejdkneed to be installed, but unlike Erlang and Elixir, there are tarballs for that, so this way is faster than the old method without depending on travis-ci cache.
- Update
- #609 - @KronicDeth
- If
multiResolvecauses aStackOverflowfororg.elixir_lang.annotator.Callable.visitCall, thencatchit and useerrorreportlogger to log the element. - Include file path in
errorreportexcerpt - Log element for
StackOverflowrelated toimports - Regression test for #605.
- Log
LookupElement#getObjectwhenLookupElement#getPsiElementisnullto track down how it wasnullin #563.
- If
- #614 - Regression test for #559 - @KronicDeth
- #504 - @JakeBecker
- Switch to Gradle for builds.
./gradlew runIde(or therunIde (VERSION)Run Configurations) will run IDEA in a sandbox with the development version of the plugin../gradlew test(or thetest (VERSION)Run Configurations) will run the main plugin and jps-builder tests.- The plugin can now be published with
./gradlew publishPlugin, BUT you’ll need to fill inpublish*properties ingradle.properties. This will eventually allow for automated “nightlies” from successful Travis-CI builds onmaster.
- Switch to Gradle for builds.
- #638 - The
Callableannotator is meant for variables, parameters, and macro and function calls and declarations. TheModuleAttributeannotator handles module attribute declaration and usage, so we can save reference resolution time by skipping module attributes inCallable. - @KronicDeth - #640 - Allow module attribute folding to be configured. - @KronicDeth
- #662 - @KronicDeth
- Allow call definition heads to resolves to themselves for consistency with Aliases of
defmodule. - Generalize
Callable.callDefinitionClauseDefiner(Call): in addition to the currentCallDefinitionClause, make it work forImplementation,Module, andProtocol.
- Allow call definition heads to resolves to themselves for consistency with Aliases of
Bug Fixes
- #574 - Fix copy-paste errors in
MatchOperatorInsteadOfTypeOperator- @KronicDeth - #579 - @KronicDeth
- Subtract 1 from arity in
.beamfile when decompiling todefmacrocalls because the Erlang function for Elixir macros has one addition argument: the first argument is theCallerof the macro. - If the name of the decompiled macro/function is an infix operator, then decompile the head as a binary operation instead of a normal prefix name as infix operators aren’t valid prefix names and led to parsing errors, which was the root cause of #575.
- Fix IntelliJ warnings in
BeamFileImpl - Remove unused
VirtualFileargument toBeamFileImpl#buildFileStub.
- Subtract 1 from arity in
- #583 - @KronicDeth
- Add
++,=~, andintoINFIX_OPERATOR_SET. - Only render infix operators if arity is
2. - Prefix operator decompilation:
+and-are both binary and unary operators. When a unary operator they need to be wrapped in parentheses, so that the call definition clause is parsed correctly.
- Add
- #585 - @KronicDeth
- Ignore
JFLexjar - Don’t check for
elixir-lang/elixrfiles remove in1.3 - Allow
nilas a keyword key.nilwas being lexed as a potential keyword key, but NIL was missing from the token list in the keywordKey grammar rule.
- Ignore
- #599 - Some SpecialForms don’t work as literals as they would be interpreted as metaprogramming, so their name needs to be wrapped as an atom to
unquote. - @KronicDeth - #600 - @KronicDeth
- Check children of
MultipleAliasesfor variable declarations. - Treat any variable declared in a
MultipleAliasesas invalid.
- Check children of
- #609 - @KronicDeth
- Skip
import Kernelinkernel.exto prevent stack overflow due to recursiveimport - Strip all outer parentheses for left type operand, so that
(+value)can be see as+operator type spec. - Use advice from
IndexNotReadyExceptiondocumentation and checkDumbService.isDumb(Project)before callingStubIndex.getElementsinModuleandmodule.MultiResolve.indexNameElements. - Don’t
assertthatLookupElement#getPsiElementis notnullinCallDefinitionCluase.renderElement - Update to
ant1.10.1because1.10.0is no longer hosted.
- Skip
- #612 - Yeah, it sounds weird, but an
ElixirVariableisn’t necessarily a variable if it doesn’t occur in a declaration context. It could just be a no-parentheses function call in the wrong spot, so check the parentPsiElementto determine ifElixirVariableis a variable. - @KronicDeth - #614 - Highlight parameterized type head (
maybe(t)in@type maybe(t)) the same as a full type definition (maybe(t)in@type maybe(t) :: t | nil) - @KronicDeth - #616 - Only show Mix ExUnit Run in context when the module, or when the module is not a available, the project SDK is Elixir. If there is no SDK configured, show “Mix ExUnit Run” in the menu. - @KronicDeth
- #617 - Mark
do:as atom in demo text - @KronicDeth - #627 - Annotations can only be applied to the single, active file, which belongs to the
referrerCallable. Theresolvedmay be outside the file if it is a cross-file function or macro usage, in which case it’sTextRangeshould not be highlighted because it is referring to offsets in a different file. - @KronicDeth - #634 - @KronicDeth
Variablescope forQualifiedMultipleAliases, which occurs when qualified call occurs over a line with assignment to a tuple, such asQualifier.\n{:ok, value} = call()- Remove call definition clauses (function or macro) completion for bare words as it had a detrimental impact on typing feedback (the editor still took input, but it wasn’t rendered until the completion returned OR
ESCwas hit to cancel the completion, which became excessive once the index of call definition clauses was expanded by the decompilation of the Elixir standard library.beams, so disable it. If bare-words completion is restored. It will either (1) need to not use theReference#getVariants()API because it generates too many objects that need to be thrown away or (2) need to only complete call definition clauses that are provably in-scope from imports or other macros.
- #636 - @KronicDeth
- Both intellij-erlang and intellij-community are Apache 2.0 licensed and its the default license for Elixir projects, so seems like a good choice for
LICENSE.md - Add
CODE_OF_CONDUCT.md
- Both intellij-erlang and intellij-community are Apache 2.0 licensed and its the default license for Elixir projects, so seems like a good choice for
- #638 - @KronicDeth
- The run configurations I put together in #504 didn’t allow for the debugger to work properly: neither pause nor breakpoints had any effect, so regenerate them from the Gradle pane.
- Check parent of
whenoperation in case it’s a guarded function head inorg.elixir_lang.annonator.Parameter.putParameterized(Parameter, PsiElement) - Instead of highlighting call definition clauses when they are referred to, which only works if it is in the same file, highlight all function and macro declarations when the
def*call is encountered. - Only increment arity for right pipe operand instead of all operands, so that left operands resolve to correct arity or as variable/parameter.
- #662 - @KronicDeth
- Override
ModuleImpl#getProject()to preventStackOverflowError. Without overriding#getProject(),IdentifierHighlighterPassgets stuck in a loop betweengetManagerandgetProjecton the target (theModuleImpl) when clicking on the space betweendefs ordefmacros in the decompiled.beamfiles. - Fix source formatting
- Skip looking for variables unless 0-arity AND no arguments
- Highlight unresolved macros as macro calls. Anything with a do keyword or a do block will be treated like a macro call even if it can’t be resolved. No resolved is either no resolve results or an empty list
- Implicit imports at top of file in addition to top of Module.
- Override
- #663 - @KronicDeth
CallDefinitionClausecompletion provider is unexpectedly invoked both when.is typed, but continues to be invoked after a letter is typed after the.; however, once the letter is typed, the letter becomes the default prefix instead, so the prefix should only be reset to""when it ends in..- Disable
Callable#getVariantsunlessUnqualifiedto prevents local functions and macros being shown as completions for qualified names.
- #651 - @StabbyMcDuck
- Among many other tweaks, the String color is now green, so that Atom and String are no longer close to one another, which was the original issue in #569
Aliasnow hasunderscoredeffectBracketsare now greenish instead of brownishCallbacksare now a lighter blue and hasunderscoredeffectCharListis little lighterCharTokenis dark yellow now instead of dark purpleDotis now purple instead of dark redExpression Substitution Markis a little lighterInterpolationis now lime greenKernel Macrosare a burnt orangeMapis now a dark blue instead of a dark yellowOperation Signis a little lighterParametersare a little darkerParenthesesare redderPredefinedis orange instead of blueSpecificationis now red instead of purpleStructis now purple instead of yellowTypeis now green instead of dark purpleVariableis more tealish
- Among many other tweaks, the String color is now green, so that Atom and String are no longer close to one another, which was the original issue in #569
- #650 - @StabbyMcDuck
- Fix indentation to fix sub-lists in
CONTRIBUTING.md - Fix pluralization in
CONTRIBUTING.md
- Fix indentation to fix sub-lists in
- #664 - @KronicDeth
- Check if resolve results are
nullforFor.resolveResultList - Check if
Protocol.resolveResultListisnull
- Check if resolve results are
- #665 - Check
matchCallis anUnqualifiedNoArgumentCall, in addition to being 0 resolved final arity, before checking if the name matches. - @KronicDeth
Incompatible Changes
- #585 - Move
^^^to its own three-operator precedence level to match1.2. This does mean the parsing will be wrong for Elixir1.1, but is simpler than maintaining two grammars for those that are still using Elixir1.1- @KronicDeth - #504 - The
antbuild files have been removed. To build the build plugin (for Install From Disk), use the./gradlew buildPlugin. - @JakeBecker - #640 - Change to module attribute folding to off by default. - @KronicDeth
README Updates
Decompilation
.beam files, such as those in the Elixir 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.
Installation
Inside IDE using JetBrains repository
- Preferences
- Plugins
- Browse Repositories
- Select Elixir
- Install plugin
- Apply
- Restart the IDE
Inside IDE using Github releases
In browser
- Go to releases.
- Download the lastest zip.
In IDE
- Preferences
- Plugins
- Install plugin from disk…
- Select the downloaded zip.
- Apply
- Restart the IDE.
KronicDeth
Thanks
- For reporting when
scope.isEquivalentTo(lastParent.getParent())isfalseinprocessDeclarationsInPreviousSibling
Changelog
v5.1.0
Enhancements
- #643 - Graphical debugger with line breakpoints - @JakeBecker
- #673 - Add Debugger to README - @KronicDeth
Bug Fixes
- #669 - Replace
assert scope.isEquivalentTo(lastParent.getParent())with anifand log whatlastParentwas when condition isfalse, so root cause can be traced. - @KronicDeth
README Updates
Debugger
IntelliJ Elixir allows for graphical debugging of *.ex files using line breakpoints.
Line breakpoints can added by clicking in the left-hand gutter of an editor tab. A red dot will appear marking the breakpoint. When a Run Configuration is Run with the Debug (bug) instead of Run (arrow) button, execution will stop at the breakpoint and you can view the local variables (with Erlang names) and the stackframes.
Steps
- Define a run/debug configuration
- Create breakpoints in the
*.exfiles - Launch a debugging session
- During the debugger session, step through the breakpoints, examine suspended program, and explore frames.
Basics
After you have configured a run configuration for your project, you can launch it in debug mode by pressing Ctrl+D.
Keyboard Shortcuts
| 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 |
Breakpoints
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:
.
Accessing Breakpoint Properties
Viewing all breakpoints
To view the list of all breakpoints and their properties, do one of the following:
- Run > View Breakpoints
Shift+Cmd+F8- Click the

- Breakpoints are visible in the Favorites tool window.
Viewing a single breakpoint
To view properties of a single breakpoint
- Right-Click a breakpoint icon in the left gutter of the editor.
Configuring Breakpoints
To configure actions, suspend policy and dependencies of a breakpoint
- Open the Breakpoint Properties
- Right-click a breakpoint in the left gutter, then click the More link or press
Shift+Cmd+F8 - Open the Breakpoints dialog box and select the breakpoint from the list
- In the Favorites tool window, select the desired breakpoint, and click the pencil icon.
- Right-click a breakpoint in the left gutter, then click the More link or press
- Define the actions to be performed by IntelliJ IDEA on hitting breakpoint:
- To notify about the reaching of a breakpoint with a text message in the debugging console, check the “Log message to console” check box. A message of the format
*DBG* 'Elixir.IntellijElixir.DebugServer' got cast {breakpoint_reached, PID}will appear in the console. - To set a breakpoint the current one depends on, select it from the “Disabled until selected breakpoint hit” drop-down list. Once dependency has been set, the current breakpoint is disabled until selected one is hit.
- Choose the “Disable again” radio button to disable the current breakpoint after selected breakpoint was hit.
- Choose the “Leave enabled” radio button to keep the current breakpoint enabled after selected breakpoint was hit.
- Enable suspending an application upon reaching a breakpoint by checking the “Suspend” check box.
- To notify about the reaching of a breakpoint with a text message in the debugging console, check the “Log message to console” check box. A message of the format
Creating Line Breakpoints
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.
- Place the caret on the desired line of the source code.
- Do one of the following:
- Click the left gutter area at a line where you want to toggle a breakpoint
- Run > Toggle Line Breakpoint
Cmd+F8
Describing Line Breakpoints
- Open the Breakpoints dialog
- Right-click the breakpoint you want to describe
- Select “Edit description” from the context menu
- In the “Edit Description” dialog box, type the desired description.
Searching for Line Breakpoints
- Open the Breakpoints dialog
- Start typing the description of the desired breakpoint
Jump to Breakpoint Source
- To view the selected breakpoint without closing the dialog box, use the preview pane.
- To open the file with the selected breakpoint for editing, double-click the desired breakpoint.
- To close Breakpoints dialog, press
Cmd+Down. The caret will be placed at the line marked with the breakpoint in question.
Disabling Line Breakpoints
When you temporarily disable or enable a breakpoint, its icon changes from
to
and vice versa.
- Place the caret at the desired line with a breakpoint.
- Do one of the following:
- Run > Toggle Breakpoint Enable
- Right-click the desired breakpoint icon, select or deselect the enabled check box, and then click Done.
- Alt-click the breakpoint icon
Deleting Line Breakpoints
Do one of he following:
- In the Breakpoints dialog box, select the desired line breakpoint, and click the red minus sign.
- In the editor, locate the line with the line breakpoint to be deleted, and click its icon in the left gutter.
- Place caret on the desired line and press
Cmd+F8.
Starting the Debugger Session
- Select the run/debug configuration to execute
- Do one of the following
- Click
on the toolbar - Run > Debug
Ctrl+D
- Click
OR
Debug quick menu
Ctrl+Alt+D- Select the configuration from the pop-up menu
- Hit
Enter
Examining Suspended Program
Processes
The "Thread" drop-down lists the current processes in the local node. Only the current process is suspended. The rest of the processes are still running.
Frames
The Frames for the current process can be navigated up and down using the arrow keys or clicking on the frame.
- Press
UporDownto change frames - Click the frame from the list
Jump to Current Execution Point
When 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:
- Run > Show Execution Point.
Alt+F10- Click
on the stepping toolbar of the Debug tool window.
Variables
While Elixir allows rebinding variable names, Erlang does not, so when viewed in the Variables pane, variables will have an @VERSION after their name indicating which rebinding of a the variable is. Even if there is no variable reuse, the first variable will still have @1 in its name.
Stepping
| 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. |
Installation
Inside IDE using JetBrains repository
- Preferences
- Plugins
- Browse Repositories
- Select Elixir
- Install plugin
- Apply
- Restart the IDE
Inside IDE using Github releases
In browser
- Go to releases.
- Download the lastest zip.
In IDE
- Preferences
- Plugins
- Install plugin from disk…
- Select the downloaded zip.
- Apply
- Restart the IDE.
Donations
If you would like to make a donation you can use Paypal:
If you’d like to use a different donation mechanism (such as Patreon), please open an issue.
KronicDeth
Version 12.0.0
Historical One-time/Monthly Donations:
| Stat | Amount |
|---|---|
| Minimum | $1.00 |
| Median | $6.25 |
| Mean | $12.52 |
| Maximum | $200.00 |
Changelog
v12.0.0
Breaking Changes
- #2179 - @KronicDeth
- Drop support for Elixir <= 1.6.
Continuing support for Elixir <= 1.6 required special handling of the language level to support differences in precedence and operators. Removing the language level tracking allows dropping theLevelandFilePropertyPusherclasses and all their usages, including in the parser grammar and the specialifVersionexternal rule. It also eliminates the need for tests to setup the SDK since it was only needed to get the appropriate Level. This makes the tests run in 45 seconds instead of 7 minutes.
- Drop support for Elixir <= 1.6.
- #2339 - @KronicDeth
- Remove code dependent on the Erlang plugin
Erlang plugin is not 2021.3 compatible yet
- Remove code dependent on the Erlang plugin
Enhancements
- #2179 - @KronicDeth
- Resolve more calls and bindings in
Ecto.Querycalls-
Resolve bindings in
Ecto.Query.lock/3 -
Resolve bindings in
Ecto.Query.windows/3 -
Walk
preloadbinding and expression -
Resolve in
updatefor Ecto.Query -
Resolve
fragmentinwith_cte -
Resolve
bindingandexprinEcto.Query.dynamic/1-2 -
Resolve
fieldinjoin(..., on: field(..., ...) ...)
Forjoin/5, descend into the options to look foron: valueand then walkvaluethe same as the value tohavingorwherein selects since they’re all boolean conditions. -
Extract
ecto.query.Nested -
Resolve
Ecto.Query.WindowAPIfunctions -
Resolve reference variable src in
join(query, ..., [{src, counter}], ..., ...)
Tuple lists in join have two forms:{^assoc, a}{src, counter}
The pinned association form was already handled because the second element was checked for a declaration, but the first element was not, so
srcin (2) could not be resolved. -
from([..] in ...) -
Treat
or_havingthe same ashaving -
Treat
or_wherethe same aswhere -
Treat
having:the same aswhere:infrom -
Treat
select_mergethe same asselectfor resolvingEcto.Query.API. -
from(..., [elements]) -
Resolve Ecto reference variables in
left in ...
-
ModuleWalkerandNameArityRangeWalker
Reduce code by abstracting common pattern for walking library APIs.ExUnit- Find modules declared in
tests. - Resolve call definitions inside
describeblocks. - Resolve variables in
assert_receiveandassert_received. - Resolve
aliasto modules defined inside the enclosingdescribeblock. - Walk
assertexpression for variable declarations- Check for earlier bindings of variables in right operand of
=inassert.
- Check for earlier bindings of variables in right operand of
- Find modules declared in
- Resolve
requireas:arguments as Aliases - Decompiler
- Erlang
- Decompile private Erlang functions
- Decompile specs from Erlang DbgI
- Decompile function bodies from Erlang DbgI
- Escape “in” when an Erlang Var in type
- Decompile types from Erlang DbgI
Fixes #2017
- Decompile Elixir function bodies using DbgI
- :erlang./(a, b) → a / b
- :erlang.*(a, b) → a * b
- Convert :erlang.==(a,b) to a == b
- Rewrite case to
andwhen there is a badbool error too - Decompile %{struct: name, …} as %name{…}
- Rewrite more :erlang functions to Elixir
- Rewrite case to ||
- Rewrite
case expr1 do pat1 -> true; _ -> false; endtomatch?(pat1, expr1) - Rewrite
if var do false else trueto!var - Rewrite case to or
- Rewrite case to and
- Rewrite :erlang.error(E.exception(M)) to raise E, M
- Rewrite case statements to if
- Rewrite case statements to &&
- Indent all lines of spec macro string in case it is multiple @spec
- Erlang
- Resolve module attributes defined outside the immediate modular lexical scope
-
Resolve module attributes registered in
elixir_module.erlto decompiled source- after_compile
- before_compile
- behaviour
- compile
- derive
- dialyzer
- external_resource
- on_definition
-
Index module attributes
Use the index to resolve module attributes when it can’t be found directly by tree walking.- Defined with
Module.put_attribute/3 - Defined with
Module.register_attribute/3 - Defined in
quoteblocks
- Defined with
-
- Resolve variables to variables in any
quoteblocks
If a variable can’t be resolved in the scope, try resolving it to any variable declared at the top-level of aquoteblock. This helps with certain patterns ofquoteblocks used in Ecto where a variable’s declaration and usage are not in the samequoteblock. - Simplify CallDefinitionClause resolver for modulars
Due to the addition of skipping if the entrance is a child call, the check for only following siblings is no longer needed. Additionally, that check caused a bug because theElixirStabBodyskipped call definitions because they were new scopes. - Resolve functions declared with
Mix.Generator.embed_templateandembed_text.
Also, new system for tracking resolves paths -imports,defdelegate, andusecalls are added to the resolve results after the preferred elements are chosen for source in the same module. This prevents only theimportshowing because the actual declaration is in another module or the SDK. - Record a call as visited in its
treeWalkUpinstead of requiring the caller to do it in the passResolveState- Record
quoteCallas visited inQuoteMacro.treeWalkUp - Record
unquoteCallas visited inUnquote.treeWalkUp - Record
unlessCallas visited inUnless.treeWalkUp - Record
ifCallas visited inIf.treeWalkUp - Record
importCallas visited inImport.treeWalkUp - Record
useCallas visited inUse.treeWalkUp
- Record
- More macro specialized in Structure View
testdescribe
- Resolve
Qualifer.unquote(variable)(...)to any definition with correct arity inQualifier. - Only calculate
element.resolvedFinalArityonce inresolvedQualified - Implementations and Protocols
- Redo icons
- Implementations Go To Protocol line markers
- Go to Super for calls to
defimplfunction/macro
Goes to corresponding name/arity in thedefprotocolthat thedefimplimplements. - Go to implementations line marker from defprotocol def
- Go to implementations line marker from defprotocol
- Go To Implementation from individual functions in defimpl
- Go To Implementation from defimpl Alias
- Resolve protocol function to
defin defprotocol - Resolve defp inside of defimpl
Process declarations inside of implementation the same as modules.
- Stop
prependQualifiersat top of file - Walk the false and true (else) branch of unless in Modules or Quote
- Walk the true and false (else) branch of if in Modules or Quote
- Port
preferredandexpandsystem fromCallablestoModules. - Update CI build dependencies
- Resolve more calls and bindings in
- #2199 - @KronicDeth
- Regression test for #2198.
- #2201 - @KronicDeth
- Use callbacks as completions for calls.
- #2223 - @KronicDeth
- Decompiler
- Don’t require
MacroNameArityforaccept, but useNameArityonly because no decompiler cares about the macro.
- Don’t require
- Tests for Code.Identifier and String.Tokenizer
- Decompiler
- #2226 - @KronicDeth
- Structure View for
EEx.function_from_(file|string) - Variants (completion) for functions declared by special macros.
- Functions defined by
EEx.function_from_(file|string) exception/1andmessage/1defined bydefexception*_text/0and*_template(assigns)functions defined byMix.Generator.embed_textandMix.Generator.embed_template.
- Functions defined by
- Structure View for
- #2334 - @KronicDeth
- Internal Tool for BEAM Bulk Decompilation
Decompile all .beam files in the project, modules and SDKs to check for errors in the generated Elixir code - Decompiler
- Erlang Abst
- Log decompilation errors
- Erlang Abst
- Error Reports
- Include system information in error reports
Instead of just including the plugin version, also include the Application name, edition, and version; and the Operation System name and version as these are common follow-up questions I have. - Remove tab at start of location for title of issues
- Don’t include "java.lang.Throwable: " in title of issues
TheThrowableis necessary to get a stacktrace, but not a real error.
- Include system information in error reports
- Internal Tool for BEAM Bulk Decompilation
- #2339 - @KronicDeth
- Build against
2021.3 runPluginVerifierin GitHub Actions- Update IDEA version range supports and verified
- Fix reported compatibility warnings
- Inline deprecated bundle messages
- Don’t bundle built-in markdown plugin, depend on it instead
- Build against
Bug Fixes
- #2074 - @Thau
- Alternative function clause for
put_eventwithsuite_finished
- Alternative function clause for
- #2179 - @KronicDeth
- StackOverflow fixes
-
getElementDescription(ElixirAtom, ElementDescriptionLocation)
Override getElementDescription for atoms to prevent StackOverflow while looking for a provider. -
Don’t descend into either branch of
iforunlessif entrance in either branch when resolving calls.
If the definition were in one of the branch, it would already have been found on processing previous siblings in theElixirStabBody. -
Treat child of modulars as being at the same level if nested in
iforunless
Preventstestinifinsupervisor_test.exsinectofrom stack overflowing. -
Fix StackOverflow when looking for earlier bindings in parameters.
-
Don’t check following siblings of modulars if entrance is a direct child
Prevent StackOverflow when trying to resolve embed_template when more than one appears in the same module.
In general, if the entrance is a child of modular then it can only be defined by a previous sibling, usually an
importoruse, but if the entrance is descendant of a child, then it child then it may be a call to a function or macro defined in the modular to following siblings of the entrance ancestor child needs to be checked if the entrance is a forward-call to a later declared function or macro. -
Fix StackOverflowError in ifErlangRewriteTo
Don’t rewriter :erlang.* to a different :erlang.*
-
- Adjust nameArityInterval in nameArityInAnyModule
Ensures thatfragment/1..used in aquotecan resolve to one inEcto.Query.API. - Resolve variable that are the only child of
quote
Ecto loves doingquote do: queryor other variable names in the code and tests, so record those as declarations to resolve as invalid results. - Find enclosing macro call when keyword
do:is surrounded by parentheses
Previously, onlyquote do: variablewould work, but nowquote(do: variable)also works to find thequotecall. - Don’t mark fields and keys that are not expected to be resolvable yet as unresolvable in Elixir References inspection.
QualifiedBracketOperationqualifierStructOperationqualifier- Expect qualified
unquoteto only have invalid results. - Don’t mark invalid only results for first chained function call.
Don’t reportunquote(schema).__schema__(:source) - Don’t mark invalid only results for chained function calls.
Don’t reportMix.shell().yes?("Are you sure you want to drop the database for repo #{inspect repo}?") - Don’t mark invalid resolved function of call output
- Don’t mark invalid resolved keys or fields of call output
- Don’t mark invalid resolved function call on keys or fields
- Don’t mark parentheses calls on variables if the call does not resolve.
Can’t find exact valid resolves on variables yet. - Don’t mark keys or fields on the output of a function call.
- Fix some bugs with
Ecto.Querycalls.- Add missing
state.put(Query.Call, call)forjoin/3-4executeOnIn. - Walk the operands of
|>inselectexpressions. - Resolve pinned variables as normal instead of as reference variables for Ecto.Query calls.
- Don’t walk keywords that cannot declare reference variables.
hintslockintersectintersect_allexceptexcept_allunionunion_allprefixpreloadoffsetwindowslimit
- Don’t treat signature for call definition as use of Ecto macro
- Add missing
- Don’t generate references to aliases, functions, or types that don’t have declarations
assoc/2injoin: .. in assoc(_, _)in a no parenthesesfromcallvarin type restrictions
Related to elixir-ecto/ecto#3756BitString
BitStringis recognized indefimpl ..., for: BitStringto define protocol implementations for<<..>>, but theBitStringmodule itself does not exist, so can’t be resolved.
- Error reporting
- Ignore
at com.intellij.openapi.diagnostic.Loggerwhen calculating location for error report titles - Improve error report format sent to GitHub
- Fix the
eventmessagenot being included, which meant that the excerpt wasn’t included, so no reproducibility or element class was available. - Filter stacktrace to stop at last line from the plugin to limit their size and improve chance of URL being short enough for GitHub.
- Don’t include “What I was doing” section unless user actually fills in the additional information in the UI form.
I’m sick of seeing the issue tracker full of “I don’t know what I was doing”, which is the default text when no additional info is given in the UI form. - Set title to the message at start of exception and first
atthat isn’t from theerrorreport.Loggerinstead of[auto-generated]as this is the pattern I follow when renaming manually.
- Ignore
- Handle
alias __MODULE__.{...}in prependQualifier
Fixes #2153 - Log error, but don’t fail with TODO() for unknown strippedQualifier or null qualifier
Fixes #2153 - Go To Declaration for captures
-
Don’t allow name to be acceptable named parent in
&name/arity.
Resolves #488
Allows Go To Declaration onnameand not just on/arity. -
Don’t allow
Mod.nameto be acceptable named parent in&Mod.name/arity.
Resolves #488
Fixes #2101Allows Go To Declaration on
nameand not just on/arity. -
Resolve
&name/arityand&Mod.name/arityusing same code as callables.
Fixes resolving &Mod.name.arity and ensures that special handling for weird definitions for callables also apply to captures.
-
- Resolve
__MODULE__inquotetodefmacro __MODULE__inKernel.SpecialForms - Performance
-
Fix String.Unicode decompiled being PlainText instead of Elixir
String.Unicodewhen decompiled using all information fromDbgIwas 161,171 lines long, which made the JetBrains API treat it as plain text instead of Elixir. Being that long also made it freeze the UI while being decompiled.Now, don’t even attempt to use the
DbgIif the function has more than 10 clauses. -
Don’t decompile private macros and functions if > 500 definitions in one module.
-
If body cannot be decompiled, decompile as one-liner with
...body -
Don’t decompile Abst clause bodies that exceed 1024 bytes.
-
Decompile Erlang one clauses as Elixir one-liners
-
- Fix resolving type specs
-
Find
ancestorTypeSpecfor qualified type used in parentheses in anonymous function type in an alternation@type run :: ((Ecto.Repo.t, changes) -> {:ok | :error, any}) | {module, atom, [any]} -
Resolve type parameters used in inline anonymous function types
-
Resolve callback heads to themselves when they have type restrictions using when
-
Ignore literal parameters
- Decimals
- Aliases
-
Check left operand of
\\for type parameters as they could appear when copying def with defaults.
-
- putInitialVisitedElement in variable.Variants
Fixes #2002 - Walk
defdelegates when walkingimports
Fixes resolvingconfigfromuse Mix.Configas it delegates toConfig - Resolve variables used in
match?guards to pattern declaration
Resolveson_deleteinmatch?(%{on_delete: on_delete} when on_delete != :nothing, reflection) - Implementations and Protocols
- Fix calculating definition for stubs of defimpl with
for:
There was no clause for defimpl being arity 3, which is the case when there is the (1) protocol (2)for:and (3)doblock. Not having a definition meant that thedefimpl protocol, for: struct dowould be in AllName index, but not ModularName. - Get name of enclosing modular for defimpl without
for:
- Fix calculating definition for stubs of defimpl with
- Store if stubs are guards
- Decompiler
- Surround
casestatements with parentheses when used incondclause conditions - Convert OtpErlangString to OtpErlangList for tuple and call argument lists
- Escape ESC character as \e
- Handle Clause arguments being OtpErlangString
- Handle tuple elements being an OtpErlangString
- Add missing
.after callee when it is a module or fn - Protect from
Macro.toString(macro)StackOverflowError when decompiling body of function clauses - Don’t print function names as atoms in captures
- Escape
\xto\\xin OtpErlangStr - Fix rewrite of :erlang calls
- Surround type unions with parentheses
Prevents parsing problems with unions in guard (when) clauses - Don’t use
prependIndentbecause it indents blank lines too.
This doesn’t matchmix formator the IntelliJ Elixir formatting. - Erlang
- Escape fn Erlang variable
- Escape Erlang char \ as \
- Don’t append lines for clauses or
afterin Erlangreceivewhen decompiling if empty. - Use
Infix,Prefix, andUnquotedecompolers for Erlang Abst chunk in addition to DbgI chunk- functions
- typesepcs
- Use
function.macroNameArityMacro.macrowhen decompiling Erlang Abst clauses.
Don’t usedefanymore when unexported and therefore private; usedefpinstead. - Remove space after
...in decompiled private types.
- Surround
- Process imports for calls
Imports were previously only processed inside of Modules and not in general, which means that imports in the file were not processed, which is needed forassociation.exin Ecto. - Classify ..// as OTHER instead of NOT_CALLABLE, so that it is escaped as a key.
- Fix Macro.ifCaptureModuleNameArity
- Resolve variable to parameter in
%parameter{}patterns for struct names Unquote.treeWalkUpUnquotedthrough tuplesQuote.treeWalkUpthroughcase- Stop searching on numerical index in binding
- Stop searching if atom in wrong place in binding
Stops invalid binding test from erroring when resolving it. - Turn off
tailrecbecause it doesn’t work correctly forElixirAccessExpression - Stop searching for qualifier when
ElixirUnqualifiedNoParenthesesManyArgumentsCall.
- StackOverflow fixes
- #2199 - @KronicDeth
- Stop highlighting types when
unquote_splicing/1is reached.
unquote_splicingis being used to splat arguments or fields of a struct into the type. The arguments tounquote_splicingare normal calls or variables, not types.
- Stop highlighting types when
- #2201 - @KronicDeth
- Implement
call_definition_clause.Variants#executeOnCallback
- Implement
- #2204 - @KronicDeth
CallDefinitionClause.time/1- Mark guards as runtime.
- Mark anything unknown as runtime too.
- Log unknown calls.
- #2207 - @KronicDeth
- Check if
CallisValidbefore usingcontainingFileforlocationString.
- Check if
- #2208 - @KronicDeth
- Check if
projectis not dumb innameArityInAnyModule.
- Check if
- #2209 - @KronicDeth
- Take
resolveInScopeonly if at least one valid
Checking only for an empty collection allowed any prefixes in the scope to override exact matches in anywhere indexed, which meant thatEctoindefmodule Ecto.Adapter doresolved to itself instead of the exactdefmodule Ecto do.
- Take
- #2214 - @KronicDeth
- When regenerating the parser,
ElixirVisitoris also regenerated. When it was regenerated it lost the bug fix for#visitLiteralSigileLinecalling itself. Added a regression test, so that this can’t happen again.
- When regenerating the parser,
- #2223 - @KronicDeth
- Ecto
- Walk keyword keys as right operand of
ininfrom
- Walk keyword keys as right operand of
- Resolving type references
- Walk struct operations for type parameters
- Check keyword values for type parameters
- Check operands of two operations for type parameters
- Stop looking for type parameters on qualified or unqualified alias
- Decompiler
- Only unquote
inwhen an Erlang function, otherwise, use operators the same as Elixir for defs and calls. - Fix apply Erlang arguments, so that they are inside
[]. - Quote keyword keys containing
-
Fixes decompiling ofElixir.Phoenix.HTML.Tag.beam - Use apply with escaped atom when Erlang function call is an Elixir operator
- Only unquote
- Port String.Tokenizer.tokenize for use in Identifier.inspectAsKey
I was putting off porting all ofIdentifer.inspectAsKeyby adding special cases as needed, but the decompiler kept having bugs, so port all of it includingString.Tokenizer.tokenize. It will also work for unicode characters now too. - Resolve calls that are unquoted values to search for quote blocks in those functions.
- Stop looking for qualifiers to prepend when reaching
=> - The parent argument to AccumulatorContinue.childExpressionsFoldWhile should be this and not parent
When converting to an extension function I leftparentin place because the argument is calledparent, but since it is an extension function that value becausethis.parentwhen it really should have beenthis. Usingthis.parentmeant it would ask for the parent’s children and keep looping back tothis. - Don’t use
tailrecin function with any body-recursion.
It causes issues withElixirAccessExpressionrecursion sometimes.
- Ecto
- #2226 - @KronicDeth
- Implement completion for functions declared with
defdelegate. - Fix
LookupElementPresentation.putItemPresentationaddTailText.
Only append suffix ofpresentableTextif it is prefixed byitemText.
- Implement completion for functions declared with
- #2334 - @KronicDeth
- Decompiler
- Elixir
- Decompile local function calls in Elixir DbgI using inspectAsFunction
While remote calls usedinspectAsFunction, local calls just used theatomValue, which meant names that needed to be unquoted weren’t and caused parsing errors.
- Decompile local function calls in Elixir DbgI using inspectAsFunction
- Erlang Abst
- Decompile Erlang Abst string with OtpErlangList as strings with non-ASCII codepoints
Fixes unknown string format inidna.beam - Always group for comprehensions in sequence even if there is only 1 element
Some forms offorcomprehensions cannot be used as the sole argument of a call unless surrounded by parentheses, so always add those parentheses. - Decompile Erlang Abst record empty record fields as
[]for updates - Decompile Erlang Abst
left xor rightas:erlang.xor(left, right)
Elixir does not have a logical xor infix operator, so have to decompile as normal function call - Decompile Erlang Abst named anonymous function as a macro
Named anonymous functions are support in Erlang, but not Elixir, so fake it as a macro when decompiling. - Add builtin-types for Erlang Abst
bitstringfloatnonempty_improper_listnonempty_maybe_improper_list
- Decompile tagged atoms and other complex expression as function name in Abst capture
- Decompile Erlang Abst
float - Decompile Erlang Abst
beginblocks as parenthesized groups separated by ; - Decompile empty OtpErlangList as “” in Erlang Abst
string - Track if decompiled Erlang Abst contains do blocks so that they can be surrounded by parentheses when necessary
- Fix decompiling Erlang Abst
record_indexwhen record name needs to be unquoted - Decompile
mapupdates in Erlang Abst - Erlang Abst Function capture names are OtpErlangAtom and not tagged Atoms
- Inspect local function names as atoms instead of as functions when apply/3 is used for operations and unquoted in Erlang Abst
Stops:unquote(:"NAME")from happening - Surround anonymous function definitions that are called immediately with parentheses and call arguments with .( in Erlang Abst
- Decompile
field_typein Erlang Abst
Fixes decompilinghipe_icode_call_elim.beam - Inspect type name usages as local functions to ensure invalid names are unquoted
- Inspect type names as local functions to ensure invalid names are unquoted
- Decompile Erlang Abst string with OtpErlangList as strings with non-ASCII codepoints
- Elixir
- References
- Stop looking for qualifiers to prepend when exiting interpolation
- Don’t safeMultiResolve null call.reference in resolvesToModularName
- Types
- Fix highlighting types declared with unquote and no secondary parentheses
- Performance
- Don’t error if a private function mirror cannot be found
Private functions are not decompiled if there are too many public functions. - Fix CallDefinitionImpl.isExported
Used to be hard-coded to returntrue, but this pre-dated decompiling private functions. Now with decompiling private functions, isExported needs to defer to theDefinitionand count as unexported if a private function, macro, or guard.
- Don’t error if a private function mirror cannot be found
- Decompiler
- #2337 - @KronicDeth
- Walk map constructin arguments, associatons, and variables when resolving type parameters.
- #2339 - @KronicDeth
- Don’t use
PluginId.findIdthat doesn’t exist in 2021.1.X
- Don’t use













