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.

17 Likes

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 :stuck_out_tongue_closed_eyes:
  • 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 a NullPointerException
  • 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 CommonProgramParametersPanel to 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 the CommonProgramParametersPanel. CommonProgramParametersPanel expects 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
  • #531 - @KronicDeth
    • enclosingMacroCall returns enclosing macro call when parent is ElixirDoBlock, so that end element goes to the macro call.
    • Navigate > Test will go to the Module that has the same canonical name as the current defimpl, defmodule, defprotocol , or quote with a Test suffix added
    • Navigate > Test Subject will go to the defimpl, defmodule, defprotocol, or quote that has the same canonical name as the current Module with the Test suffix 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 ElixirMultipleAliases for isVariable because ElixirMultipleAliases can be hit in isVariable when MyAlias. 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 VirtualFile is not null before creating attachment because PsiFile can lack a VirtualFile if the PsiFile only exists in memory. - @KronicDeth
  • #537 - Convert CallDefinitionClause(Call) to CallDefinitionClause.fromCall(Call), so that null can be returned when CallDefinitionClause.enclosingModular(Call) returns null. - @KronicDeth
  • #539 - @KronicDeth
    • Use functionName instead of getName when multiresolving unqualified functions because getName will return the Alias when called on defmodule.
    • maybeQualifiedCallToModular returned null BOTH (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, like File.read! returned null because File could not be resolved to a modular. Remove maybeqQualifiedToModular and call qualifiedToModular when myElement is qualified. If the modular is null, then return an empty ResolveResult[] instead of looking for unqualified matches.
    • Pass maxScope to Module reference. maxScope is generally the containing file for the element, but when using Module to resolve imports, it is the import call’s parent element, so that the resolve doesn’t ricochet between the defmodule and its child, the import call until StackOverflowError.
  • #545 - A variable cannot be declared in update arguments, so return LocalSearchScope.EMPTY, the same as interpolation. - @KronicDeth
  • #548 - ElixirSystemUtil.getProcessOutput already allowed for an empty, invalid ProcessOutput when the workDir wasn’t a directory, so allow it to also be null and return the empty ProcessOutput. - @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.

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
  1. Run > Edit Configurations…
  2. Click +
  3. Select “Elixir Mix ExUnit”
  4. Fill in the “Program arguments” with the argument(s) to pass to mix test. Normally, this will be a directory like test, relative to the “Working directory”
  5. Fill in the “Working directory”
  • Type the absolute path to the directory.
  • Select the path using directory picker by clicking the ... button
  1. (Optionally) click the ... button on the “Environment variables” line to add environment variables.
  2. Click “OK” to save the Run Configuration and close the dialog
  3. Click the RUn arrow in the Toolbar to run the mix test task
  4. 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.

  1. In the Project pane, ensure your OTP application’s test directory is marked as a Test Sources Root
  2. Check if the test directory is green. If it is, it is likely a Test Sources Root. This color may differ in different themes, so to be sure you can check the context menu
  3. Right-click the test directory.
  4. 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
  1. Right-click the directory in the Project pane
  2. 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

  1. Select the directory in the Project pane.
  2. Ctrl+Shift+R will create the Run Configuration and Run it.
Creating/Running mix test Run Configurations from file
  1. Right-click the file in the Project pane
  2. 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

  1. Select the directory in the Project pane.
  2. Ctrl+Shift+R will create the Run Configuration and Run it.

Finally, you can use the editor tabs

  1. Right-click the editor tab for the test file you want to run
  2. 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

  1. Right-click a line in the test file
  2. 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

  1. Place the cursor on the line you want to test
  2. Ctrl+Shift+R will create the Run Configuration and Run it.

Installation

Inside IDE using JetBrains repository

  1. Preferences
  2. Plugins
  3. Browse Repositories
  4. Select Elixir
  5. Install plugin
  6. Apply
  7. Restart the IDE

Inside IDE using Github releases

In browser

  1. Go to releases.
  2. Download the lastest zip.

In IDE

  1. Preferences
  2. Plugins
  3. Install plugin from disk…
  4. Select the downloaded zip.
  5. Apply
  6. Restart the IDE.
12 Likes

Thanks

  • Thanks to Jimmi Fragkos (@FraDim) and Mariusz Nosiński (@marioosh) for voting for Code Folding
  • Thanks to Andrei Dziahel (@develop7), Gerard de Brieder (@smeevil), and César Izurieta (@cesarizu) for voting for Multiple Alias (A.{B, C}) support. Special thanks to Alexander Merkulov (@merqlove) for guilting me to support this Elixir 1.2 (!) feature that I knew about since September 2015 after he let me know not having it broke parsing on over half his code and for supplying test repositories.
  • Thanks to Filip Haglund (@drathier) for reporting a second reproduction of the stack trace linking bug
  • Thanks to @swieckib and @fcruxen for reporting the Variable scope processor wasn’t checking if the PsiElements were null, as can happen with error handling recovery.
  • Thanks to @swieckib and @zan-kusterle for reporting where isParameter failed
  • Thanks to Nicholas Schell (@Nicholi), Calinoiu Alexandru Nicolae (@alexandru-calinoiu), and Christian Meter (@n2o) for reporting that the mix --version output changed AGAIN for Elixir 1.3. Thanks to Christian Meter (@n2o), Gonzalo Gabriel Jiménez Fuentes (@mendrugory), Roeland (@r03), Serhij Korochanskyj (@webzepter) for confirming the fixed worked in the Canary build.
  • Thanks to Bulat Gaifullin (@bgaifullin) for reporting errors with Match#processDeclarations.

Changelog

v4.1.0

Enhancements

  • #331 - @KronicDeth
    • Allow do end blocks to fold to do: ...
    • Allow -> operator and the right operand to fold to -> ...
    • Allow @doc, @moduledoc and @typedoc value to fold to "...".
    • Fold runs of adjacent alias, import, require, or use to be followed to a single alias, import, require, or use followed by ....
  • #334 - Function separators - @KronicDeth
    • Show a function separator (Preferences > Editor > General > Appearance > Show method separators) above the group of @doc, @spec and def, defp, defmacro, and defmacrop (call definition clauses) of the same name and arity range. Arity range will be used if one of the call definition clauses uses default arguments.
  • #337 - @KronicDeth
    • @for folds to the resolved module name in defimpl
    • @protocol folds to the protocol name in defimpl
  • #343 - Share code between mix and elixir version parsing. - @KronicDeth
  • #344 - @KronicDeth
    • Allow Unknown modulars in the Structure pane, in addition to Go To Symbol. Their icon is a big ? to indicate their correct usage is unknown.
  • #348 - @KronicDeth
  • #349 - Have both QualifiedBracketOperation and UnqualifiedBracketOperation extend BracketOperation, so that BracketOperation can be used to match both when the qualification does not matter. - @KronicDeth
  • #364 - @KronicDeth
    • Regenerate parser with GrammarKit 1.4.2

    • ElixirSdkRelease is now Comparable, so version checks can be done for tests to restrict them to Elixir 1.2+ for multiple alias support.

    • Resolve Multiple Aliases with unqualified Alias in tuples.

    • canonicalName borrows from the idea of PsiReference#canonicalText: an element can have both a Name (from getName), which is the literal name in the code, which can be renamed, and a Canonical Name, which is the name to refer to the element without need for imports or aliases. For this change, defimpl, defmodule, and defprotocol will show their full module Alias for their Canonical Name.

      This change addresses the use case of Go To Declaration that should resolved to a nested defmodule.

Bug Fixes

  • #330 - Check if parameter is null before Variable#execute call in Variable#execute(PsiElement[], ResolveState). - @KronicDeth
  • #336 - Fix isVariable and variableUseScope for var!(name)[...] - @KronicDeth
  • #337 - @KronicDeth
    • @for is no longer marked as unresolved in defimpl and instead resolve to the either the <name> in for: <name> or the module name for the enclosing module when for: is not given.
    • @protocol is no longer marked as unresolved in defimpl and instead resolve to the <name> in defimpl <name>.
  • #342 - @KronicDeth
    • Instead of assert checkRight || checkLeft in Match#processDeclaraions, do the normal code if checkRight || checkLeft and log an error report otherwise, so that the exact code that trigger this error can be reported and the method fixed to handle that form of Match later.
  • #343 - Be able to parse mix version from 1.3.0+ - @KronicDeth
    • Check all lines of output for mix version as Elixir 1.3.0 changed the format of mix --version, so that it includes the Erlang header (Erlang/OTP ... [erts-...] [source] [64-bit] [smp:..:..] [async-threads:..] [hipe] [kernel-poll:false] [dtrace]) on the first line and Mix <version> on the 3rd line. Previously the parsing expected Mix <version> to be the first line.
  • #344 - @KronicDeth
    • If no known modular (Module, Implementation, Protocol, Quote, or Use) matches the call, then use Unknown, which accepts any macro with a do block or keyword. This allows Go To Symbol to no error in projects using Dogma as defrule is now treated as Unknown instead of causing an error that the enclosing modular could not be found.
  • #349 - BracketOperations are neither parameters nor variables. - @KronicDeth
  • #353 - Fix stacktrace linking picking wrong file with same basename - @KronicDeth
    • Strip spaces from front of file path in mix output, which allows file looks to work correctly.
    • Ensure file reference highlight doesn’t include the leading and trailing characters by fix off-by-one errors.
  • #358 - Determine whether to check left, right, or both by doing isAncestor checks for all operands, not just the normalized operand. The normalized operand is still used for PsiScopeProcessor#execute since #execute is not expected to handle error elements. - @KronicDeth
  • #364 - @KronicDeth
    • Add A.{B, C} to grammar with quoting to check consistence with Elixir 1.2. Ports elixir-lang/elixir#3666.
    • Use fullyQualifiedName instead of getName for resolvableName because fullyQualifiedName is needed so that qualified aliases inside of the { } of a multiple alias will not have a name as getName is null for those qualified aliases because the name from getName has to be a literal name that can be renamed and qualified names can’t be renamed.
  • #365 - The Module icon got the same icon as Unknown when creating Unknown somehow, I assume due to find-replace. - @KronicDeth

README Updates

Code Folding

You can collaspe (fold) pre-defined regions of your Elixir code to make it easier to quickly scroll through files or hide details you don’t care about right now.

Controls

Collapsing
  1. Position cursor between lines with with downward facing - arrow and upward facing - arrow.
  2. Cmd±
Expanding
  1. Position cursor on the collapsed line with the square +
  2. Cmd++

Regions

Expanded Collapsed Folded By Default?
do end do: ... No
-> and right operand -> ... No
@doc VALUE @doc "..." No
@moduledoc VALUE @moduledoc "..." No
@typedoc VALUE @typedoc "..." No
alias ALIAS1
alias ALIAS1
alias ... Yes
import ALIAS1
import ALIAS2
import ... Yes
require ALIAS1
require ALIAS2
require ... Yes
use ALIAS1
use ALIAS2
use ALIAS1 Yes
@for FOR in defimpl PROTOCOL, for: FOR Yes
@protocol PROTOCOL in defimpl PROTOCOL, for: FOR Yes
@MODULE_ATTRIBUTE VALUE in @MODULE_ATTRIBUTE VALUE Yes

Installation

Inside IDE using JetBrains repository

  1. Preferences
  2. Plugins
  3. Browse Repositories
  4. Select Elixir
  5. Install plugin
  6. Apply
  7. Restart the IDE

Inside IDE using Github releases

In browser

  1. Go to releases.
  2. Download the lastest zip.

In IDE

  1. Preferences
  2. Plugins
  3. Install plugin from disk…
  4. Select the downloaded zip.
  5. Apply
  6. Restart the IDE.
6 Likes

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 NullPointerException from org.elixir_lang.psi.scope.call_definition_clause.Variants.getLookupElementCollection.
  • Thanks to Sergey Zubtsovskiy (@szubtsovskiy) for reporting StackOverflowErrors from certain forms of with and 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 do for do blocks.
  • Thanks to Scott Bennett (sbennett33) for finding a case that triggered the ElixirNoParenthesesStrict error handling, which revealed that isVariable didn’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.primaryArguments wasn’t using Normalized and still was using basic asserts.
  • Thanks to Aaron Foster (AnimalRepellentGranules) for reporting that the do end matching was too aggressive and incorrectly matched on one liners starting at the do:.

Changelog

v4.6.0

Enhancements

Bug Fixes

  • #454 - Return emptySet when lookupElementByPsiElement is null. - @KronicDeth
  • #455 - @KronicDeth
    • Don’t do a naked assert that there are 2 children because this can fail during error recovery on the operand, instead use the prefix.Normalized.operand() through prefix.operand().

      WARNING: This changes the @NotNull array so that its sole element changes from @NotNull to @Nullable. It may trigger new bugs.

  • #461 - Use shipped GeneratedParserUtilBase.DUMMY_BLOCK because the DUMMY_BLOCK MUST match the GeneratedParserUtilBase to detect dummy blocks inserted for error handling. - @KronicDeth
  • #465 - Skip over ElixirNoParenthesesStrict for isVariable - @KronicDeth
  • #466 - Allow newlines before do in doBlock - @KronicDeth
  • #467 - Don’t match do or fn to end when used as a keyword key. - @KronicDeth
  • #474 - @KronicDeth
    • Check if iterator.atEnd() before calling iterator.getTokenType() to avoid IndexOutOfBounds exception.
    • Don’t add current call definition clause being written to completion
  • #476 - When#leftOperand will return null (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 normalized null, but use left, non-error element if it is unique. - @KronicDeth
  • #477 - Highlight types in QualifiedNoParenthesesCall - @KronicDeth
  • #478 - Still not obvious why name for a CallDefinitionClause lookup renderer can be longer than presentableText, so still log an error, but with Logger.error, so we get name, presentableText, and the original element. - @KronicDeth
  • #479 - @KronicDeth
    • Skip Arguments elements in previousParentExpresion to eliminate an unnecessary level of processing declarations since calls will enter their arguments.
    • Only put new ENTRANCE in ResolveState in variable.MultiResolve.resolveResultList, so that caller can override the default value.
    • Set ENTRANCE to matchAncestor instead of previous expression to eliminate the looping that occurred when a variable was unbound (or a function) because the check for with (and for) was expecting the ENTRANCE to be the previous child expression instead of the with clause as a whole (or the Arguments element as had been the case before 6fcc19b).
  • #483 - @KronicDeth
    • Resolves functions qualified by Aliases that are either direct Module references or one-step aliases.
    • Remove Call#resolvedFunctionName because import can’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.each the same as Enum.map around def - @KronicDeth
  • #486 - Increase resolvedFinalArity by 1 for piping. - @KronicDeth
  • #498 - @KronicDeth
    • Go To Declaration resolves through import
      • for import MyModule
        • the import statement
        • the call definition clause in the imported Module.
      • for import MyModule, only: [name: arity]
        • the import statement
        • the call definition clause in the imported Module.
      • for import MyModule, except: [name: arity] if reference is not name/arity.
        • the import statement
        • the call definition clause in the imported Module.

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 .beam file, 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 .beam file, 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.

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
  1. Place the cursor over name of the function or macro call.
  2. Activate the Go to Declaration action with one of the following:
    1. Cmd+B
    2. Select Navigate > Declaration from the menu.
    3. Cmd+Click
  3. A Go To Declaration lookup menu will appear, allowing you to jump to either the import that imported the function or macro or jumping directly to the function or macro definition clause. Select which declaration you want.
    1. Use arrow keys to select and hit Enter
    2. Click
Local Functions or Macros
  1. Place the cursor over name of the function or macro call.
  2. Activate the Go to Declaration action with one of the following:
    1. Cmd+B
    2. Select Navigate > Declaration from the menu.
    3. Cmd+Click
  3. Whether a lookup a Go To Declaration lookup menu appears depends on the number of clauses in the function or macro definition:
    1. If there is only one clause in the function or macro definition, you’ll jump immediately to that clause
    2. 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 import that imported the function or macro or jumping directly to the function or macro definition clause. Select which declaration you want.
      1. Use arrow keys to select and hit Enter
      2. Click
Remote Functions or Macros
  1. Place the cursor over name of the function or macro call that is qualified by an Alias.
  2. Activate the Go to Declaration action with one of the following:
    1. Cmd+B
    2. Select Navigate > Declaration from the menu.
    3. Cmd+Click
    1. If there is only one clause in the function or macro definition, you’ll jump immediately to that clause
    2. 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 import that imported the function or macro or jumping directly to the function or macro definition clause. Select which declaration you want.
      1. Use arrow keys to select and hit Enter
      2. Click

Installation

Inside IDE using JetBrains repository

  1. Preferences
  2. Plugins
  3. Browse Repositories
  4. Select Elixir
  5. Install plugin
  6. Apply
  7. Restart the IDE

Inside IDE using Github releases

In browser

  1. Go to releases.
  2. Download the lastest zip.

In IDE

  1. Preferences
  2. Plugins
  3. Install plugin from disk…
  4. Select the downloaded zip.
  5. Apply
  6. Restart the IDE.
14 Likes

@KronicDeth Hi i can’t seem to find how to autocomplete builtin modules and functions from them, like Enum, List etc. How can I turn it on?

From the changelog:

only those defined in parseable-Elixir source. Erlang functions and Elixir function only in compiled .beam file, such as the standard library will not complete

So, you aren’t missing anything. The features don’t work on the standard modules because they leverage the .ex/.exs parser currently.

Is there anyway to set a code style specific for Elixir code files?

When I installed this and went to Settings -> Code style it does not show Elixir in the list of languages. Elixir is the only language that tries to force me in the 2 space indention that I dislike, but I really don’t feel like fighting things like doctests :-/.

That could be changed though, you can pull call information out of the module_info callback. The atom elixir plugins use that to provide autocomplete (so, consequently, autocomplete does not work until at least one compile is done). A mixing of the two styles would be great for both completeness and up-to-date’ness. ^.^

I’m aware how Alchemist and atom-elixir does completion (with a fork of alchemist-server). It’s just a matter of time and I find using the OpenAPI tools better because the IDE knows how to cache the index and can jump to specific clauses and around errors while alchemist-server (correct me if I’m mistaken) needs at least the file to be error free because it’s using Elixir runtime to reflect on the .beam.

Now that the parseable-code Go To Declaration is complete I’m left with the .beams, but I don’t necessarily have to solve that with alchemist-server. Other solutions:

  1. Use JInterface to talk directly instead of using text-based protocol used by alchemist-server (so a fork and maybe a PR to alchemist-server)
  2. Use Java to read the .beams directly
  3. Decompile .beam (the way IntelliJ does with Java .class) with the option to download source for stdlib.

I like (2) because it means the IDE doesn’t have to run an external process, which requires getting its environment correct too, but it would require more tightly understanding the internal .beam format. (3) isn’t a complete solution because only reflection will get a complete listing when a lot of macro indirection is involved or in the bootstrapped parts of the standard-lib, but it would be cool to have the .beam dissassembler built-in the way it in for Java in IntelliJ.

The Elixir plugin does not support Code Style or Formatting yet. There’s are open issue for Code Style (https://github.com/KronicDeth/intellij-elixir/issues/99) and Formatter (https://github.com/KronicDeth/intellij-elixir/issues/98).

You really should try to get used to 2 space indent in Elixir. It’s the idiomatic indentation for Elixir and part of the community style guide (https://github.com/levionessa/elixir_style_guide#source-code-layout).

Erlangs JInterface is pretty awesome, low level but awesome. :slight_smile:

Each version of erlang can change that, so be careful. :slight_smile:

You probably don’t need to read the entire .beam - it should be enough to read Atom chunk (with atom table) and the ExtT chunk with table that lists all exports (public functions in Erlang speak) in a module. Both seem relatively simple. http://beam-wisdoms.clau.se/en/latest/indepth-beam-file.html is a great reference.

1 Like

Sorry I must have written it ambigously. When writing elixir code I 100% do write it using 2 space indent in VS Code, even though I think it’s bad form that this is enforced by language mechanics (personal opinions and all that).

However, since I use Intellij for non-Elixir languages I do not want to set global settings for elixir specific styling and thus would rather be able to tell Intellij to only use 2 space indent for elixir code files only (just like I can do if I wanted to for rust or go).

I don’t think there is anything that forces any indentation on you. If I’m not mistaken you can get away with out any indentation. But since it’s community style guide, almost everyone use this two space indentation. And I’m yet to see any sane reason against two space indentation. But again, no one is forcing you to use it, and no one will condemn you for using any indentation you want. It’s no one’s business how you indent code… well until it is :wink:

Wow! Thanks a lot! :smiley:

I have valid reasons for not liking 2 space indentation but this isn’t the thread for it. There are several language/ecosystem issues which prevent doing 4 space indentation (such as doctests). There is definitely more ramifications for not following the community style guidelines in Elixir than there is if I wwere doing C# or Rust.

My main point was that I could not find an easy way to say that only for elixir code files do 2 space indentation, so I can do 4 space indentation for all my other non-elixir code files (especially since different language communities have different indentation style preferences) and thus I can’t use the intellij plugin for Elixir until I find a good way to make the indentation (at a minimum) for Elixir projects only.

I use 2-spaces in elixir, but almost everywhere else I am a fan of tabs for indentation (of which I usually have set to 2-space width anyway), such as for example:

int function(int with,
             int a,
             int lot,
             int of,
             int parameters) {
	return with + a + lot + of + parameters;
}

Where · is a space and is a tab (also wow those characters break discourse’s formatting o.O).:

int function(int with,
·············int a,
·············int lot,
·············int of,
·············int parameters) {
→return with + a + lot + of + parameters;
}

So spaces are used for alignment (sometimes after tabs if necessary), and tabs are used for indentation.

This method allows alignment to work properly and indention width to be whatever the viewer wants it to be.

Sadly I’m in a minority though. :icon_sad:

I just want to clarify one thing. Doctests in Elixir will run regardless of of indentation level, as long as the test does not mix tabs and spaces. But you can mix tabs and spaces (and indentation) between the actual test in doctest and rest of the doctest. Elixir does not care. The two space limitation for creating nice readable documentation is something completely different because it’s Markdown limitation.

But I understand that not being able to customize Elixir plugin is troublesome.

The plugin currently doesn’t do any automatic code formatter and no real code generation except for the file templates, so I’m confused why you’d need to set the indentation level. I have Java project (the plugin itself) that uses the Java default of 4 spaces and Elixir projects where I do 2 spaces and there are no issues.

Where are you seeing it insert 4 spaces currently or warn that you’re using 2 spaces instead of 4?? Screenshots would be helpful.

Nevermind, I see now.

Intellij’s default settings have been fine for me and I never delved deep into the the settings since I use Visual Studio in my day job and Intellij only for hobby non-C# projects. So when I went into Settings -> Code style and didn’t see Elixir I had no idea how to proceed from there, but I missed the “other languages” option at the bottom of the language list, and I now see the project profile part of the settings.

So I’m good now, sorry!